CreateProcess usage for executable wrapper for Windows?

断了今生、忘了曾经 提交于 2019-12-21 22:38:24

问题


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, only lpCommandLine 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 specified lpApplicationName 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 and lpThreadAttributes - NULL.

  • bInheritHandles - TRUE, since you want the child to inherit the standard handles.

  • dwCreationFlags - none needed in your scenario, so 0.

  • 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 - call GetStartupInfo 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!