Executing a program on PocketPC Today Items doesn't work - c++

I have the following problem. I am using a DLL to add a new menu item for the Today Item List on a PocketPC 2002. I have tested the written DLL on a Windows Mobile 6.5.3 an it works on there.
On the PocketPC the path contains a space and I guess that I don't escape the string right. Consider the following snippet where the problem occurs:
char commandline[100];
strcpy(commandline, "\\SDMMC Disk\\Test\\Test.exe");
STARTUPINFO si = { sizeof(si) };
int len;
int slength = (int)strlen(commandline) + 1;
len = MultiByteToWideChar(CP_ACP, 0, commandline, slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, commandline, slength, buf, len);
PROCESS_INFORMATION pinfo;
::CreateProcess(buf, buf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pinfo);
Now I was trying to escape the path to the executable like this:
strcpy(commandline, "\"\\SDMMC Disk\\Test\\Test.exe"");
But unfortunately this does not work. Did I escape the string in a wrong way?
I'd appreciate any help, thank you.

Related

why isn't CreateProcessW() performing the Command provided?

To present the minimal reproducible code I wrote a code to delete a file from a given location using CreateProcessW(). The file does not get deleted. Some help would be really useful in knowing why this isn't working.
dprintf(("Error %d", GetLastError()));
STARTUPINFO si = { sizeof(STARTUPINFO), 0 };
si.cb = sizeof(si);
PROCESS_INFORMATION pi = { 0 };
LPWSTR AppName = L"C:\\Windows\\System32\\cmd.exe";
string bstr = "C:\\Windows\\System32\\cmd.exe /C del"+trans_loc+"a.rtf";
LPWSTR Command = new WCHAR[bstr.length()];
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, NULL, 0);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, Command, wchars_num);
DWORD res = CreateProcessW(AppName, Command, 0, 0, 0, DETACHED_PROCESS, 0, 0, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
define TRANSCRIPT_LOCATION "C:\Users\Administrator\Desktop\" this is the location of the file to be deleted
GetLastError() keeps returning 50(ERROR_NOT_SUPPORTED) and the value of res = 1
My first thought is that
LPWSTR Command = new WCHAR[bstr.length()];
is not right. Perhaps
LPWSTR Command = new WCHAR[bstr.length() + 1];
will work. A better alternative is to use wchars_num for allocating memory.
instead of
LPWSTR Command = new WCHAR[bstr.length()];
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, NULL, 0);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, Command, wchars_num);
DWORD res = CreateProcessW(AppName, Command, 0, 0, 0, DETACHED_PROCESS, 0, 0, &si, &pi);
use
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, NULL, 0);
LPWSTR Command = new WCHAR[wchars_num];
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, Command, wchars_num);
DWORD res = CreateProcessW(AppName, Command, 0, 0, 0, DETACHED_PROCESS, 0, 0, &si, &pi);
A second issue is that perhaps you missed a space character when composing the del command.
string bstr = "C:\\Windows\\System32\\cmd.exe /C del " + trans_loc + "a.rtf";
// ^^
I see a number of problems with your code:
LPWSTR AppName = L"C:\\Windows\\System32\\cmd.exe"; does not compile in C++11 and later. You need to (and should) use LPCWSTR instead, since a string literal is const data, and LPCWSTR is a pointer to const WCHAR data, but LPWSTR is a pointer to non-const WCHAR data.
In string bstr = "C:\\Windows\\System32\\cmd.exe /C del"+trans_loc+"a.rtf";, you are missing a required space character between the del command and the filename to delete.
In LPWSTR Command = new WCHAR[bstr.length()];, you are not allocating enough space for a null terminator. Also, you should not be using bstr.length() for the converted length anyway, because there is no guarantee that the converted string will not be larger than the original string. You should call MultiByteToWideChar() one time with a NULL output buffer to calculate the actual converted length (which you ARE doing), THEN allocate the memory (which you are NOT doing - you are allocating too soon!), THEN call MultiByteToWideChar() again to do the actual conversion.
You are leaking the allocated memory (you are not calling delete[] Command;). I would suggest using std::wstring or std::vector<WCHAR> instead of new WCHAR[].
You say that res is being set to 1, which means CreateProcessW() is actually successful in running cmd.exe (now, whether cmd.exe is successful in executing your command is a different matter - use GetExitCodeProcess() to find that out), and thus the return value of GetLastError() is meaningless! It is certainly meaningful to call GetLastError() before calling CreateProcessW()
You are calling WaitForSingleObject() regardless of whether CreateProcessW() succeeds or fails.
Try this instead:
STARTUPINFO si = {};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {};
std::string bstr = "C:\\Windows\\System32\\cmd.exe /C del \"" + trans_loc + "a.rtf\"";
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), NULL, 0);
if (wchars_num == 0)
{
dprintf(("MultiByteToWideChar Error %d", GetLastError()));
}
else
{
std::vector<WCHAR> Command(wchars_num + 1);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), Command.data(), wchars_num);
if (!CreateProcessW(nullptr, Command.data(), nullptr, nullptr, FALSE, DETACHED_PROCESS, nullptr, nullptr, &si, &pi))
{
dprintf(("CreateProcessW Error %d", GetLastError()));
}
else
{
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwExitCode = 0;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
dprintf(("cmd.exe Exit Code %d", dwExitCode));
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
}
Or, if you are using Windows 10 build 17035 or later and have enabled the "Beta: Use Unicode UTF-8 for worldwide language support" option in your Windows settings (or, if trans_loc does not contain any non-ASCII, non-user-locale characters), then no MultiByteToWideChar() conversion is needed at all:
STARTUPINFO si = {};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {};
std::string Command = "C:\\Windows\\System32\\cmd.exe /C del \"" + trans_loc + "a.rtf\"";
if (!CreateProcessA(nullptr, const_cast<char*>(Command.c_str()), nullptr, nullptr, FALSE, DETACHED_PROCESS, nullptr, nullptr, &si, &pi))
{
dprintf(("CreateProcessA Error %d", GetLastError()));
}
else
{
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwExitCode = 0;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
dprintf(("cmd.exe Exit Code %d", dwExitCode));
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
That being said, a better option would be to simply use std::wstring instead of std::string to begin with:
STARTUPINFO si = {};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {};
// make sure trans_loc is std::wstring instead of std::string...
std::wstring bstr = L"C:\\Windows\\System32\\cmd.exe /C del \"" + trans_loc + L"a.rtf\"";
if (!CreateProcessW(nullptr, Command.data(), nullptr, nullptr, FALSE, DETACHED_PROCESS, nullptr, nullptr, &si, &pi))
{
dprintf(("CreateProcessW Error %d", GetLastError()));
}
else
{
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwExitCode = 0;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
dprintf(("cmd.exe Exit Code %d", dwExitCode));
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
Of course, the simplest solution would be to just not use cmd.exe / C del at all, but instead use DeleteFileW():
// make sure trans_loc is std::wstring instead of std::string...
std::wstring bstr = trans_loc + L"a.rtf";
if (!DeleteFileW(bstr.c_str()))
{
dprintf(("DeleteFileW Error %d", GetLastError()));
}
Or, if you insist on using a UTF-8 encoded std::string:
std::string bstr = trans_loc + "a.rtf";
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), NULL, 0);
if (wchars_num == 0)
{
dprintf(("MultiByteToWideChar Error %d", GetLastError()));
}
else
{
std::vector<WCHAR> wstr(wchars_num + 1);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), wstr.data(), wchars_num);
if (!DeleteFileW(wstr.c_str()))
{
dprintf(("DeleteFileW Error %d", GetLastError()));
}
}
Or, if you are using Windows 10 with UTF-8 support enabled (or, if trans_loc does not contain any non-ASCII, non-user-locale characters):
std::string bstr = trans_loc + "a.rtf";
if (!DeleteFileA(bstr.c_str()))
{
dprintf(("DeleteFileA Error %d", GetLastError()));
}

