Create a .zip folder and add files to it in C++ MFC - c++

I am working on an C++ MFC project and one step is to create a large zip file with many other files inside it which may have different paths
As of right now, I have a vector that correctly stores all the file paths as CStrings to each file I will need to collect and add to the zip file
The way I am thinking to tackle this is by creating an empty zip file in a specified folder such as: C:\foo1\foo2\foo3 . The zip file would be titled foo4.zip, so its path would be C:\foo1\foo2\foo3\foo4
Then I would add each file one at a time by iterating through the vector containing the file paths with a for loop
Is it possible to create an empty zip file and add files to it ? If so, how ? Or should I create a regular folder, add files to it, then zip the folder after storing all files ? Or maybe I could create a zip file while supplying it the vector or file paths ?
Which is the better option in which situation and how do I accomplish this in C++ MFC ?
I have done some research on the ZipFile Class in Microsoft's Online Documentation but from my understanding, this Class expects all the files to be zipped to be in the same folder, but in my situation, these files are scattered across many different folders.

I use the ZipArchive library which is freely available here:
https://www.artpol-software.com/
It has excellent help documentation (via download) and you can create zips, add files to zips etc and has full progress monitoring support.
Used it for several years. Later on I will update my answer with sample code.
Single File
try
{
rZipArchive.AddNewFile(strFile, CZipCompressor::levelDefault, false);
}
catch (CZipException* e_)
{
const gsl::not_null<CZipException*> e{ e_ };
e->ReportError();
AfxMessageBox(e->GetErrorDescription(), MB_OK | MB_ICONERROR);
e->Delete();
}
Multiple Files
rZipArchive.AddNewFiles(strFullPath, strFileSpec, false, CZipCompressor::levelDefault, false);
Creating the Zip
Admittedly I have not included the progress callback class as it is fairly easy to write and would be bespoke to your application.
But this code gives you a flavour of what you can do:
try
{
// Create archive and set basic details
zipArchive.Open(strBackupPath, CZipArchive::zipCreate);
zipArchive.SetGlobalComment(_T("xxx"));
zipArchive.SetRootPath(theApp.GetWorkingPath());
/* ================================================ */
CMultiProgressCallback callback;
callback.SetProgressCtrl(&m_ProgressBackupRestore);
callback.SetProgressTextWnd(GetDlgItem(IDC_STATIC_PROGRESS_FILENAME));
callback.SetProgressPercentTextWnd(&m_lblProgressBackupRestore);
zipArchive.SetCallback(&callback,
CZipActionCallback::cbMultiAdd | CZipActionCallback::cbCalculateForMulti);
zipArchive.AddNewFile(strRegPath, CZipCompressor::levelDefault, false);
/* ================================================ */
// Build group filter
ZipArchiveLib::CGroupFileFilter groupFilter;
// Files to include
groupFilter.Add(std::make_unique<ZipArchiveLib::CNameFileFilter>(_T("*.*")).release());
// Files to exclude
groupFilter.Add(std::make_unique<ZipArchiveLib::CNameFileFilter>(_T("exp*.htm"), true).release()); // Preview in Browser
groupFilter.Add(std::make_unique<ZipArchiveLib::CNameFileFilter>(_T("mwe*.xml"), true).release()); // Meeting Workbook Editor
groupFilter.Add(std::make_unique<ZipArchiveLib::CNameFileFilter>(_T("mwp*.xml"), true).release()); // Meeting Workbook Print
groupFilter.Add(std::make_unique<ZipArchiveLib::CNameFileFilter>(_T("sre*.xsl"), true).release()); // Sound Rota Editor
groupFilter.Add(std::make_unique<ZipArchiveLib::CNameFileFilter>(_T("srt*.xsl"), true).release()); // Sound Rota Temp
groupFilter.Add(std::make_unique<ZipArchiveLib::CNameFileFilter>(_T("gcal_*.tmp"), true).release()); // Google Calendar Temp
groupFilter.Add(std::make_unique<ZipArchiveLib::CNameFileFilter>(_T("*.bin3"), true).release()); // Outlook OAuth Cache
zipArchive.AddNewFiles(theApp.GetWorkingPath(), groupFilter, false, CZipCompressor::levelDefault, false);
/* ================================================ */
/* ================================================ */
// AJT v20.1.7 — Backup mode
if (eBackupWhat == COtherSettingsAutomaticBackupPage::EnumBackupWhat::Complete)
{
// Reset
groupFilter.Clear();
// Files to include
groupFilter.Add(std::make_unique<ZipArchiveLib::CNameFileFilter>(_T("*.*")).release());
// Files to exclude
groupFilter.Add(std::make_unique<ZipArchiveLib::CNameFileFilter>(_T("exp*.htm"), true).release()); // Preview in Browser
groupFilter.Add(std::make_unique<ZipArchiveLib::CNameFileFilter>(_T("mwa*.xml"), true).release()); // Meeting Workbook Assignment slips
zipArchive.AddNewFiles(theApp.GetWorkingPath() + _T("AssignmentSlips"),
groupFilter, // Files to include
true, // Recursive
CZipCompressor::levelDefault, // Compression level
false); // Skip initial path
}
/* ================================================ */
auto zipCloseResult = zipArchive.Close();
}
catch (CZipException* e_)
{
const gsl::not_null<CZipException*> e{ e_ };
zipArchive.Close(CZipArchive::afAfterException);
e->ReportError();
bExported = false;
e->Delete();
}
catch (CException *e_)
{
const gsl::not_null<CException*> e{ e_ };
zipArchive.Close(CZipArchive::afAfterException);
// Problem - tell user
e->ReportError();
bExported = false;
e->Delete();
}
if (bExported)
AfxMessageBox(IDS_STR_SETTINGS_SAVED, MB_OK | MB_ICONINFORMATION);
}
The ZipArchive library does have a commercial version where you pay for a license but in my case I did not need to purchase one. This is what it says:
The open source version of the ZipArchive Library is licensed under GNU GPL.
When you download the library you need to build the right version to create your required LIB or DLL for use with your application. Several Visual Studio platform files are provided. Just pick the one you need.

