I'm trying to download a file to
char* appdata = getenv("APPDATA");
lpURLDownloadToFile URLDownloadToFile;
HMODULE hUrlmon = LoadLibrary("URLMON.DLL");
URLDownloadToFile = (lpURLDownloadToFile)GetProcAddress(hUrlmon, "URLDownloadToFileA");
URLDownloadToFile(0, "http://example.com/test.zip",appdata+"test.zip", 0, 0);
I don't have an error when I write the path manually,But ı got error when I try to use appdata+"test.zip"
How can I do it. Thanks.
You cannot combine char* pointers by adding them like so, you need to concatenate those strings. You might want to use std:string instead which support operator+=.
std::string appdata(getenv("APPDATA"));
appdata += "test.zip";
HMODULE hUrlmon = LoadLibrary("URLMON.DLL");
URLDownloadToFile = (lpURLDownloadToFile)GetProcAddress(hUrlmon, "URLDownloadToFileA");
URLDownloadToFile(0, "http://example.com/test.zip", appdata.c_str(), 0, 0);
Related
Using C++ (on Windows 10), I'm trying to execute a command in cmd.exe that execute a python file that takes in another file (in csv format).
What I would like to do is the same thing as if I were typing on the command line something like this:
python3 .\plotCSV.py .\filetoplot.csv
Or in better mode:
python3 C:\...\Documents\plotCSV.py C:\...\Documents\filetoplot.csv
For this I'm using ShellExecute like this:
ShellExecute(NULL, "open", "C:\\Windows\\System32\\cmd.exe", "/c \"python3 C:\...\Documents\plotCSV.py C:\...\Documents\filetoplot.csv\"", NULL, SW_SHOWDEFAULT);
And for the csv file selected (filetoplot.csv for example) this works. Except that, for what I need, the name of the csv file is generate and changes every time in my C++ program and is saved in a variable file_name.c_str(). So, if I use this in ShellExecute I have:
ShellExecute(NULL, "open", "C:\\Windows\\System32\\cmd.exe", "/c \"python3 C:\...\Documents\plotCSV.py C:\...\Documents\file_name.c_str()\"", NULL, SW_SHOWDEFAULT);
But unfortunately (obviously) it doesn't work because there really isn't a csv file renamed "file_name.c_str()".
I found also the function ShellExecuteEx and wanting to repeat the same procedure I think the function should be used like this:
SHELLEXECUTEINFO info = {0};
info.cbSize = sizeof(SHELLEXECUTEINFO);
info.fMask = SEE_MASK_NOCLOSEPROCESS;
info.hwnd = NULL;
info.lpVerb = NULL;
info.lpFile = "cmd.exe";
info.lpParameters = ("python3 C:\...\Documents\plotCSV.py C:\...\Documents\file_name.c_str()");
info.lpDirectory = NULL;
info.nShow = SW_SHOW;
info.hInstApp = NULL;
ShellExecuteEx(&info);
But even here it doesn't work (I probably misinterpret how the function works).
Hoping I have explained myself well, I kindly ask for your advice on how to proceed in this regard.
Thank you very much
You are trying to write code inside a string literal.
That is not possible in C++!
You need to first create your dynamic parameter string, then pass it to a function.
std::string has an overloaded + operator that supports string literals (const char *).
std::string param1 = "/c \"python3 C:\\...\\Documents\\plotCSV.py C:\\...\\Documents\\" + file_name + '\"';
ShellExecute(NULL, "open", "C:\\Windows\\System32\\cmd.exe", param1.c_str(), NULL, SW_SHOWDEFAULT);
I'd like to get the name of an application on Windows.
Currently I'm using EnumProcesses() to enumerate all processes and receive a list of PIDs.
Then I'm looping through all PIDs, each iteration looks like this, when aProcess[i] is the current PID:
HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, aProcesses[i]);
std::string processName = get_process_name(proc);
My get_process_name(proc) function uses GetModuleFileNameEx to get the executable path and GetProcessImageFileName in order to retrieve the name of the executable file.
What I want to retrieve is basically the App Name, as it is displayed in the Windows Task Manager.
I've looked throughout Win32 API's documentation and could not find a clue on how to achieve this.
I've tried looking for other ways such as Windows Shell tasklist but it outputs different things, for example- Google Chrome:
Image Name: chrome.exe PID: 84 Session Name: Console
I'd really appreciate any thought on the matter, whether it be the Win32 API or some other way I can implement through C++ code.
You can do this with GetFileVersionInfoA and VerQueryValueA.
You just need to follow the example given in the VerQueryValueA document.
Here is my sample:
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *lpTranslate;
int main()
{
HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION , FALSE, 2140); //Modify pid to the pid of your application
if (!handle) return 0;
wchar_t pszFile[MAX_PATH] = L"";
DWORD len = MAX_PATH;
QueryFullProcessImageName(handle, 0, pszFile, &len);
UINT dwBytes, cbTranslate;
DWORD dwSize = GetFileVersionInfoSize(pszFile, (DWORD*)&dwBytes);
if (dwSize == 0) return 0;
LPVOID lpData = (LPVOID)malloc(dwSize);
ZeroMemory(lpData, dwSize);
if (GetFileVersionInfo(pszFile, 0, dwSize, lpData))
{
VerQueryValue(lpData,
L"\\VarFileInfo\\Translation",
(LPVOID*)&lpTranslate,
&cbTranslate);
wchar_t strSubBlock[MAX_PATH] = { 0 };
wchar_t* lpBuffer;
for (int i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); i++)
{
StringCchPrintf(strSubBlock,50,
L"\\StringFileInfo\\%04x%04x\\FileDescription",
lpTranslate[i].wLanguage,
lpTranslate[i].wCodePage);
VerQueryValue(lpData,
strSubBlock,
(void**)&lpBuffer,
&dwBytes);
std::wcout << lpBuffer << std::endl;
}
}
if(lpData) free(lpData);
if (handle) CloseHandle(handle);
return 0;
}
And it works for me:
I think what you want are the "version" resources embedded in the PE file (the executables.)
You seem to be familiar with using Win32 API, so I'm just going to give you some hints.
You have to use LoadLibraryEx to load the EXE file (the Ex suffix is to enable passing the LOAD_LIBRARY_AS_DATAFILE flag,) and then call EnumResourceTypes (also see EnumResourceNames) to enumerate all the resource types/resources in the file, and find what you are looking for and then extract the data with LoadResource. The resource type you want is RT_VERSION.
I'm sure I'm omitting a lot of details (as per usual for Win32 programming,) and there might not be a need for enumeration at all; in which case you may want to call FindResource or FindResourceEx directly (if there is a fixed name for this particular resource.)
As further clarification, this gives you the date you see if you right-click on the EXE file (not the shortcut) in Windows Explorer and select "Properties", then go to the "Details" tab. If that information is indeed what you want (e.g. the "File description" field) then the above method should give you the data.
I'm working on a little singleplayer cheat for a game and am planning on making it public, I have written the API and it all works well but I want to save the data that the user enters to a .ini file so that they will only have to log in one time unless their credentials change. I have done this and it works with a relative path C:\Users\Name\Documents\Cheat\Authorise.ini Although When check the result, it doesn't seem to save nor read the data in the file.
I'm wondering if there is a better way to get the Documents Directory.
Function:
std::string authFile = "C:\\Users\\%USERNAME%\\Documents\\Cheats\\Authorise.ini";
std::string username = GUI::Files::ReadStringFromIni(authFile, "Login", "Username");
std::string password = GUI::Files::ReadStringFromIni(authFile, "Login", "Password");`
Since you're on Windows, you should use the Windows API call available for this very purpose in <ShlObj.h> called SHGetKnownFolderPath. Note that you had best use a std::wstring instead for this purpose, since there is no variant of SHGetKnownFolderPath that accepts an MBCS or ANSI string. Also, this will get you the entire path to the user's profile directory, not just the username.
PWSTR path;
SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, NULL, &path);
std::wstring strpath(path);
CoTaskMemFree(path);
Also, once you're done with path, free it with CoTaskMemFree.
Different versions of Windows store user profiles in different locations, and even the default name of the Documents folder can differ. In fact, the name and location of the user's Documents folder is fully customizable by the user, and may not even be located under the user's profile at all. So, you should not assume the Documents folder is always located at C:\\Users\\%USERNAME%\\Documents.
The best way to get the correct path to a user's Documents folder on all versions of Windows is to simply ask Windows itself. Use SHGetFolderPath(CSIDL_MYDOCUMENTS) (pre-Vista) or SHGetKnownFolderPath(FOLDERID_Documents) (Vista+) for that, eg:
#include <shlobj.h>
#include <shlwapi.h>
std::string GetDocumentsFolder()
{
std::string path;
char szPath[MAX_PATH+1] = {};
if (SHGetFolderPathA(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, szPath) == S_OK)
path = PathAddBackslashA(szPath);
/*
PWSTR pPath = NULL;
if (SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_DEFAULT, NULL, &pPath) == S_OK)
{
int wlen = lstrlenW(pPath);
int len = WideCharToMultiByte(CP_ACP, 0, pPath, wlen, NULL, 0, NULL, NULL);
if (len > 0)
{
path.resize(len+1);
WideCharToMultiByte(CP_ACP, 0, pPath, wlen, &path[0], len, NULL, NULL);
path[len] = '\\';
}
CoTaskMemFree(pPath);
}
*/
return path;
}
std::string GetAuthFilePath()
{
std::string path = GetDocumentsFolder();
if (!path.empty())
path += "Cheats\\Authorise.ini";
return path;
}
std::string authFile = GetAuthFilePath();
...
In C++, how can I retrieve the location of a mounted drive?
for example, if I have mounted drive s: to c:\temp (using subst in the command line)
"subst c:\temp s:"
how can I get "c:\temp" by passing "s:"
I would also like to know how can it be done for a network drive.
(if s: is mounted to "\MyComputer\Hello", then I want to retrieve "\MyComputer\Hello" and then to retrieve "c:\Hello" from that)
It might be a very easy question but I just couldn't find information about it.
Thanks,
Adam
If you've used SUBST, the API you want is QueryDosDevice. You can SUBST things yourself by using DefineDosDevice.
You can probably use the GetVolumeInformation function. From the documentation:
Symbolic link behavior
If the path points to a symbolic link, the function returns volume information for the
target.
Haven't tested it myself, though.
To find the path of a mounted network share, you have to use the WNet APIs:
wstring ConvertToUNC(wstring sPath)
{
WCHAR temp;
UNIVERSAL_NAME_INFO * puni = NULL;
DWORD bufsize = 0;
wstring sRet = sPath;
//Call WNetGetUniversalName using UNIVERSAL_NAME_INFO_LEVEL option
if (WNetGetUniversalName(sPath.c_str(),
UNIVERSAL_NAME_INFO_LEVEL,
(LPVOID) &temp,
&bufsize) == ERROR_MORE_DATA)
{
// now we have the size required to hold the UNC path
WCHAR * buf = new WCHAR[bufsize+1];
puni = (UNIVERSAL_NAME_INFO *)buf;
if (WNetGetUniversalName(sPath.c_str(),
UNIVERSAL_NAME_INFO_LEVEL,
(LPVOID) puni,
&bufsize) == NO_ERROR)
{
sRet = wstring(puni->lpUniversalName);
}
delete [] buf;
}
return sRet;;
}
I am looking for a way to create a ZIP file from a folder in Windows C/C++ APIs. I can find the way to do this in VBScript using the Shell32.Application CopyHere method, and I found a tutorial explaining how to do it in C# also, but nothing for the C API (C++ is fine too, project already uses MFC).
I'd be really grateful if anyone can share some sample C code that can successfully create a zip file on Windows XP/2003. Failing that, if someone can find solid docs or a tutorial that would be great, since MSDN searches don't turn up much. I'm really hoping to avoid having to ship a third-party lib for this, because the functionality is obviously there, I just can't figure out how to access it. Google searches turn up nothing useful, just tantalizing bits and pieces of information. Here's hoping someone in the community has sorted this out and can share it for posterity!
As noted elsewhere in the comments, this will only work on a already-created Zip file. The content must also not already exist in the zip file, or an error will be displayed. Here is the working sample code I was able to create based on the accepted answer. You need to link to shell32.lib and also kernel32.lib (for CreateToolhelp32Snapshot).
#include <windows.h>
#include <shldisp.h>
#include <tlhelp32.h>
#include <stdio.h>
int main(int argc, TCHAR* argv[])
{
DWORD strlen = 0;
char szFrom[] = "C:\\Temp",
szTo[] = "C:\\Sample.zip";
HRESULT hResult;
IShellDispatch *pISD;
Folder *pToFolder = NULL;
VARIANT vDir, vFile, vOpt;
BSTR strptr1, strptr2;
CoInitialize(NULL);
hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD);
if (SUCCEEDED(hResult) && pISD != NULL)
{
strlen = MultiByteToWideChar(CP_ACP, 0, szTo, -1, 0, 0);
strptr1 = SysAllocStringLen(0, strlen);
MultiByteToWideChar(CP_ACP, 0, szTo, -1, strptr1, strlen);
VariantInit(&vDir);
vDir.vt = VT_BSTR;
vDir.bstrVal = strptr1;
hResult = pISD->NameSpace(vDir, &pToFolder);
if (SUCCEEDED(hResult))
{
strlen = MultiByteToWideChar(CP_ACP, 0, szFrom, -1, 0, 0);
strptr2 = SysAllocStringLen(0, strlen);
MultiByteToWideChar(CP_ACP, 0, szFrom, -1, strptr2, strlen);
VariantInit(&vFile);
vFile.vt = VT_BSTR;
vFile.bstrVal = strptr2;
VariantInit(&vOpt);
vOpt.vt = VT_I4;
vOpt.lVal = 4; // Do not display a progress dialog box
hResult = NULL;
printf("Copying %s to %s ...\n", szFrom, szTo);
hResult = pToFolder->CopyHere(vFile, vOpt); //NOTE: this appears to always return S_OK even on error
/*
* 1) Enumerate current threads in the process using Thread32First/Thread32Next
* 2) Start the operation
* 3) Enumerate the threads again
* 4) Wait for any new threads using WaitForMultipleObjects
*
* Of course, if the operation creates any new threads that don't exit, then you have a problem.
*/
if (hResult == S_OK) {
//NOTE: hard-coded for testing - be sure not to overflow the array if > 5 threads exist
HANDLE hThrd[5];
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPALL ,0); //TH32CS_SNAPMODULE, 0);
DWORD NUM_THREADS = 0;
if (h != INVALID_HANDLE_VALUE) {
THREADENTRY32 te;
te.dwSize = sizeof(te);
if (Thread32First(h, &te)) {
do {
if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID)) ) {
//only enumerate threads that are called by this process and not the main thread
if((te.th32OwnerProcessID == GetCurrentProcessId()) && (te.th32ThreadID != GetCurrentThreadId()) ){
//printf("Process 0x%04x Thread 0x%04x\n", te.th32OwnerProcessID, te.th32ThreadID);
hThrd[NUM_THREADS] = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
NUM_THREADS++;
}
}
te.dwSize = sizeof(te);
} while (Thread32Next(h, &te));
}
CloseHandle(h);
printf("waiting for all threads to exit...\n");
//Wait for all threads to exit
WaitForMultipleObjects(NUM_THREADS, hThrd , TRUE , INFINITE);
//Close All handles
for ( DWORD i = 0; i < NUM_THREADS ; i++ ){
CloseHandle( hThrd[i] );
}
} //if invalid handle
} //if CopyHere() hResult is S_OK
SysFreeString(strptr2);
pToFolder->Release();
}
SysFreeString(strptr1);
pISD->Release();
}
CoUninitialize();
printf ("Press ENTER to exit\n");
getchar();
return 0;
}
I have decided not to go this route despite getting semi-functional code, since after further investigation, it appears the Folder::CopyHere() method does not actually respect the vOptions passed to it, which means you cannot force it to overwrite files or not display error dialogs to the user.
In light of that, I tried the XZip library mentioned by another poster as well. This library functions fine for creating a Zip archive, but note that the ZipAdd() function called with ZIP_FOLDER is not recursive - it merely creates a folder in the archive. In order to recursively zip an archive you will need to use the AddFolderContent() function. For example, to create a C:\Sample.zip and Add the C:\Temp folder to it, use the following:
HZIP newZip = CreateZip("C:\\Sample.zip", NULL, ZIP_FILENAME);
BOOL retval = AddFolderContent(newZip, "C:", "temp");
Important note: the AddFolderContent() function is not functional as included in the XZip library. It will recurse into the directory structure but fails to add any files to the zip archive, due to a bug in the paths passed to ZipAdd(). In order to use this function you'll need to edit the source and change this line:
if (ZipAdd(hZip, RelativePathNewFileFound, RelativePathNewFileFound, 0, ZIP_FILENAME) != ZR_OK)
To the following:
ZRESULT ret;
TCHAR real_path[MAX_PATH] = {0};
_tcscat(real_path, AbsolutePath);
_tcscat(real_path, RelativePathNewFileFound);
if (ZipAdd(hZip, RelativePathNewFileFound, real_path, 0, ZIP_FILENAME) != ZR_OK)
We use XZip for this purpose. It's free, comes as C++ source code and works nicely.
http://www.codeproject.com/KB/cpp/xzipunzip.aspx
EDIT: This answer is old, but I cannot delete it because it was accepted. See the next one
https://stackoverflow.com/a/121720/3937
----- ORIGINAL ANSWER -----
There is sample code to do that here
[EDIT: Link is now broken]
http://www.eggheadcafe.com/software/aspnet/31056644/using-shfileoperation-to.aspx
Make sure you read about how to handle monitoring for the thread to complete.
Edit: From the comments, this code only works on existing zip file, but #Simon provided this code to create a blank zip file
FILE* f = fopen("path", "wb");
fwrite("\x50\x4B\x05\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 22, 1, f);
fclose(f);
The above code to create an empty zip file is broken, as the comments state, but I was able to get it to work. I opened an empty zip in a hex editor, and noted a few differences. Here is my modified example:
FILE* f = fopen("path", "wb");
fwrite("\x50\x4B\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 22, 1, f);
fclose(f);
This worked for me. I was able to then open the compressed folder. Not tested with 3rd party apps such as winzip.
A quick Google search came up with this site: http://www.example-code.com/vcpp/vcUnzip.asp which has a very short example to unzip a file using a downloadable library. There are plenty of other libraries available. Another example is availaible on Code Project entitled Zip and Unzip in the MFC way which has an entire gui example. If you want to do it with .NET then there is always the classes under System.Compression.
There is also the 7-Zip libarary http://www.7-zip.org/sdk.html. This includes source for several languages, and examples.
I do not think that MFC or the Windows standard C/C++ APIs provide an interface to the built in zip functionality.
You could always statically link to the freeware zip library if you don't want to ship another library...