e. boyd
e. boyd

Reputation: 29

how to use the CreateProcess function in Visual Studios

I've seen some examples of the CreateProcess() function on Stack Overflow, but I could not get the examples to work. I'm using Visual Studio but programming the straight C. Here is my latest attempt. I can get it to compile and execute, but I can't get it to start the application:

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 ; 

hr = CreateProcess((NULL, (LPTSTR) "C:\\Windows\\System32\\notepad.exe"), NULL,  
                    NULL,NULL,FALSE,0,NULL,
                    NULL,&StartupInfo,&ProcessInfo);

                int error_1 = 0;
                if(!hr)
                {
                    error_1 = GetLastError();
                }

When I read 'error_1', it returns a '2', which based upon the System Error Codes indicates that "The system cannot find the file specified.". However, I'm sure the path is correct as I can use the path from the command line to start "Notepad". I can't see what I'm doing wrong.

Upvotes: 1

Views: 3157

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596612

You need to get rid of the extra parenthesis around the first 2 parameter values you are passing.

But, more importantly, you need to get rid of the LPTSTR type-cast. If your project is configured to compile for Unicode, CreateProcess() will map to CreateProcessW(), and LPTSTR will map to wchar_t*, thus you will be type-casting a narrow const char[] literal to a wchar_t* pointer and end up passing garbage to CreateProcessW(), which could easily account for the error you are seeing. The correct way to create a TCHAR[] string literal is to use the TEXT() macro instead of a typecast (see Working with Strings on MSDN for more details):

hr = CreateProcess(NULL, TEXT("C:\\Windows\\System32\\notepad.exe"), NULL,  
                    NULL, NULL, FALSE, 0, NULL,
                    NULL, &StartupInfo, &ProcessInfo);

However, pay attention to the CreateProcess() documentation:

lpCommandLine [in, out, optional]
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 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 can do the following to avoid that:

TCHAR szCmdLine[] = TEXT("C:\\Windows\\System32\\notepad.exe");
hr = CreateProcess(NULL, szCmdLine, NULL,  
                    NULL, NULL, FALSE, 0, NULL,
                    NULL, &StartupInfo, &ProcessInfo);

Or:

WCHAR szCmdLine[] = L"C:\\Windows\\System32\\notepad.exe";
hr = CreateProcessW(NULL, szCmdLine, NULL,  
                    NULL, NULL, FALSE, 0, NULL,
                    NULL, &StartupInfo, &ProcessInfo);

Otherwise, use the ANSI version of CreateProcess() directly, which you can safely pass a string literal to:

hr = CreateProcessA(NULL, "C:\\Windows\\System32\\notepad.exe", NULL,  
                    NULL, NULL, FALSE, 0, NULL,
                    NULL, &StartupInfo, &ProcessInfo);

On a side note, you don't need to specify the full path to notepad.exe, as Windows knows where it is located. You can use "notepad.exe" by itself.

However, if you do specify the full path, you have to take into account the bitness of your app. If your app is compiled for 32-bit, and is run on a 64-bit system, C:\Windows\System32\notepad.exe will redirect to C:\Windows\SysWOW64\notepad.exe, which is the 32-bit version of Notepad. If you want to run the 64-bit version of Notepad, you would have to use C:\Windows\Sysnative\notepad.exe instead, per the WOW64 documentation:

File System Redirector

The %windir%\System32 directory is reserved for 64-bit applications. Most DLL file names were not changed when 64-bit versions of the DLLs were created, so 32-bit versions of the DLLs are stored in a different directory. WOW64 hides this difference by using a file system redirector.

In most cases, whenever a 32-bit application attempts to access %windir%\System32, the access is redirected to %windir%\SysWOW64. Access to %windir%\lastgood\system32 is redirected to %windir%\lastgood\SysWOW64. Access to %windir%\regedit.exe is redirected to %windir%\SysWOW64\regedit.exe.

...

32-bit applications can access the native system directory by substituting %windir%\Sysnative for %windir%\System32. WOW64 recognizes Sysnative as a special alias used to indicate that the file system should not redirect the access. This mechanism is flexible and easy to use, therefore, it is the recommended mechanism to bypass file system redirection. Note that 64-bit applications cannot use the Sysnative alias as it is a virtual directory not a real one.

Sysnative only works in a 32-bit app running inside of the WOW64 emulator. you can use System32 in a 32-bit app running on a 32-bit system, and a 64-bit app running on a 64-bit system:

TCHAR szCmdLine[MAX_PATH];

#ifndef _WIN64
BOOL bIsWow64Process = FALSE;
if (IsWow64Process(GetCurrentProcess(), &bIsWow64Process) && bIsWow64Process)
    lstrcpy(szCmdLine, TEXT("C:\\Windows\\Sysnative\\notepad.exe"));
else
#endif
    lstrcpy(szCmdLine, TEXT("C:\\Windows\\System32\\notepad.exe"));

hr = CreateProcess(NULL, szCmdLine, NULL,  
                    NULL, NULL, FALSE, 0, NULL,
                    NULL, &StartupInfo, &ProcessInfo);

However, you should take into account that not everyone installs Windows in the C:\Windows folder, so you should ask Windows where the System folder is actually located:

TCHAR szSysDir[MAX_PATH];

#ifndef _WIN64
BOOL bIsWow64Process = FALSE;
if (IsWow64Process(GetCurrentProcess(), &bIsWow64Process) && bIsWow64Process)
{
    TCHAR szWinDir[MAX_PATH];
    GetWindowsDirectory(szWinDir, MAX_PATH);
    PathCombine(szSysDir, szWinDir, TEXT("Sysnative");
}
else
#endif
    GetSystemDirectory(szSysDir, MAX_PATH);

TCHAR szCmdLine[MAX_PATH];
PathCombine(szCmdLine, szSysDir, TEXT("notepad.exe");

hr = CreateProcess(NULL, szCmdLine, NULL,  
                    NULL, NULL, FALSE, 0, NULL,
                    NULL, &StartupInfo, &ProcessInfo);

Upvotes: 1

Related Questions