Related

Embedding localized satellite dll into exe application

I have a C++ CLR/CLI project, I wonder how to embed a localized satellite dll into my exe application, I found similar solutions but it's for C# projects which is pretty different from my project structure.
Is it possible to embed it directly into the binary?
By the way I'm getting issues with namespaces, it seems my custom namespace is not linked to my localized resource file.
I've been searching for hours to find a solution for a C++ CLR/CLI project which is pretty different comparing with C# projects which apparently comes with Build Action and Custom Tool Namespace all these options we don't have in a CLR/CLI project, it's really important, especially if we have changed Namespaces so we gotta use Resource Logical Name instead. Here's my answer how to solve Namespace issues, this also works for localized resource files linked to satellite dlls.
After your localized satellite dll is generated, include that in your project as Compiled Managed Resource you can set that by opening its file property and setting the Item Type. In projects such as C# you won't find that but something similar like "Embedded Resource". Anyways this is intended to C++ CLR/CLI projects only. If you have changed namespaces, don't forget to set Resource Logical Name of the respective resource file.
Next step is to do some code in order to embed that dll into our exe application, here's a good one for that:
Since C++ CLR/CLI doesn't support lambda expressions we have to do this way:
private: System::Reflection::Assembly^ currentDomainAssemblyResolve(System::Object^ sender, System::ResolveEventArgs^ args) {
System::Reflection::AssemblyName^ assemblyName = gcnew System::Reflection::AssemblyName(args->Name);
System::String^ resourceName = assemblyName->Name + ".dll";
System::IO::Stream^ stream = System::Reflection::Assembly::GetExecutingAssembly()->GetManifestResourceStream(resourceName);
array<Byte>^ assemblyData = gcnew array<Byte>((unsigned long) stream->Length);
try {
stream->Read(assemblyData, 0, assemblyData->Length);
} finally {
if (stream != nullptr) delete stream;
}
return System::Reflection::Assembly::Load(assemblyData);
}
Usage:
//Put it in your constructor before InitializeComponent()
MyClass(void) {
AppDomain::CurrentDomain->AssemblyResolve += gcnew System::ResolveEventHandler(this, &MyNameSpace::MyClass::currentDomainAssemblyResolve);
InitializeComponent();
}
So now it's no longer necessary satellite dlls to load your localized resources.
Use a free application packer to bundle files into a single exe.
https://enigmaprotector.com/en/aboutvb.html
This one is free, I use it and it works very well for me.

WPD Object Filename Truncated at '.'

