How to use IFileOperation CopyItems for a destination ZIP file - c++

I am using IFileOperation interface's CopyItems method to copy one more files from source folder to a destination folder. This works without any error as long as the destination folder is a standard file system directory. However, my main motivation of using IFileOperation instead of SHFileOperation() method is to allow copying items from / into non-file-system objects as the documentation says (https://msdn.microsoft.com/en-us/library/windows/desktop/bb775771%28v=vs.85%29.aspx).
The following code works perfectly for file-system destination e.g. "g:\my folder" but does not work if the destination is a ZIP file e.g. "g:\Super Cars.zip". For this quick sample please overlook the resource leaks :).
Please note the ZIP file is an existing ZIP file created using standard built-in ZIP capability of Windows and it does not contain the file is am trying to copy.
#include "stdafx.h"
#include <Windows.h>
#include <shellapi.h>
#include <Shlobj.h>
#include <iostream>
#include <atlbase.h>
void CopyFile(LPCTSTR strSource, LPCTSTR strDestination)
{
PIDLIST_ABSOLUTE abSourcePidl = nullptr;
SFGAOF attrs;
HRESULT hr = SHParseDisplayName(strSource, nullptr, &abSourcePidl, 0, &attrs);
if (FAILED(hr))
{
std::wcout << _T("SHParseDisplayName failed for ") << strSource << std::endl;
return;
}
PIDLIST_ABSOLUTE abDestPidl = nullptr;
hr = SHParseDisplayName(strDestination, nullptr, &abDestPidl, 0, &attrs);
if (FAILED(hr))
{
std::wcout << _T("SHParseDisplayName failed for ") << strDestination << std::endl;
return;
}
IShellItem2* pDestShellItem;
hr = SHCreateItemFromIDList(abDestPidl, IID_IShellItem2, reinterpret_cast<LPVOID*>(&pDestShellItem));
if (FAILED(hr))
{
std::wcout << _T("SHCreateItemFromIDList failed") << std::endl;
return;
}
PIDLIST_ABSOLUTE* pPidlRawArray = new PIDLIST_ABSOLUTE[1];
pPidlRawArray[0] = abSourcePidl;
IShellItemArray* pShellItemArr = nullptr;
hr = SHCreateShellItemArrayFromIDLists(1, (LPCITEMIDLIST*)pPidlRawArray, &pShellItemArr);
if (FAILED(hr))
{
std::wcout << _T("SHCreateShellItemArrayFromIDLists failed") << std::endl;
return;
}
CComPtr<IFileOperation> fileOp;
hr = fileOp.CoCreateInstance(CLSID_FileOperation);
if (FAILED(hr))
{
std::wcout << _T("Creation of IFileOperation failed") << std::endl;
return;
}
hr = fileOp->CopyItems(pShellItemArr, pDestShellItem);
if (FAILED(hr))
{
std::wcout << _T("CopyItems fall failed") << std::endl;
return;
}
hr = fileOp->PerformOperations();
if (FAILED(hr))
{
std::wcout << _T("PerformOperations call failed") << std::endl;
return;
}
std::wcout << _T("File copied successfully") << std::endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
CopyFile(_T("D:\\TEMP\\COMTIME.TXT"), _T("G:\\My Folder")); // WORKS!!
CopyFile(_T("D:\\TEMP\\COMTIME.TXT"), _T("G:\\Super Cars.zip")); // DOES NOT WORK!!
CoUninitialize();
return 0;
}

Related

Dynamically allocate buffer[] for URLOpenBlockingStreamW() output