BSTR conversion to UTF-8

I'm working with UIAutomation and I'm struggling with the localized BSTRs. I'm in Germany, so there are some special characters that are represented funny in the BSTRs. I'm logging the information and need to have them in UTF-8 to process later on.
I tried already every version of the answers that I could find regarding to WideCharToMultiByte, but that's just converting the funny character into an even funnier one. I'd really appreciate if anyone could tell me what I'm doing wrong, it's really bugging me.
So I tried both of the following versions and got both times this result (the upper one is the converted one, the lower the original one):
The first word should be "Schaltfläche" and the second "Fünf".
My tried code:
BSTR* origin;
_bstr_t originWrapper(*origin);
char* originChar = originWrapper;
size_t len = strlen(originChar) + 1;
int room = MultiByteToWideChar(CP_ACP, 0, originChar, -1, NULL, 0);
wchar_t* unicodeString = (wchar_t*)malloc((sizeof(wchar_t))*room);
MultiByteToWideChar(CP_ACP, 0, originChar, -1, unicodeString, room);
int size_needed = WideCharToMultiByte(CP_UTF8, 0, unicodeString, -1, NULL, 0, NULL, NULL);
char* utf8Char = (char*) malloc(size_needed);
WideCharToMultiByte(CP_UTF8, 0, unicodeString, -1, utf8Char, size_needed, NULL, NULL);
and
BSTR* origin;
_bstr_t originWrapper(*origin);
int size_needed = WideCharToMultiByte(CP_UTF8, 0, originWrapper, SysStringByteLen(*origin), NULL, 0, NULL, NULL);
std::string resultingString(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, *origin, SysStringByteLen(*origin), &resultingString[0], size_needed, NULL, NULL);
BSTR is a pointer to UTF-16 (WCHAR) character data, preceded by the string length. So, your roundtrip through narrow strings is misguided, you should straight use WideCharToMultiByte:
std::string BSTRtoUTF8(BSTR bstr) {
int len = SysStringLen(bstr);
// special case because a NULL BSTR is a valid zero-length BSTR,
// but regular string functions would balk on it
if(len == 0) return "";
int size_needed = WideCharToMultiByte(CP_UTF8, 0, bstr, len, NULL, 0, NULL, NULL);
std::string ret(size_needed, '\0');
WideCharToMultiByte(CP_UTF8, 0, bstr, len, ret.data(), ret.size(), NULL, NULL);
return ret;
}
To check the validity of the conversion don't output the result to the console, as it doesn't support UTF-8 output by default (it interprets narrow strings not even as in CP_ACP, but in CP_OEM, go figure). Instead, write the output to a file and check it with a reliable editor supporting UTF-8.

