问题描述
我有一个简单的 C++ 控制台应用程序,它启动 notepad.exe 并加载文件 D:\MyTextFile.txt,然后控制台应用程序退出,但记事本仍在运行。代码效果很好:
int _tmain(int argc,_TCHAR* argv[])
{
STARTUPINFO si;
ZeroMemory( &si,sizeof(si) );
si.cb = sizeof(si);
PROCESS_informatION pi;
ZeroMemory( &pi,sizeof(pi) );
WCHAR pCmd[] = {'n','o','t','e','p','a','d','.','x',' ','D',':','\\','M','y','T','F','i','l',0};
BOOL result = CreateProcess
(
_T("C:\\Windows\\System32\\notepad.exe"),// Module name
pCmd,// Command line (as modifiable array)
NULL,// Process handle not inheritable
NULL,// Thread handle not inheritable
FALSE,// Set bInheritHandles to FALSE
DETACHED_PROCESS,// Detach process
NULL,// Use parent's environment block
NULL,// Use parent's starting directory
&si,// Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_informatION structure (returned)
);
return (result) ? 0 : -1;
}
但是,如果我用 cmd 替换记事本,用 MyBatFile.bat 替换 MyTextFile.txt 那么它就不起作用了。 MyBatFile.bat 的内容:
C:\Windows\System32\notepad.exe D:\MyTextFile.txt
修改后的控制台应用程序:
int _tmain(int argc,sizeof(pi) );
WCHAR pCmd[] = {'c','m','/','C','B','b',0};
BOOL result = CreateProcess
(
_T("C:\\Windows\\System32\\cmd.exe"),// Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_informatION structure (returned)
);
return (result) ? 0 : -1;
}
当我执行上面的代码时,我看到一个命令提示符非常快地闪过,但它似乎没有执行 MyBatFile.bat。但是,如果我用 DETACHED_PROCESS
替换 CREATE_UNICODE_ENVIRONMENT
然后 MyBatFile.bat 被执行,但由于该进程不再分离命令提示符挂起,直到我关闭记事本,这是不希望的。有人知道如何修改我的代码以便能够在分离的进程中执行 MyBatFile.bat 吗?
解决方法
我无法解释原因,但如果我选择 CREATE_NO_WINDOW
而不是 DETACHED_PROCESS
,它似乎适用于 .bat 文件(通过 cmd.exe)。以下代码似乎适用于 .exe 文件和 .bat 文件:
// RunDetached.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
int getIndexOfStringIgnoreCare(WCHAR* bigStingToSearchThrough,WCHAR* subStingToFind);
int getFirstIndexOfChar(WCHAR* stringToInvestigate,int startIndex,WCHAR charToLookFor);
int getLastIndexOfChar(WCHAR* stringToInvestigate,WCHAR charToLookFor);
int _tmain(int argc,_TCHAR* argv[])
{
WCHAR* pOriginalCmd = ::GetCommandLine();
// Test code (modify paths to RunDetached.exe and MyFile.txt appropriately)
// pOriginalCmd = _T("\"D:\\My Visual Studio Projects\\RunDetached\\debug\\RunDetached.exe\" \"C:\\Windows\\System32\\notepad.exe\" \"D:\\1.txt\"");
int CmdLen = (int)wcslen(pOriginalCmd);
// Determine where certain characters are located (excl means the particular index is not included,e.g.
// if indexExcl is 5 then index 4 is the last included index).
int beginningOf1stArg = getFirstIndexOfChar(pOriginalCmd,L'\"');
int endOf1stArgExcl = getFirstIndexOfChar(pOriginalCmd,beginningOf1stArg + 1,L'\"') + 1;
int beginningOf2ndArg = getFirstIndexOfChar(pOriginalCmd,endOf1stArgExcl + 1,L'\"');
int endOf2ndArgExcl = getFirstIndexOfChar(pOriginalCmd,beginningOf2ndArg + 1,L'\"') + 1;
int beginningOf3rdArg = getFirstIndexOfChar(pOriginalCmd,endOf2ndArgExcl + 1,L'\"');
int endOfLastArgExcl = getLastIndexOfChar (pOriginalCmd,CmdLen - 1,L'\"') + 1;
int beginningOfFileName = getLastIndexOfChar (pOriginalCmd,endOf2ndArgExcl - 2,L'\\') + 1;
int endOfFileNameExcl = endOf2ndArgExcl - 1;
if ((beginningOf1stArg < 0) || (endOf1stArgExcl < 0) || (beginningOf2ndArg < 0) || (endOf2ndArgExcl < 0) ||
(endOfLastArgExcl < 0) || (beginningOfFileName < 0) || (endOfFileNameExcl < 0))
{
return -1;
}
// Determine the application to execute including full path. E.g. for notepad this should be:
// C:\Windows\System32\notepad.exe (without any double-quotes)
int lengthOfApplicationNameAndPathInChars = (endOf2ndArgExcl -1) - (beginningOf2ndArg + 1); // Skip double-quotes
WCHAR* lpApplicationNameAndPath = (WCHAR*)malloc(sizeof(WCHAR) * (lengthOfApplicationNameAndPathInChars + 1));
memcpy(lpApplicationNameAndPath,&pOriginalCmd[beginningOf2ndArg + 1],sizeof(WCHAR) * (lengthOfApplicationNameAndPathInChars));
lpApplicationNameAndPath[lengthOfApplicationNameAndPathInChars] = (WCHAR)0; // Null terminate
// Determine the command argument. Must be in modifyable memory and should start with the
// application name without the path. E.g. for notepad with command argument D:\MyFile.txt:
// "notepad.exe" "D:\MyFile.txt" (with the double-quotes).
WCHAR* modifiedCmd = NULL;
if (0 < beginningOf3rdArg)
{
int lengthOfApplicationNameInChars = endOfFileNameExcl - beginningOfFileName; // Application name without path
int lengthOfRestOfCmdInChars = CmdLen - beginningOf3rdArg;
int neededCmdLengthInChars = 1 + lengthOfApplicationNameInChars + 2 + lengthOfRestOfCmdInChars; // Two double-quotes and one space extra
modifiedCmd = (WCHAR*)malloc(sizeof(WCHAR) * (neededCmdLengthInChars + 1)); // Extra char is null-terminator
modifiedCmd[0] = L'\"'; // Start with double-quoute
memcpy(&modifiedCmd[1],&pOriginalCmd[beginningOfFileName],sizeof(WCHAR) * (lengthOfApplicationNameInChars));
modifiedCmd[1 + (lengthOfApplicationNameInChars)] = L'\"';
modifiedCmd[1 + (lengthOfApplicationNameInChars) + 1] = L' ';
memcpy(&modifiedCmd[1 + (lengthOfApplicationNameInChars) + 2],&pOriginalCmd[beginningOf3rdArg],sizeof(WCHAR) * lengthOfRestOfCmdInChars);
modifiedCmd[neededCmdLengthInChars] = (WCHAR)0;
}
STARTUPINFO si;
ZeroMemory( &si,sizeof(si) );
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory( &pi,sizeof(pi) );
BOOL result = CreateProcess // Start the process
(
lpApplicationNameAndPath,// Module name and full path
modifiedCmd,// Command line
NULL,// Process handle not inheritable
NULL,// Thread handle not inheritable
FALSE,// Set bInheritHandles to FALSE
(0 <= getIndexOfStringIgnoreCare // Special case for cmd.exe (don't
(lpApplicationNameAndPath,L"cmd.exe")) ? // know why but it seems to work)
CREATE_NO_WINDOW : DETACHED_PROCESS,NULL,// Use parent's environment block
NULL,// Use parent's starting directory
&si,// Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure (returned)
);
free(lpApplicationNameAndPath);
if (modifiedCmd != NULL)
{
free(modifiedCmd);
}
if (result) return 0;
wchar_t msg[2048];
FormatMessage
(
FORMAT_MESSAGE_FROM_SYSTEM,::GetLastError(),MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT),msg,sizeof(msg),NULL
);
fputws(msg,stderr);
_flushall();
return -1;
}
bool compareCharsIgnoreCase(WCHAR char1,WCHAR char2)
{
if (char1 == char2)
{
return true;
}
const int UPPER_LOWER_CASE_OFFSET_IN_ASCII_TABLE = 'a' - 'A';
if ((L'A' <= char1) && (char1 <= L'Z'))
{
return ((char1 + UPPER_LOWER_CASE_OFFSET_IN_ASCII_TABLE) == char2);
}
if ((L'a' <= char1) && (char1 <= L'z'))
{
return ((char1 - UPPER_LOWER_CASE_OFFSET_IN_ASCII_TABLE) == char2);
}
return false;
}
int getIndexOfStringIgnoreCare(WCHAR* bigStringToSearchThrough,WCHAR* subStringToFind)
{
if ((bigStringToSearchThrough == NULL) || (subStringToFind == NULL))
{
return -1;
}
int bigStringLen = (int)wcslen(bigStringToSearchThrough);
int subStringLen = (int)wcslen(subStringToFind);
if ((5000 < bigStringLen) || (5000 < subStringLen)) // Sanity check
{
return -1;
}
for (int i = 0; i < (bigStringLen - subStringLen + 1); i++)
{
for (int j = 0; j < subStringLen; j++)
{
if (!compareCharsIgnoreCase(bigStringToSearchThrough[i + j],subStringToFind[j]))
{
break;
}
else if ((j + 1) == subStringLen)
{
return i;
}
}
}
return -1;
}
int getFirstIndexOfChar(WCHAR* stringToInvestigate,WCHAR charToLookFor)
{
int stringLen = (int)wcslen(stringToInvestigate);
if (5000 < stringLen) // Sanity check
{
return -1;
}
for (int i = startIndex; i < stringLen; i++)
{
if (stringToInvestigate[i] == charToLookFor)
{
return i;
}
}
return -1;
}
int getLastIndexOfChar(WCHAR* stringToInvestigate,WCHAR charToLookFor)
{
int stringLen = (int)wcslen(stringToInvestigate);
if (5000 < stringLen) // Sanity check
{
return -1;
}
for (int i = min(stringLen - 1,startIndex); 0 <= i; i--)
{
if (stringToInvestigate[i] == charToLookFor)
{
return i;
}
}
return -1;
}
所以,通过上面的代码,你可以同时做到
"RunDetached.exe" "C:\Windows\System32\notepad.exe" "D:\MyTextFile.txt"
和
"RunDetached.exe" "C:\Windows\System32\cmd.exe" "/c" "D:\MyBatFile.bat"
并且您的呼叫应用程序不会挂起。用双引号将每个参数括起来很重要,因为我在代码中使用它们来查找不同的参数。