In my project, I'm using the Windows Portable Device (WPD) API to enumerate the contents of a mobile device. WPD API Enumeration Guide. I'm able to enumerate over each object and view their properties as shown in the API programming guide. WPD API Properties Guide
However when I try to get an object's name that has a . within the name, the returned value is truncated at that .
HRESULT hr = objectProperties->GetStringValue(WPD_OBJECT_NAME, &strOriginalFileName);
if(FAILED(hr))
return false;
PWSTR wideStr = strOriginalFileName;
char buffer[20];
wcstombs(buffer, wideStr, 20);
qDebug() << buffer;
So for example, an object (folder on the device) with the name of com.example is returned as com. This becomes an obvious issue when I'm trying to locate a specific filepath on the device.
I can't seem to figure out what's wrong. Am I misunderstanding how the filename actually is? Is example another property or something within the com object? I'm very confused.
EDIT:
So I used the WPD API sample software to retrieve all the object properties of the com.example object and you can see that WPD itself cannot get the full folder name.
Thanks for your time!
The WPD Application Programming Reference refers following 3 NAMEs.
WPD_OBJECT_HINT_LOCATION_DISPLAY_NAME: A friendlier name, mostly intended for display
WPD_OBJECT_NAME: The name of the object on device.
WPD_OBJECT_ORIGINAL_FILE_NAME: The original filename of the object on device.
The MS code sample in C++ uses WPD_OBJECT_ORIGINAL_FILE_NAME to get to the actual file name (underneath the object) while transferring files from device to PC.
I modified the MS code sample (to enumerate object properties) and it showed me the actual file name (nothing truncated from the filename com.ef1.first.second)
I used:
Windows Windows 7 Ultimate (without SP1)
Visual Studio 2013
Android 4.4.4 (Moto-E)
Connection type: MTP
Memory type: Internal Memory as well as External (SD Card)
I wouldn't be surprised if it doesn't work on some combination of Windows versions, Windows SDK versions, android versions, Connection types (MTP, PTP, USB Mass Storage).
Here is the part of code that I modified (and that is how it worked).
// Reads properties for the user specified object.
void ReadContentProperties(_In_ IPortableDevice* device)
{
//.... Edited for brevity
tempHr = propertiesToRead->Add(WPD_OBJECT_NAME);
if (FAILED(tempHr))
{
wprintf(L"! Failed to add WPD_OBJECT_NAME to IPortableDeviceKeyCollection, hr= 0x%lx\n", tempHr);
}
// Here is the added code
tempHr = propertiesToRead->Add(WPD_OBJECT_ORIGINAL_FILE_NAME);
if (FAILED(tempHr))
{
wprintf(L"! Failed to add WPD_OBJECT_ORIGINAL_FILE_NAME to IPortableDeviceKeyCollection, hr= 0x%lx\n", tempHr);
}
//.... Edited for brevity
}

Write Excel file from rdl/rdlc file in Windows application

I have rdl files made by Report Builder 3.0. I need to use them in my Windows application written in C++ in way so that I am able to set SQL Server connection parameters and Report location at runtime.
Is this possible and how?
Google told me that I need to convert rdl to rdlc file, but even then I couldn't find a way of setting these parameters and calling rdlc file from C++.
You can use ReportViewer and use RDL or RDLC files but RDL could have some limitations since it doesn't include necessary information to create data-binding code.
RDL files do not contain some information that the design-time of the
ReportViewer control depends on for automatically generating
data-binding code. By manually binding data, RDL files can be used in
the ReportViewer control.
You can set the parameters from ReportViewer, check this documentation and the below code example in C#:
private void SetReportParameters() {
// Set Processing Mode
reportViewer1.ProcessingMode = ProcessingMode.Remote;
// Set report server and report path
reportViewer1.ServerReport.ReportServerUrl = new
Uri("http://<ServerName>/reportserver");
reportViewer1.ServerReport.ReportPath =
"/AdventureWorks Sample Reports/Employee Sales Summary";
List<ReportParameter> paramList = new List<ReportParameter>();
paramList.Add(new ReportParameter("EmpID", "288", false));
paramList.Add(new ReportParameter("ReportMonth", "12", false));
paramList.Add(new ReportParameter("ReportYear", "2003", false));
this.reportViewer1.ServerReport.SetParameters(paramList);
// Process and render the report
reportViewer1.RefreshReport();
}
This is an example of ReportViewer usage to generate Excel files:
protected void Button1_Click(object sender, EventArgs e)
{
Warning[] warnings;
string[] streamids;
string mimeType;
string encoding;
string extension;
byte[] bytes = ReportViewer1.LocalReport.Render(
"Excel", null, out mimeType, out encoding,
out extension,
out streamids, out warnings);
FileStream fs = new FileStream(#"c:\output.xls",
FileMode.Create);
fs.Write(bytes, 0, bytes.Length);
fs.Close();
Label1.Text = "Report exported to output.xls";
}
Let me know if this can help you.

