why does ::CreateProcess(path,cmd,...) fail with error "File not found"? - c++

I am trying to have a C++ program call an already made C# program to run in the background.
STARTUPINFO info = {sizeof(info)};
PROCESS_INFORMATION processinfo;
DWORD error1 = GetLastError();
bool x = ::CreateProcess((LPCWSTR)"C:\Convert_Shrink.exe", GetCommandLine(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
DWORD error = GetLastError();
error1 is 0 before CreateProcess
error is 2 after CreateProcess
error 2:
ERROR_FILE_NOT_FOUND 2 (0x2) The system cannot find the file specified.
I've changed it to C:\ \ incase they were checking for escape sequences but I still get error 2 and I'm not sure why.

You can:
Use CreateProcessA to match your ANSI file path:
bool x = ::CreateProcessA("C:\\Convert_Shrink.exe", GetCommandLineA(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
* Provide a file path which matches the string format required by your Unicode settings:
bool x = ::CreateProcess(_T("C:\\Convert_Shrink.exe"), GetCommandLine(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
or
Use CreateProcessW so you can pass a Unicode filepath (supports extended characters):
bool x = ::CreateProcessW(L"C\\\Convert_Shrink.exe", GetCommandLineW(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
(as #dolphy noted, the argument has to be a writable string)
Provide a file path which matches the string format required by your Unicode settings:
#if UNICODE
std::wstring exename =
#else
const char* exename =
#endif
_T("C:\\Convert_Shrink.exe");
bool x = ::CreateProcess(&exename[0], GetCommandLine(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
or
Use CreateProcessW so you can pass a Unicode filepath (supports extended characters):
wchar_t exename[] = L"C:\\Convert_Shrink.exe";
bool x = ::CreateProcessW(exename, GetCommandLineW(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);

Just for the record. CreateProcessAsUser calls SearchPath internally. SearchPath uses the File System Redirector https://msdn.microsoft.com/en-us/library/windows/desktop/aa384187%28v=vs.85%29.aspx
So, if you are running a 32 bit app under WOW64 and you ask for a process using an exe in system32 dir e.g. "c:\windows\system32\myapp.exe", CreateProcessAsUser will look in syswow64 instead e.g."c:\windows\syswow64\myapp.exe". If your exe is not there you'll get a "file not found error".

I just looked up GetCommandLine(), and MSDN states that it gets the command line for the current process. MSDN entry for CreateProcess() states that the second argument is the command line command that you want to be executed, if I'm reading it correctly. So you are essentially telling CreateProcess() to run another instance of the C++ program, not the C# program.
(edit)
Actually, upon closer inspection, the CreateProcess() documentation does not seem to clearly explain what will happen if you supply both the first and second arguments. It says that the first specifies the module and the second specifies the command line. What's the diff?
Sorry for the inconclusive answer, I would convert this answer into a couple of comments on your question if I could.

Have you tried casting the string to LPCTSTR instead:
bool x = ::CreateProcess((LPCTSTR)"C:\Convert_Shrink.exe", GetCommandLine(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
From Microsoft:
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.

Related

Define in C++ new environment variable and use it in bat file

It is very simple, I want to define a new environment variable in C++ using SetEnvironmentVariable, and then call a bat file that use it. This is my c++ code;
PROCESS_INFORMATION processInformation = { 0 };
STARTUPINFO startupInfo = { 0 };
SetEnvironmentVariable("GSDebbugingDir", "Hello");
BOOL result = CreateProcess(NULL,
"dummy.bat"),
NULL,
NULL,
FALSE,
CREATE_NO_WINDOW,
NULL,
NULL,
&startupInfo,
&processInformation);
And my dummy.bat file is :
#echo off
echo GSDebbugingDir = %GSDebbugingDir%
if defined GSDebbugingDir mkdir c:\temp\dummy
pause
But this didn't out the value of GSDebbugingDir variable. What is the problem?
CreateProcess documentation says:
To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file.
Which makes sense because a .bat file by itself is not an executable module by itself.
But, contrary to what the doc says the following code worked for me with UseApplicationName NOT #defined, i.e. with "cmd.exe" placed into commandLine
Also, to see the output of the bat file you probably want to remove that CREATE_NO_WINDOW flag
//#define UseApplicationName
#ifdef UseApplicationName
TCHAR commandLine[] = _T(" /c dummy.bat");
#else
TCHAR commandLine[] = _T("cmd.exe /c dummy.bat");
#endif
BOOL result = CreateProcess(
#ifdef UseApplicationName
_T("cmd.exe"),
#else
NULL,
#endif
commandLine,
NULL,
NULL,
FALSE,
0, //CREATE_NO_WINDOW,
NULL,
NULL,
&startupInfo,
&processInformation);
BTW pay attention that
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).

Executing rundll32.exe with CreateProcess

I've created a DLL and would like to execute one of the functions using the rundll32.exe command on windows.
Using rundll32.exe, it runs correctly from the command line; however, I'd like to call it (rundll32.exe) from a separate program. I cannot directly call the function from my code due to 32/64 bit compatibility issues in the underlying libraries I'm using (Easyhook).
Below is what I'm using in an attempt to run the dll function:
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi));
LPCTSTR application = "C:\\Windows\\system32\\rundll32.exe";
LPTSTR cmd = "C:\\Projects\\Test\\mydll.dll,MyFunc";
BOOL cpRes = CreateProcess(application,
cmd,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi);
if(cpRes == 0) {
cout << "ERROR\n";
cout << GetLastError() << endl;
} else {
cout << "DLL Launched!" << endl;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
The output to my console is always DLL Launched; however, I do not see the effects of my DLL actually being called (currently stubbed out in such a way that the command writes to a file).
If I swap out the application with something such as C:\\Windows\\system32\\notepad.exe, the program successfully runs.
For completion, here's the body of MyFunc:
ofstream file;
file.open("C:\\Projects\\Test\\test.txt");
file << "I wrote to a file!";
file.close();
Is there any reason CreateProcess cannot be used with rundll32? While reading over this I found several warnings about LoadLibrary() and DLLMain but it doesn't seem like they're relevant to this.
More Clarification:
This is currently a 32-bit application (allegedly) launching the 32-bit rundll32.exe (Logic will be added later to call the 32 or 64 bit version).
My dll is as follows:
extern "C" __declspec(dllexport) void CALLBACK MyFunc(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);
void CALLBACK MyFunc(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) { ... }
Which also has a .def file with:
EXPORTS
MyFunc
Running
C:\Windows\system32\rundll32.exe C:\Projects\Test\mydll.dll,MyFunc
produces the expected results.
Update
Setting application to NULL and including the rundll32.exe in cmd as mentioned in the comments seems to work.
Relevant Docs:
CreateProcess
RunDll32.exe
Per the CreateProcess() documentation:
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.
You are not repeating rundll32.exe as the first command-line token.
So, if you continue using the lpApplicationName parameter, then change this:
LPCTSTR application = "C:\\Windows\\system32\\rundll32.exe";
LPTSTR cmd = "C:\\Projects\\Test\\mydll.dll,MyFunc";
To this instead:
LPCTSTR application = TEXT("C:\\Windows\\system32\\rundll32.exe");
LPTSTR cmd = TEXT("C:\\Windows\\system32\\rundll32.exe C:\\Projects\\Test\\mydll.dll,MyFunc");
Note that you are currently compiling for ANSI/MBCS (by virtue of the fact that you are passing narrow strings to CreateProcess()). If you ever update the project to compile for Unicode, use this instead:
TCHAR cmd[] = TEXT("C:\\Windows\\system32\\rundll32.exe C:\\Projects\\Test\\mydll.dll,MyFunc");
This is because the documentation states:
lpCommandLine [in, out, optional]
...
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.
You might consider changing cmd into a TCHAR[] array anyway, even in ANSI/MBCS, so you can do something like this:
LPCTSTR application = TEXT("C:\\Windows\\system32\\rundll32.exe");
TCHAR cmd[(MAX_PATH*2)+10];
wsprintf(cmd, TEXT("%s %s,%s"), application, TEXT("C:\\Projects\\Test\\mydll.dll"), TEXT("MyFunc"));
Either way, by passing the module filename as the first token in the lpCommandLine parameter, you can then set the lpApplicationName parameter to NULL:
The lpApplicationName parameter can be NULL. In that case, the module name must be the first white space–delimited token in the lpCommandLine string.
Let CreateProcess() setup the correct command-line to pass to rundll32.exe for you:
TCHAR cmd[] = TEXT("C:\\Windows\\system32\\rundll32.exe C:\\Projects\\Test\\mydll.dll,MyFunc");
BOOL cpRes = CreateProcess(NULL, cmd, ...);

using the CreateProcess function

I'm trying to create something similar to a cmd with Microsoft Visual Studio Express 2013 for Windows Desktop in c++ and one of my function should start a process like open skype by typing "skype.exe". I searched in the internet and found the CreateProcess function that should do the work for me. when I created a function that receives a class value that I created called Line (the name of the class but it doesn't really metter) and used the CreateProcess function in the way that is shown bellow I have to type in my cmd "start skype.exe" but I want it to work like in the regular cmd by writing only "skype.exe", how can I do it?
(the l.parameter is just a string that contains the word skype)
void execute(Line l){
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
LPSTR s = const_cast<char *>(l.parameter.c_str());
if (!CreateProcess(NULL, s, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
printf("CreateProcess failed (%d).\n", GetLastError());
return;
}
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);}
First thing is that:
LPSTR s = const_cast<char *>(l.parameter.c_str());
is bad idea, CreateFile accepts for lpCommandLine non const buffer for a reason - it might modify it:
The system adds a terminating null character to the command-line string to separate the file name from the arguments. This divides the original string into two strings for internal processing.
so you should pass an array, for example:
TCHAR szCmd[MAX_PATH] = {0};
then to your question, if "start skype.exe" works for you and you want to enter only skype.exe at the command line - then why not concatenate strings? for example:
_tcscat(szCmd, _T("start "));
_tcscat(szCmd, parameter.c_str());
and pass szCmd to CreateProcess
the question is whether you use UNICODE build, if yes then make sure parameter is std::wstring, otherwise if you use non-UNICODE build (and it looks like thats true) then std::string is fine.
start is not an executable, it is a feature of cmd.exe, so to invoke start skype.exe via CreateProcess(), you would have to specify cmd.exe as the command and /C start skype.exe as its parameter.
Line l;
line.parameter = "cmd.exe /C start skype.exe";
execute(l);
But that is overkill in this situation, as start is not actually needed, despite what you claim. It is perfectly valid and preferable to invoke skype.exe directly as the command.
However, you have to provide the full path to skype.exe (same if you were to invoke start), otherwise CreateProcess() won't be able to find it, as Skype does not register its .exe file path in the App Paths key of the Registry, or the path to its Phone subfolder (where skype.exe resides) on the system's %PATH% environment variable.
For example:
Line l;
line.parameter = "C:\\Program Files (x86)\\Skype\\Phone\\Skype.exe";
execute(l);
Fortunately, Skype does store the full path to skype.exe in the Registry, specifically in the following key:
HKEY_CURRENT_USER\Software\Skype\Phone
It is stored in a REG_SZ value named "SkypePath".
std::string GetSkypePath()
{
std::string sPath;
HKEY hKey;
if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Skype\\Phone", 0, KEY_QUERY_VALUE, &hKey) == 0)
{
char szPath[MAX_PATH+1] = {0};
DWORD dwPathLen = MAX_PATH;
if (RegQueryValueExA(hKey, "SkypePath", NULL, NULL, (LPBYTE)szPath, &dwPathLen) == 0)
sPath = szPath;
RegCloseKey(hKey);
}
return sPath;
}

pass filename from existing Process to new Process MFC c++

I'm trying to figure out how to pass filename from within an existing executable to a newly generated executable of same type & then the new exe load said file name. Following is something I'm working on but I'm bit lost really.
CString cstrExePathLoc;
GetModuleFileName(NULL, cstrExePathLoc.GetBuffer(MAX_PATH), MAX_PATH);
wchar_t szCommandLine[1024] = _T("C:\\Users\\Home\\Desktop\\testfile.tmp");
PROCESS_INFORMATION processInfo;
STARTUPINFO startupInfo;
::ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
CreateProcess(
cstrExePathLoc, szCommandLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
&startupInfo, &processInfo
);
EDIT: this still doesn't open the file. A new ExeApp is started but no file is loaded. No errors generated at all.
I've searched net but no examples I've come across clearly explain how to do this. Any help would be appreciated. Thanks.
EDIT: simple solution here that's worked thanks to Robson Filho Colodeti below.
CString cstrExeFilePathAndFilePath2Open = cstrExePathLoc;
cstrExeFilePathAndFilePath2Open += L" \"";
cstrExeFilePathAndFilePath2Open += cstrFilePath2Open;
cstrExeFilePathAndFilePath2Open += L"\"";
CreateProcess(csExePath, cstrExeFilePathAndFilePath2Open.GetBuffer(0), NULL, NULL, TRUE, NULL, NULL, NULL, &sui, &pi);
Opening the other program
Using CreateProcess
you are in the right way, you can use the CreateProcess method.
BOOL fSuccess;
CString csDir = L"c:\your\working\directory\";
CString csParameters = L"parameter1 parameter2 parameter3 /parameter4=value";
CString csCommand = L"c:\folder\of\the\executable\executable.exe";
csCommand+= L" ";
csCommand+= csParameters;
// Create the child process.
fSuccess = CreateProcess(NULL, csCommand.GetBuffer(0), NULL, NULL, TRUE, 0, NULL,
csDir, &startupInfo, &processInfo);
Using ShellExecute
an easier way is to use the ShellExecute method because the create process method is a more "advanced" way to call a process since it gives you a lot of possibilities to control the results etc...
Reading the parameters inside the other program
then you will have to read these parameters from the other executable: check this thread

LoadLibraryW doesn't work while LoadLibraryA does the job

I have written some sample program and DLL to learn the concept of DLL injection.
My injection code to inject the DLL to the sample program is as follows (error handling omitted):
std::wstring dll(L"D:\\Path\\to\\my\\DLL.dll");
LPTHREAD_START_ROUTINE pLoadLibraryW =
(LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryW");
int bytesNeeded = WideCharToMultiByte(CP_UTF8, 0, dll.c_str(), dll.length(),
NULL, 0, NULL, NULL);
std::vector<byte> dllName(bytesNeeded);
WideCharToMultiByte(CP_UTF8, 0, dll.c_str(), dll.length(),
(LPSTR)&dllName[0], bytesNeeded, NULL, NULL);
// Memory is a class written by me to simplify memory processes.
// Constructor takes desired permissions.
Memory mem (pid, false, true, false, true, false, false, false,
false, false, true, true, true, false);
// Ensures deletion of the allocated range.
// true / true / false = read and write access, no execute permissions
std::tr1::shared_ptr<void> allocated =
mem.AllocateBytes(dllName.size(), true, true, false);
mem.WriteBytes((unsigned int)allocated.get(), dllName);
mem.CreateThread(pLoadLibraryW, allocated.get());
Memory::CreateThread is as follows:
void Memory::CreateThread(LPTHREAD_START_ROUTINE address, LPVOID parameter) const {
std::tr1::shared_ptr<void> hThread(CreateRemoteThread(m_hProcess.get(),
NULL, 0, address, parameter, 0, NULL), CloseHandle);
if (hThread.get() == NULL) {
throw std::runtime_error("Memory::CreateThread: CreateRemoteThread failed");
}
DWORD returned = WaitForSingleObject(hThread.get(), INFINITE);
if (returned != WAIT_OBJECT_0) {
throw std::runtime_error("Memory::CreateThread: The remote thread did not complete properly");
}
}
The problem is, that the module isn't loaded. However, when I change the second line to
LPTHREAD_START_ROUTINE pLoadLibraryW =
(LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryA");
it works (since the test dll has no unicode characters in it's name).
How to make it work with LoadLibraryW?
HMODULE WINAPI LoadLibrary(
__in LPCTSTR lpFileName
);
It takes a TCHAR -- so the argument for LoadLibraryW has to be a wide string; the code above passes the multi-byte form of the argument, which is the form that LoadLibraryA wants.
I'm not sure why you are creating a thread and passing it the address of the LoadLibraryW function. Wouldn't it be easier and safer to call LoadLibraryW directly?
Either way, you certainly don't need to make any WideCharToMultiByte calls. LoadLibraryW expects a wide character module name.
Is there any reason why you can't just do this?
HMODULE hLibHandle = LoadLibraryW( L"D:\\Path\\to\\my\\DLL.dll" );