How can I embed a bat file in my project?

Ive been using the following code to start a bat script.
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
std::wstring env = GetEnvString();
env += L"myvar=boo";
env.push_back('\0'); // somewhat awkward way to embed a null-terminator
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi;
wchar_t cmdline[] = L"cmd.exe /C C:\\Users\\jrowler\\Desktop\\test\\startsimulator.bat";
if (!CreateProcess(NULL, cmdline, NULL, NULL, false, CREATE_UNICODE_ENVIRONMENT,
(LPVOID)env.c_str(), NULL, &si, &pi))
{
std::cout << GetLastError();
abort();
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
This actually works perfectly, but I was wondering if it is possible to take that bat file and somehow include it in my project? Eventually this project will be distributed to a few different people, and I would like it to be set up in such a way that it does not require the user to download a .bat seperately and make sure it stays in the correct location.
You can write the file out using WriteFile. Since you're using C++ you can eschew the <strsafe.h> function I used in my code (I'm used to C) and build an std::wstring containing the file path with standard string operations, and then use the c_str() method to pass the first argument to CreateFile.
char batContent[] = "#echo off\r\necho Hello World\r\n";
wchar_t temp[MAX_PATH], path[MAX_PATH];
GetEnvironmentVariableW(L"Temp", temp, MAX_PATH);
int len = strlen(batContent);
DWORD dwWritten;
StringCchPrintfW(path, MAX_PATH, L"%s\\filename.bat", temp);
HANDLE hFile = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
// handle error
}
WriteFile(hFile, batContent, len, &dwWritten, NULL);
CloseHandle(hFile);
// Call CreateProcess with your existing code

