如何在 DETACHED_PROCESS 中执行 .bat 文件?
How do I execute a .bat-file in a DETACHED_PROCESS?
我有一个简单的 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','.','e','x','e',' ','D',':','\','M','y','T','e','x','t','F','i','l','e','.','t','x','t',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 并将 MyTextFile.txt 替换为 MyBatFile.bat,则它不起作用。 MyBatFile.bat的内容:
C:\Windows\System32\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[] = {'c','m','d','.','e','x','e',' ','/','C',' ','D',':','\','M','y','B','a','t','F','i','l','e','.','b','a','t',0};
BOOL result = CreateProcess
(
_T("C:\Windows\System32\cmd.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;
}
当我执行上面的代码时,我看到命令提示符快速闪过,但它似乎没有执行 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, int startIndex, 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, 0, 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,
NULL,
::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, int startIndex, 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, int startIndex, 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"
并且您的调用应用程序不会挂起。用双引号将每个参数括起来很重要,因为我在代码中使用它们来查找不同的参数。
我有一个简单的 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','.','e','x','e',' ','D',':','\','M','y','T','e','x','t','F','i','l','e','.','t','x','t',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 并将 MyTextFile.txt 替换为 MyBatFile.bat,则它不起作用。 MyBatFile.bat的内容:
C:\Windows\System32\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[] = {'c','m','d','.','e','x','e',' ','/','C',' ','D',':','\','M','y','B','a','t','F','i','l','e','.','b','a','t',0};
BOOL result = CreateProcess
(
_T("C:\Windows\System32\cmd.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;
}
当我执行上面的代码时,我看到命令提示符快速闪过,但它似乎没有执行 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, int startIndex, 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, 0, 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,
NULL,
::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, int startIndex, 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, int startIndex, 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"
并且您的调用应用程序不会挂起。用双引号将每个参数括起来很重要,因为我在代码中使用它们来查找不同的参数。