Using WRL in Windows runtime component to get folder path returns empty string

I'm currently working on porting the Boost 'filesystem' library to Windows Phone 8.1. I succeeded in porting about half of the problematic functions by substituting 'banned' Win32 API functions with other, newer non-banned functions.
I now need to tackle those Boost functions for which there is no alternative Win32 API. Based on Steve Gates's excellent port of other Boost libraries to WP8.1, and in a private communication with him, I have decided to use WRL within the Boost code, rather than C++/CX.
To learn WRL and get my bearings, I wrote a minimal WP8.1 app consisting of a C++/CX client app that calls down into a Windows Runtime Component, the latter written in C++. In the runtime component I have a function that attempts to determine the file system path of the Picture Library. The problem I'm encountering is that the final path I get (i.e., pszPath) is an empty string.
Here is the runtime component code:
void Class1::Test1()
{
HRESULT hr;
HString hstrKnownFolders;
hstrKnownFolders.Set(RuntimeClass_Windows_Storage_KnownFolders);
// Get the Activation Factory
ComPtr<IActivationFactory> pKnownFoldersActivationFactory;
hr = ABI::Windows::Foundation::GetActivationFactory(hstrKnownFolders.Get(),
&pKnownFoldersActivationFactory);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
// QI for the IKnownFoldersStatics
ComPtr<IKnownFoldersStatics> pKnownFolders;
hr = pKnownFoldersActivationFactory.As(&pKnownFolders);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
// Get the Pictures library folder
ComPtr<IStorageFolder> pStorageFolder;
hr = pKnownFolders->get_PicturesLibrary(&pStorageFolder);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
// QI for the IStorageItem interface (from which IStorageFolder is derived)
ComPtr<IStorageItem> pItem;
hr = pStorageFolder.As(&pItem);
// Get the path corresponding to the folder
HSTRING hsPath;
pItem->get_Path(&hsPath);
PCWSTR pszPath = WindowsGetStringRawBuffer(hsPath, 0);
}
At the end of the function, the function get_Path() returns an empty string. Can anyone shed light one what I'm doing wrong, and how it should be done?
Thanks in advance!
No path is the right result: the Pictures library is a shell folder which compiles data from several locations (such as the public Pictures directory and the user's picture directory). The Pictures library itself doesn't have a path.
Individual items within the library probably have paths, but they may not be paths in the same file system directory.
StorageFiles are not limited to "files" from the file system. They can also include objects from elsewhere in the shell and objects from other apps. All of these are represented as file streams, but don't necessarily have file system paths.

Creating a hash-value of a directory in Qt

Is there any mean to determine if the directory content (including deep subdirectory structure) has changed since last access? I'm looking for a portable solution in C/C++, preferably in Qt.
P.S.:
If relevant, the background of the question.
In my application I have to scan recursively many directories and import some data in a database, when some conditions are true. Once the directory was imported, I mark it with a file ".imported" and ignore next times.
Now I want to mark also scanned but not imported directories. For this I'd store a file containing a directory hash. So, prior to scan I could compare the hash calculated with the last hash in file and skip scanning if they are equal.
There is a QFileSystemWatcher class that will notify you of changes.
If you want to create a Cryptographic Hash of a directory and its contents, this is how I do it: -
void AddToHash(const QFileInfo& fileInf, QCryptographicHash& cryptHash)
{
QDir directory(fileInf.absoluteFilePath());
directory.setFilter(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files);
QFileInfoList fileInfoList = directory.entryInfoList();
foreach(QFileInfo info, fileInfoList)
{
if(info.isDir())
{
// recurse through all directories
AddToHash(info, cryptHash);
continue;
}
// add all file contents to the hash
if(info.isFile())
{
QFile file(info.absoluteFilePath());
if(!file.open(QIODevice::ReadOnly))
{
// failed to open file, so skip
continue;
}
cryptHash.addData(&file);
file.close();
}
}
}
// create a fileInfo from the top-level directory
QFileInfo fileInfo(filePath);
QString hash;
// Choose an arbitrary hash, say Sha1
QCryptographicHash cryptHash(QCryptographicHash::Sha1);
// add all files to the hash
AddToHash(fileInfo, cryptHash);
// get a printable version of the hash
hash = cryptHash.result().toHex();