I'm super new to C++ and I'm trying to download an executable file from a URL and write it to disk.
I have the below code which successfully downloads the file and stores it in memory. The issue I am having is then writing that to disk.
I am pretty sure this is down to where I am creating the buffer where the downloaded data will be written to before being going to the new file.
char buffer[4096];
The 4096 is an arbitrary number from the template code I got elsewhere. What I can't figure out or don't know is how to dynamically allocate that buffer size based on the size of the data at &pStream.
I have tried using functions such as sizeof() but these just get me the memory address size rather than the value itself.
Alternatively, is there better way to try and accomplish this download and write?
#include <Windows.h>
#include <Urlmon.h> // URLOpenBlockingStreamW()
#include <atlbase.h> // CComPtr
#include <iostream>
#include "download.h"
#include <fstream>
#include <assert.h>
#include <chrono>
#include <thread>
#pragma comment( lib, "Urlmon.lib" )
struct ComInit
{
HRESULT hr;
ComInit() : hr(::CoInitialize(nullptr)) {}
~ComInit() { if (SUCCEEDED(hr)) ::CoUninitialize(); }
};
int download_file()
{
ComInit init;
HRESULT hr;
// use CComPtr so you don't have to manually call Release()
CComPtr<IStream> pStream;
bool success = false;
while (success == false)
{
try {
// Open the HTTP request.
hr = URLOpenBlockingStreamW(nullptr, L"https://www.foo.bar/download/somefile.exe", &pStream, 0, nullptr);
if (FAILED(hr))
{
std::cout << "ERROR: Could not connect. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
}
else
{
success = true;
}
}
catch (const std::exception& ex) {
std::cout << ex.what();
}
}
// Download the response and write it to stdout.
char buffer[4096]; // Issue is here I think
do
{
DWORD bytesRead = 0;
hr = pStream->Read(buffer, sizeof(buffer), &bytesRead);
if (bytesRead > 0)
{
//std::cout.write(buffer, bytesRead);
std::ofstream file;
file.open("some_path_dot_exe", std::ios_base::binary);
assert(file.is_open());
for (int i = 0; i < sizeof(buffer) / sizeof(buffer[0]); ++i)
file.write((char*)(buffer + i * sizeof(buffer[0])), sizeof(buffer[0]));
file.close();
}
} while (SUCCEEDED(hr) && hr != S_FALSE);
if (FAILED(hr))
{
std::cout << "ERROR: Download failed. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
return 2;
}
std::cout << "\n";
return 0;
}
The IStream that URLOpenBlockingStreamW() gives you isn't guaranteed to be able to give you the full file size up front, so if you want to hold the entire file in memory, you will have to use std::vector or other dynamically-growing buffer.
Though, you don't actually need to hold the entire file in memory just to save it to disk, you can use a fixed array and write it to disk as it is being downloaded, as you already are doing.
The real problem is, you are opening and closing the file on every Read(), wiping out all previous data written. And you are ignoring the bytesRead value that Read() gives you.
You need to open the file one time, leave it open until you are done with the download, and don't write more than is actually in the buffer on each write().
Try this:
#include <Windows.h>
#include <Urlmon.h> // URLOpenBlockingStreamW()
#include <atlbase.h> // CComPtr
#include <iostream>
#include "download.h"
#include <fstream>
#include <assert.h>
#include <chrono>
#include <thread>
#pragma comment( lib, "Urlmon.lib" )
struct ComInit
{
HRESULT hr;
ComInit() : hr(::CoInitialize(nullptr)) {}
~ComInit() { if (SUCCEEDED(hr)) ::CoUninitialize(); }
};
int download_file()
{
ComInit init;
HRESULT hr;
// use CComPtr so you don't have to manually call Release()
CComPtr<IStream> pStream;
do
{
try {
// Open the HTTP request.
hr = URLOpenBlockingStreamW(nullptr, L"https://www.foo.bar/download/somefile.exe", &pStream, 0, nullptr);
if (SUCCEEDED(hr)) break;
std::cout << "ERROR: Could not connect. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
}
catch (const std::exception& ex) {
std::cout << ex.what();
}
}
while (true);
std::ofstream file("some_path_dot_exe", std::ios_base::binary);
if (!file.is_open()) {
std::cout << "ERROR: Download failed. Unable to create output file.\n";
return 1;
}
// Download the response and write it to file.
char buffer[4096];
DWORD bytesRead;
do
{
hr = pStream->Read(buffer, sizeof(buffer), &bytesRead);
if (bytesRead > 0)
file.write(buffer, bytesRead);
} while (SUCCEEDED(hr) && hr != S_FALSE);
file.close();
if (FAILED(hr))
{
std::cout << "ERROR: Download failed. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
return 2;
}
std::cout << "\n";
return 0;
}

