问题
TL;DR:
CreateProcess(?, ?, ?, ...)
for:
- Pass current process params (i.e. "batchfile" %*)
- correctly connect stdin and stdout
- creation flags?
I have the following problem:
- I need to launch a given 3rd party executable with a custom environment and custom parameters. (Both semi-fixed)
- I cannot use a batch file, because the (again, 3rd party) side invoking the module directly calls
CreateProcess
- I need to pass on any additional paramers passed
So, what I'd like to do is create a very simple executable launcher that would be the equivalent of a batch file like:
set PATH=...
set WHATEVER=...
...\3rd-pty-tool.exe -switch1 -switch2 %*
exit /B %ERRORLEVEL%
And I certainly don't want to mess with any bat2exe converter stuff - just too ugly when I have Visual Studio around anyway.
Running another executable via CreateProcess is trivial in principle:
STARTUPINFO info={sizeof(info)};
PROCESS_INFORMATION processInfo;
if (CreateProcess(?, ?, ?, ?, ?, ?, ?, ?, &info, &processInfo))
{
WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
Setting up the environment for the child process via _putenv et al. is also pretty easy.
What is not trivial to me is however what to pass on to CreateProcess:
BOOL WINAPI CreateProcess(
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);
- How to I get at the
%*
equivalent for the current Win32 process? - Pass only
lpApplicationName
, onlylpCommandLine
or both? - What to do about handle inheritance and creation flags?
- How to I correctly forward / return stdin and stdout?
Not a dupe: CreateProcess to execute Windows command
回答1:
Should be reasonably straightforward.
BOOL WINAPI CreateProcess(
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);
Let's take it in order.
lpApplicationName
- if you have the full path to the executable you want to run, put it here. That ensures that you get the executable you were expecting, even if another executable with the same name is on the PATH.lpCommandLine
- the first element is the executable name. If you've specifiedlpApplicationName
this doesn't need to be fully qualified, or even be the executable's actual name, but it does need to be present. This must be a writable buffer, it cannot be a constant string.
If your extra arguments can go at the end of the command line, this is easy:
wchar_t buffer[1024];
wcscpy_s(buffer, _countof(buffer), GetCommandLine());
wcscat_s(buffer, _countof(buffer), L" -switch1 -switch2");
Otherwise, you'll need to parse the command line to find the right place to insert the arguments, something like this:
while (*lpCmdLine == L' ') lpCmdLine++;
while (ch = *lpCmdLine)
{
if (ch == L'"') for (lpCmdLine++; ch = *lpCmdLine; lpCmdLine++) if (ch == L'"') break;
if (ch == L' ') break;
lpCmdLine++;
}
while (*lpCmdLine == L' ') lpCmdLine++;
lpProcessAttributes
andlpThreadAttributes
-NULL
.bInheritHandles
-TRUE
, since you want the child to inherit the standard handles.dwCreationFlags
- none needed in your scenario, so0
.lpEnvironment
-NULL
to pass the current environment. In some situations you'd want to explicitly construct a new environment, or a modified copy of your environment, but since your process exists only to launch the child that shouldn't be necessary.lpCurrentDirectory
-NULL
to inherit your current directory.lpStartupInfo
- callGetStartupInfo
to fill this out with the same settings as the current process, or just leave it empty as in the code you posted.lpProcessInformation
- this is an output parameter, used as shown in your code. In your scenario, where one application is standing in for another, you might want to keep the process handle and use it to wait for the child process to exit before exiting yourself. (This isn't necessary if you know that your parent won't get confused if you exit before your child does.)
You don't need to do anything special about the standard handles, apart from ensuring that bInheritHandles
is set. The default is for the child to keep the same standard handles as the parent.
来源:https://stackoverflow.com/questions/37528300/createprocess-usage-for-executable-wrapper-for-windows