CreateProcess execute EXE

I have an application where the user uploads a file to the remote server, the same server to receive this file should run this application. I'm using the CreateProcess method. The problem is, the file directory is already defined in a std :: string, and I'm having difficulties to pass this directory as a parameter to the CreateProcess.
How do I that this directory can be passed to the CreateProcess without errors?
//the client remotely sends the directory where the file will be saved
socket_setup.SEND_BUFFER("\nRemote directory for upload: ");
char *dirUP_REMOTE = socket_setup.READ_BUFFER();
std::string DIRETORIO_UP = dirUP_REMOTE; // variable where it stores the remote directory
//after uploading this is validation for executing file
if (!strcmp(STRCMP_EXECUTE, EXECUTE_TIME_YES))
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
std::wstring wdirectory;
int slength = (int)directory.length() + 1;
int len = MultiByteToWideChar(CP_ACP, 0, directory.c_str(), slength, 0, 0);
wdirectory.resize(len);
MultiByteToWideChar(CP_ACP, 0, directory.c_str(), slength, &wdirectory[0], len);
if (!CreateProcess(NULL, wdirectory.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi));
}
There are two versions of CreateProcess: CreateProcessA and CreateProcessW (like most similar windows APIs). The right version is used depending on whether you have Unicode enabled.
Here you need to convert your std::string to a std::wstring first, because that CreateProcess is actually a CreateProcessW.
std::wstring wdirectory;
int slength = (int)directory.length() + 1;
int len = MultiByteToWideChar(CP_ACP, 0, directory.c_str(), slength, 0, 0);
wdirectory.resize(len);
MultiByteToWideChar(CP_ACP, 0, directory.c_str(), slength, &wdirectory[0], len);
//...
if (!CreateProcess(NULL,(LPWSTR)wdirectory.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi));
You could also try replacing CreateProcess by a manual call to CreateProcessA and passing the cstring like you tried to do in the question, but then you won't support wide characters :
if (!CreateProcessA(NULL, directory.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi));

How can I fix this code?

This code does not work as expected and I don't know what's wrong.
szTeam should change, but doesn't.
Could anyone explain this?
-----------------------------------------------------
WCHAR szTeam[MAX_PATH] = L"\u7F57\u5207\u8FBE\u5C14\u6D41\u6D6A";
char szMsg[MAX_PATH];
sprintf(szMsg , "%s" , WideStringToMultiByte(szTeam));
swprintf( szTeam , L"%s" , MultiByteToWideString(szMsg));
......
WCHAR* MultiByteToWideString(const char* szSrc)
{
int iSizeOfStr = MultiByteToWideChar(CP_ACP, 0, szSrc, -1, NULL, 0);
wchar_t* wszTgt = new wchar_t[iSizeOfStr];
if(!wszTgt)
return (NULL);
MultiByteToWideChar(CP_ACP, 0, szSrc, -1, wszTgt, iSizeOfStr);
return(wszTgt);
}
char* WideStringToMultiByte(const wchar_t* wszSrc)
{
int iSizeOfStr = WideCharToMultiByte(CP_UTF8, 0, wszSrc, -1, NULL, 0, NULL, NULL);
char* szTgt = new char[iSizeOfStr];
if(!szTgt)
return(NULL);
WideCharToMultiByte(CP_UTF8, 0, wszSrc, -1, szTgt, iSizeOfStr, NULL, NULL);
return szTgt;
}
-----------------------------------------------------
Erm, no szTeam does change. Into something unrecognizable, mojibake. You start out with "罗切达尔流浪" and convert it from its utf-16 encoding to utf-8. That works fine. The debugger won't show you anything recognizable because it neither knows nor cares that szMsg is encoded in utf-8.
You then go wrong though, you are converting that utf-8 string with CP_ACP. Which says that the string is encoded in the default system code page. It is not, it was encoded in utf-8.
Fix your problem with:
WCHAR* MultiByteToWideString(const char* szSrc)
{
int iSizeOfStr = MultiByteToWideChar(CP_UTF8, 0, szSrc, -1, NULL, 0);
// etc..
}
And now szTeam won't change because the string got properly converted back.