How do I determine a mapped drive's details

How do I determine a mapped drive's details like its actual path, FreeSize, and so on? So if I have a mapped drive on a machine called "MP" how can I using C++/Win32 or Qt determine the machine and path for the mapped folder and also other practical details?
I wanted to get extract information from the remote machine filesystem. I can make an SMB connection with the remote machine and get access to shared drive but I wanted to enumerate all information as possible from its file system. How can I do that?
There is a command in Powershell which we can use it to enumerate such information like the following command:
get-WmiObject win32_logicaldisk -Computername remotecomputer
However, I wanted to get such information with written some code in my application and show those information in user friendly format to the user.
You could use WMI class win32_logicaldisk in C++, here is the sample:
#include <stdio.h>
#define _WIN32_DCOM
#include <wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
#include <iostream>
using namespace std;
#include <comdef.h>
void PrintDriveDetails(wstring drive)
{
HRESULT hr;
IWbemLocator* pWbemLocator = NULL;
IWbemServices* pServices = NULL;
IWbemClassObject* pDrive = NULL;
hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, EOAC_NONE, 0);
if (FAILED(hr))
{
CoUninitialize();
cout << "Failed to initialize security. Error code = 0x" << hex << hr << endl;
return;
}
hr = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (void**)&pWbemLocator);
if (FAILED(hr))
{
CoUninitialize();
cout << "Failed to CoCreateInstance. Error code = 0x" << hex << hr << endl;
return;
}
_bstr_t bstrNamespace = L"root\\cimv2";
hr = pWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, NULL, 0, NULL, NULL, &pServices);
if (FAILED(hr))
{
pWbemLocator->Release();
CoUninitialize();
cout << "Failed to Connect to the Server. Error code = 0x" << hex << hr << endl;
return;
}
pWbemLocator->Release();
printf("Successfully connected to namespace.\n");
hr = CoSetProxyBlanket(
pServices,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE
);
if (FAILED(hr))
{
pServices->Release();
cout << "Could not set proxy blanket. Error code = 0x" << hex << hr << endl;
CoUninitialize();
return; // Program has failed.
}
wstring bstrPath = L"Win32_LogicalDisk.DeviceID=\"" + drive + L"\"";
// *******************************************************//
// Perform a full-instance retrieval.
// *******************************************************//
hr = pServices->GetObject(BSTR(bstrPath.c_str()),
0, 0, &pDrive, 0);
if (FAILED(hr))
{
pServices->Release();
CoUninitialize();
cout << "failed GetObject. Error code = 0x" << hex << hr << endl;
return;
}
// Display the object
BSTR bstrDriveObj;
hr = pDrive->GetObjectText(0, &bstrDriveObj);
if (FAILED(hr))
{
pServices->Release();
CoUninitialize();
cout << "failed GetObjectText. Error code = 0x" << hex << hr << endl;
return;
}
printf("%S\n\n", bstrDriveObj);
VARIANT freesize, totlesize;
hr = pDrive->Get(L"FreeSpace", 0, &freesize, 0, NULL);
if (FAILED(hr))
{
pServices->Release();
CoUninitialize();
cout << "failed Get FreeSpace. Error code = 0x" << hex << hr << endl;
return;
}
printf("freesize %S\n", freesize.bstrVal);
hr = pDrive->Get(L"Size", 0, &totlesize, 0, NULL);
if (FAILED(hr))
{
pServices->Release();
CoUninitialize();
cout << "failed Get Size. Error code = 0x" << hex << hr << endl;
return;
}
printf("totlesize : %S\n", totlesize.bstrVal);
VariantClear(&freesize);
VariantClear(&totlesize);
pDrive->Release();
pServices->Release();
pServices = NULL;
}
void main(void)
{
HRESULT hr = S_OK;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
PrintDriveDetails(L"Z:");
CoUninitialize();
return;
};

