I'm having trouble using CreateProcess with command line arguments. I've read to all the posts I've found but none of the solutions did work.
Here's what I have:
std::string path = "C:\\my\\path\\myfile.exe";
std::wstring stemp = std::wstring(path.begin(), path.end());
LPCWSTR path_lpcwstr = stemp.c_str();
std::string params = " Param1 Param2 Param3";
STARTUPINFO info = { sizeof(info) };
PROCESS_INFORMATION processInfo;
CreateProcess(path_lpcwstr, LPTSTR(params.c_str()), NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &info, &processInfo);
The code works and myfile.exe (a QT Application) is opened, but argc is always 1. I've also tried specifying only the first parameter as "C:\my\path\myfile.exe Param1 Param2 Param3" but that didn't work either.
Any help is greatly appreciated.
Solution:
Using CreateProcessA and change the parameters accordingly fixed the problem pointed out by one of the answers.
STARTUPINFOA info = { sizeof(info) };
PROCESS_INFORMATION processInfo;
std::string path = "C:\\my\\path\\myfile.exe";
std::string params = " Param1 Param2 Param3";
CreateProcessA(path.c_str(), const_cast<char *>(config.c_str()) , NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &info, &processInfo);
There are two versions of CreateProcess (and many other Winapi functions too):
One takes "normal" strings in ASCII/ISO88591/whatever where each character has 1 byte.
"abc" would have the numbers 97 98 99.
The other CreateProcess takes UTF16 strings; each char has 2 or 4 byte there,
and "abc" would have the byte numbers 0 97 0 98 0 99
(UTF16 is a bit more complicated, but in this case, it´s just 0s added).
The advantage is better support for internationalization, because the
old 1-byte charsets are problematic with languages like Russian, Greek etc.
You´re using the second version. path_lpcwstr, ie. the program path and name as first parameter, is correctly provided as UTF16 string by you (std::wstring on Windows and LPCWSTR etc. ...).
However, the second parameter with the arguments for the new process, is not UTF16 in your code (but a one-byte charset) and to avoid a compiler error, you are simply casting a pointer and telling the compiler to treat the not-UTF16 content as UTF16.
The bytes of " Param1 Param2 Param3" understood as UTF16 won´t give any sane string without proper conversion, and to start with, the 2 byte 0 value to terminate the string, as requried by Windows, is nowhere in there. The result is undefined behaviour, any strange things can happen.
Make you parameter string like you did with the path, and everything should be fine.
have you ever try ShellExecuteA()?
Related
I'm trying to add a wide character string to registry in C++. The problem is that the RegSetValueEx() function does not support wide chars, it only supports BYTE type (BYTE = unsigned char).
WCHAR myPath[] = "C:\\éâäà\\éâäà.exe"
RegSetValueExA(HKEY_CURRENT_USER, "MyProgram", 0, REG_SZ, myPath, sizeof(myPath)); // error: cannot convert argument 5 from WCHAR* to BYTE*
And please don't tell me I should convert WCHAR to BYTE because characters such as é and â can't be stored as 8 bit characters.
I'm sure this is possible because I tried opening regedit and adding a new key with value C:\\éâäà\\éâäà.exe and it worked. I wonder how other programs can add themselves to startup on a Russian or Chinese computer.
Is there another way to do so? Or is there a way to format wide character path using wildcards?
Edit: The Unicode version of the function RegSetValueExW() only changes the type of the second argument.
You are calling RegSetValueExA() when you should be calling RegSetValueExW() instead. But in either case, RegSetValueEx() writes bytes, not characters, that is why the lpData parameter is declared as BYTE*. Simply type-cast your character array. The REG_SZ value in the dwType parameter will let RegSetValueEx() know that the bytes represent a Unicode string. And make sure to include the null terminator in the value that you pass to the cbData parameter, per the documentation:
cbSize [in]
The size of the information pointed to by the lpData parameter, in bytes. If the data is of type REG_SZ, REG_EXPAND_SZ, or REG_MULTI_SZ, cbData must include the size of the terminating null character or characters.
For example:
WCHAR myPath[] = L"C:\\éâäà\\éâäà.exe";
RegSetValueExW(HKEY_CURRENT_USER, L"MyProgram", 0, REG_SZ, (LPBYTE)myPath, sizeof(myPath));
Or:
LPCWSTR myPath = L"C:\\éâäà\\éâäà.exe";
RegSetValueExW(HKEY_CURRENT_USER, L"MyProgram", 0, REG_SZ, (LPCBYTE)myPath, (lstrlenW(myPath) + 1) * sizeof(WCHAR));
That being said, you should not be writing values to the root of HKEY_CURRENT_USER itself. You should be writing to a subkey instead, eg:
WCHAR myPath[] = L"C:\\éâäà\\éâäà.exe";
if (RegCreateKeyEx(HKEY_CURRENT_USER, L"Software\\MyProgram", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL) == 0)
{
RegSetValueExW(hKey, L"MyValue", 0, REG_SZ, (LPBYTE)myPath, sizeof(myPath));
RegCloseKey(hKey);
}
It seems to me you're trying to use the narrow/non-wide-char version of that function, which will only support ASCII. How about trying RegSetValueExW? Maybe you should also look up how the Windows API tries to supports ASCII and UNICODE as transparently as possible.
Edit: The Unicode version of the function RegSetValueExW() only changes the type of the second argument.
No it does not.
REG_SZ: A null-terminated string. This will be either a Unicode or an ANSI string, depending on whether you use the Unicode or ANSI functions.
From here:
https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types
Please save me! I am new to this, trying to figure this out. I would like to have my program add a run key to run itself on startup . Here is "my" code:
HKEY hKey = 0;
RegOpenKeyEx( HKEY_LOCAL_MACHINE,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
0,
KEY_ALL_ACCESS,
&hKey );
const unsigned char Path[ MAX_PATH ] = "C:\\test.exe";
RegSetValueEx( hKey, L"Testing", 0, 1, Path, strlen("C:\\test.exe") );
RegCloseKey(hKey);
This "works" except they key added reads "㩃瑜獥硥" under data . Took me a while to figure out that the key is going to WoW6432Node too, thought it complied but wasn't working for the first 5 hours, much head to wall action there...
I am sure this has something to do with the way my string is formatted, ANSII vs ASCII vs the other 10 types of strings C++ doesn't seem to be able to convert between... I've tried using (BYTE*)"C:\virus.exe" and anything else i could think of... If i set the length to 1, the first character shows fine. But if its any other length, Chinese starts to show again.
Please help! I am about ready to start choking kittens here!
The problem is this:
const unsigned char Path[ MAX_PATH ] = "C:\\test.exe";
You have defined an ANSI string and then attempted to use the Unicode (UTF-16) version of RegSetValueEx:
RegSetValueEx( hKey, L"Testing", 0, 1, Path, strlen("C:\\test.exe") );
Under the hood, RegSetValueEx is a macro that aliases to RegSetValueExW because you defined the macro UNICODE.
The correct solution is to use a Unicode string literal:
const wchar_t Path[] = L"C:\\test.exe";
RegSetValueEx( hKey, L"Testing", 0, 1, (const BYTE *) Path, sizeof(Path) );
Here I used sizeof because the string is an array of characters whose size is known at compile time. For dynamic strings, use (wcslen(Path) + 1) * sizeof(*Path) instead.
Note: There is no need to specify the length of a constant literal in the declaration because the compiler can automatically deduce that in this specific scenario. It's also bad idea to duplicate the string literal inside your strlen/wcslen because if it goes out of sync your code could be broken and trigger undefined behavior.
I have a Unicode string stored in CString and I need to know the number bytes this string takes in UTF-8 encoding. I know CString has a method getLength(), but that returns number of characters, not bytes.
I tried (beside other things) converting to char array, but I get (logically, I guess) only array of wchar_t, so this doesn't solve my problem.
To be clear about my goal. For the input lets say "aaa" I want "3" as output (since "a" takes one byte in UTF-8). But for the input "āaa", I'd like to see output "4" (since ā is two byte character).
I think this has to be quite common request, but even after 1,5 hours of search and experimenting, I couldn't find the correct solution.
I have very little experience with Windows programming, so maybe I left out some crucial information. If you feel like that, please let me know, I'll add any information you request.
As your CString contains a series of wchar_t, you can just use WideCharToMultiByte with the output charset as CP_UTF8. The function will return the number of bytes written to the output buffer, or the length of the UTF-8 encoded string
LPWSTR instr;
char outstr[MAX_OUTSTR_SIZE];
int utf8_len = WideCharToMultiByte(CP_UTF8, 0, instr, -1, outstr, MAX_OUTSTR_SIZE, NULL, NULL);
If you don't need the output string, you can simply set the output buffer size to 0
cbMultiByte
Size, in bytes, of the buffer indicated by lpMultiByteStr. If this parameter is set to 0, the function returns the required buffer size for lpMultiByteStr and makes no use of the output parameter itself.
In that case the function will return the number of bytes in UTF-8 without really outputting anything
int utf8_len = WideCharToMultiByte(CP_UTF8, 0, instr, -1, NULL, 0, NULL, NULL);
If your CString is really CStringA, i.e. _UNICODE is not defined, then you need to use MultiByteToWideChar to convert the string to UTF-16 and then convert from UTF-16 to UTF-8 with WideCharToMultibyte. See How do I convert an ANSI string directly to UTF-8? But new code should never be compiled without Unicode support anyway
I created a simple function:
std::wstring GetRegKey(const std::string& location, const std::string& name){
const int valueLength = 10240;
auto platformFlag = KEY_WOW64_64KEY;
HKEY key;
TCHAR value[valueLength];
DWORD bufLen = valueLength*sizeof(TCHAR);
long ret;
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, location.c_str(), 0, KEY_READ | platformFlag, &key);
if( ret != ERROR_SUCCESS ){
return std::wstring();
}
ret = RegQueryValueExA(key, name.c_str(), NULL, NULL, (LPBYTE) value, &bufLen);
RegCloseKey(key);
if ( (ret != ERROR_SUCCESS) || (bufLen > valueLength*sizeof(TCHAR)) ){
return std::wstring();
}
std::wstring stringValue(value, (size_t)bufLen - 1);
size_t i = stringValue.length();
while( i > 0 && stringValue[i-1] == '\0' ){
--i;
}
return stringValue;
}
And I call it like auto result = GetRegKey("SOFTWARE\\Microsoft\\Cryptography", "MachineGuid");
yet string looks like
㤴ㄷ㤵戰㌭㉣ⴱ㔴㍥㤭慣ⴹ㍥摢㘵〴㉡ㄵ\0009ca9-e3bd5640a251
not like RegEdit
4971590b-3c21-45e3-9ca9-e3bd5640a251
So I wonder what shall be done to get a correct representation of MachineGuid in C++?
RegQueryValueExA is an ANSI wrapper around the Unicode version since Windows NT. When building on a Unicode version of Windows, it not only converts the the lpValueName to a LPCWSTR, but it will also convert the lpData retrieved from the registry to an LPWSTR before returning.
MSDN has the following to say:
If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, and
the ANSI version of this function is used (either by explicitly
calling RegQueryValueExA or by not defining UNICODE before including
the Windows.h file), this function converts the stored Unicode string
to an ANSI string before copying it to the buffer pointed to by
lpData.
Your problem is that you are populating the lpData, which holds TCHARs (WCHAR on Unicode versions of Windows) with an ANSI string.
The garbled string that you see is a result of 2 ANSI chars being used to populate a single wchar_t. That explains the Asian characters. The portion that looks like the end of the GUID is because the print function blew past the terminating null since it was only one byte and began printing what is probably a portion of the buffer that was used by RegQueryValueExA before converting to ANSI.
To solve the problem, either stick entirely to Unicode, or to ANSI (if you are brave enough to continue using ANSI in the year 2014), or be very careful about your conversions. I would change GetRegKey to accept wstrings and use RegQueryValueExW instead, but that is a matter of preference and what sort of code you plan on using this in.
(Also, I would recommend you have someone review this code since there are a number of oddities in the error checking, and a hard coded buffer size.)
Hello I have the following code but it isn't working as expected, can't figure out what the problem is.
Basically, I'm executing a process (a .NET process) and passing it command line arguments, it is executed successfully by CreateProcess() but CreateProcess() isn't passing the command line arguments
What am I doing wrong here??
int main(int argc, char* argv[])
{
PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter
STARTUPINFO StartupInfo; //This is an [in] parameter
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field
LPTSTR cmdArgs = "name#example.com";
if(CreateProcess("D:\\email\\smtp.exe", cmdArgs,
NULL,NULL,FALSE,0,NULL,
NULL,&StartupInfo,&ProcessInfo))
{
WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
printf("Yohoo!");
}
else
{
printf("The process could not be started...");
}
return 0;
}
EDIT: Hey one more thing, if I pass my cmdArgs like this:
// a space as the first character
LPTSTR cmdArgs = " name#example.com";
Then I get the error, then CreateProcess returns TRUE but my target process isn't executed.
Object reference not set to an instance of an object
You should specify also the module name in parameters: LPTSTR cmdArgs = "App name#example.com";
It should be the whole command line (including argv[0]).
If the first parameter to CreateProcess() is non-NULL, it will use that to locate the image to launch.
If it is NULL, it will parser the 2nd argument to try to get the executable to launch from the 1st token.
In either case, the C runtime will use the second argument to populate the argv array. So the first token from that parameter shows up in argv[0].
You probably want something like the following (I've change the smtp.exe program to echoargs.exe - a simple utility I have to help figure out just this kind of issue):
int main(int argc, char* argv[])
{
PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter
STARTUPINFO StartupInfo; //This is an [in] parameter
char cmdArgs[] = "echoargs.exe name#example.com";
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field
if(CreateProcess("C:\\util\\echoargs.exe", cmdArgs,
NULL,NULL,FALSE,0,NULL,
NULL,&StartupInfo,&ProcessInfo))
{
WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
printf("Yohoo!");
}
else
{
printf("The process could not be started...");
}
return 0;
}
Here's the output I get from that program:
echoargs.exe name#example.com
[0]: echoargs.exe
[1]: name#example.com
Yohoo!
It doesn't look like you are using CreateProcess correctly, see http://msdn.microsoft.com/en-us/library/ms682425%28VS.85%29.aspx.
The command line to be executed. The maximum length of this string is 32,768 characters, including the Unicode terminating null character. If lpApplicationName is NULL, the module name portion of lpCommandLine is limited to MAX_PATH characters.
The lpCommandLine parameter can be NULL. In that case, the function uses the string pointed to by lpApplicationName as the command line.
If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string pointed to by lpApplicationName specifies the module to execute, and the null-terminated string pointed to by lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.
So in your case, you need this as the command argument and should probably pass a NULL for the first parameter to get the behaviour your want.
// NOTE THE Null-Terminated string too!
LPTSTR cmdArgs = "D:\\email\\smtp.exe name#example.com\0";
Below is a cut down version of the code used by the Zeus IDE to run external processes:
bool createProcess(const char *pszTitle, const char *pszCommand)
{
STARTUPINFO StartInfo;
memset(&StartInfo, 0, sizeof(StartInfo));
StartInfo.cb = sizeof(StartInfo);
StartInfo.lpTitle = (pszTitle) ? (char *)pszTitle : (char *)pszCommand;
StartInfo.wShowWindow = SW_NORMAL;
StartInfo.dwFlags |= STARTF_USESHOWWINDOW;
if (CreateProcess(0, (char *)pszCommand,
0, 0, TRUE,
CREATE_NEW_PROCESS_GROUP, 0, 0,
&StartInfo, &ProcessInfo))
{
lErrorCode = 0;
}
else
{
lErrorCode = GetLastError();
}
return (lErrorCode == 0);
}
The pszCommand would be the full executable path and file name and arguments so for example:
pszCommand = "D:\\email\\smtp.exe name#example.com";
From what I can tell, the only real difference between the two is that in the Zeus example, the dwCreationFlags argument is set to the CREATE_NEW_PROCESS_GROUP value.
You can add a space as first character of the cmdArgs string:
LPTSTR cmdArgs = " name#example.com";
Apparently Windows appends the 2nd argument string to the application name represented by the first argument, and the result is passed as command line arguments to the executable. So adding a space will properly separate the arguments.
Try this:
LPTSTR cmdArgs = "name#example.com";
CString szcmdline("D:\\email\\smtp.exe");
szcmdline += _T(" ") + cmdArgs ;
//Leave first param empty and pass path + argms in
if(CreateProcess(NULL, szcmdline, second
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.
Therefore you can try using LPTSTR cmdArgs = _tcsdup("name#example.com").
Another problem is: how does the target process reads the arguments? using argv[0] as application name? Then you shoud append the application name as the first parameter too.
You are not allocating memory for your string.
Instead of:
LPTSTR cmdArgs = "name#example.com";
try:
TCHAR cmdArgs[] = "name#example.com";
Edit:
then call:
CreateProcess("D:\\email\\smtp.exe", &cmdArgs[0], ...
This will create a local array on the stack and then pass a pointer to that array.