Creating file with CreateFile function results in write protected file

I have been trying to write a file in path returned by SHGetFolderPath . But as File Is created, it has been given Write Protected. Following is my Code:
#include <Windows.h>
#include <Shlobj.h>
#include <iostream>
#include <Shlwapi.h>
int main()
{
HANDLE hfile;
TCHAR szPath[MAX_PATH];
char dataBuffer[] = "Some data to write here";
DWORD dwBytesWritten = 0;
if(SUCCEEDED(SHGetFolderPath(NULL,CSIDL_COMMON_APPDATA,NULL,0, szPath)))
{
std::cout << szPath << std::endl;
PathAppend(szPath,TEXT("lpa"));
std::cout << szPath << std::endl;
PathAppend(szPath,TEXT("config"));
std::cout << szPath << std::endl;
CreateDirectory(szPath, NULL);
PathAppend(szPath, TEXT("lpa.config"));
std::cout << szPath << std::endl;
hfile = CreateFile(szPath,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
/*WriteFile(hfile,dataBuffer,(DWORD)strlen(dataBuffer),&dwBytesWritten,NULL);
std::cout << szPath <<std::endl;*/
}
}
The code creates a file in C:\ProgramData\lpa\config\lpa.config but editing the file pops up dialog saying WriteProtection. What am I doing wrong here?
Yes, When you run VS instance with Admin privileges, special permissions are assigned to the created file. Either run as the program as Non-Admin or use SECURITY_DESCRIPTOR so that other user can access the file.
Creating a Security Descriptor for a New Object in C++
the example in the link shows how a registry key is created, you can put CreateFile() instead.
I solved the issue by using CSIDL_LOCAL_APPDATA instead of CSIDL_COMMON_APPDATA . I have the modified code given as below:
#include <Windows.h>
#include <Shlobj.h>
#include <iostream>
#include <Shlwapi.h>
int main()
{
HANDLE hfile;
TCHAR szPath[MAX_PATH];
char dataBuffer[] = "Some data to write here";
DWORD dwBytesWritten = 0;
if(SUCCEEDED(SHGetFolderPath(NULL,CSIDL_LOCAL_APPDATA,NULL,0, szPath)))
{
std::cout << szPath << std::endl;
PathAppend(szPath,TEXT("lpa"));
if(!CreateDirectory(szPath, NULL))
{
std::cout << "Create directory failed" <<std::endl;
}
std::cout << szPath << std::endl;
PathAppend(szPath,TEXT("config"));
if(!CreateDirectory(szPath, NULL))
{
std::cout << "Create directory failed" <<std::endl;
}
std::cout << szPath << std::endl;
PathAppend(szPath, TEXT("lpa.config"));
std::cout << szPath << std::endl;
hfile = CreateFile(szPath,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
WriteFile(hfile,dataBuffer,(DWORD)strlen(dataBuffer),&dwBytesWritten,NULL);
std::cout << szPath <<std::endl;
}
}

getting external media type

i would like to find a way detecting the type of the media in my optical drive (e.g. DVD+R, DVD-R, DVD-RW, CD+R, etc.) using a simple function in C++ on windows.
The function should not require Admin privilege.
EDIT
I implemented the following code:
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <imapi2.h>
#include <imapi2fs.h>
#include <imapi2error.h>
#include <imapi2fserror.h>
int main(int argc, char *argv[])
{
IDiscFormat2Data* discFormatData = NULL;
HRESULT hr;
CoInitialize ( NULL );
hr = CoCreateInstance( __uuidof(MsftDiscFormat2Data),
NULL,
CLSCTX_ALL,
__uuidof(IDiscFormat2Data),
(void**)&discFormatData);
if ( SUCCEEDED(hr) )
{
IMAPI_MEDIA_PHYSICAL_TYPE mediaType = IMAPI_MEDIA_TYPE_UNKNOWN;
hr = discFormatData->get_CurrentPhysicalMediaType(&mediaType);
if ( SUCCEEDED(hr) )
{
std::cout << "MediaPhysicalType: " << mediaType << std::endl;
}
else
{
std::stringstream str;
str << "get_CurrentPhysicalMediaType() failed with the error: 0x";
str << std::hex << hr << ".";
std::cout << str.str() << std::endl;
}
// Release the interface.
// Tell the COM object that we're done with it.
discFormatData->Release();
}
else
{
std::stringstream str;
str << "CoCreateInstance() failed with the error: 0x" << std::hex << hr;
std::cout << str.str() << std::endl;
}
cin.get();
return 0;
}
at the moment my problem is that i get the following error: E_IMAPI_RECORDER_REQUIRED which means
"The request requires a current disc recorder to be selected."
Assuming i have at least two optical drivers, how can i differ between them?
Any ideas?
On Windows 2000 and later, you can use IOCTL_CDROM_GET_CONFIGURATION with the SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT flag to query an optical device for its current profile, which will tell you which type of disc (CD, DVD+-R/W, HDDVD, BluRay, etc) has been inserted, if any. On earlier versions, you will have to manually send SCSI MMC commands directly to the device to query the same info.

How to Enumerate Names of All Named Pipes in a Process?

I need to open a certain named pipe so I can fuzz test it, however my test code does not have access to the same data used to generate the name of the named pipe. However I can recognize the name of the pipe and then use that name to open up the pipe for fuzzing.
I used this forum post to start enumerating names of the handles on the system:
http://forum.sysinternals.com/howto-enumerate-handles_topic18892.html
However it seems that won't work with named pipes for some reason.
TL;DR: What API(s) do I need to use to list the names of all named pipes in the current process on Windows?
This will enumerate all named pipes in the system, or at the very least put you a step in the right direction.
This works in MinGW when built with -fpermissive. It should work with similar settings in MSVC.
#ifndef _WIN32_WINNT
// Windows XP
#define _WIN32_WINNT 0x0501
#endif
#include <Windows.h>
#include <Psapi.h>
// mycreatepipeex.c is at http://www.davehart.net/remote/PipeEx.c
// I created a simple header based on that.
#include "mycreatepipeex.h"
#include <iostream>
#include <cstdio>
#include <errno.h>
void EnumeratePipes()
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
#define TARGET_PREFIX "//./pipe/"
const char *target = TARGET_PREFIX "*";
memset(&FindFileData, 0, sizeof(FindFileData));
hFind = FindFirstFileA(target, &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
std::cerr << "FindFirstFileA() failed: " << GetLastError() << std::endl;
return;
}
else
{
do
{
std::cout << "Pipe: " << TARGET_PREFIX << FindFileData.cFileName << std::endl;
}
while (FindNextFile(hFind, &FindFileData));
FindClose(hFind);
}
#undef TARGET_PREFIX
return;
}
int main(int argc, char**argv)
{
HANDLE read = INVALID_HANDLE_VALUE;
HANDLE write = INVALID_HANDLE_VALUE;
unsigned char pipe_name[MAX_PATH+1];
BOOL success = MyCreatePipeEx(&read, &write, NULL, 0, 0, 0, pipe_name);
EnumeratePipes();
if ( success == FALSE )
{
std::cerr << "MyCreatePipeEx() failed: " << GetLastError() << std::endl;
return 1;
}
FILE *f = fopen((const char*)pipe_name, "rwb");
if ( f == NULL )
{
std::cerr << "fopen(\"" << pipe_name << "\") failed: " << (int)errno << std::endl;
}
CloseHandle(read);
CloseHandle(write);
return 0;
}