4
回答
Windows2000下CreateProcess启动成功的进程却执行出错
终于搞明白,存储TCO原来是这样算的>>>   
VS2008编译的MFC应用程序,需要从windows2000支持到Win8.1,其中有一个模块需要启动一个第三方的exe进程,并获取其输出,此功能在XP至Win8.1下都运行成功,仅windows2000下失败。
windows2000为sp4,专业版和高级服务器版都测试失败。
重要提示:此第三方exe在手动启动(直接双击)下,正常运行,dos窗口打印“install success!”,但在createprocess下会输出"install failed, error:-xxx",xxx为错误号,此错误号非标准GetLastError()的错误号。项目略急,求大神帮助!
我的代码如下:
HANDLE hStdInWrite, hStdInRead, hStdInWriteUp; //用于重定向子进程输入的句柄
HANDLE hStdOutWrite, hStdOutRead, hStdOutReadUp; //用于重定向子进程输出的句柄
SECURITY_ATTRIBUTES stSa;
STARTUPINFO stSi;
PROCESS_INFORMATION stPi;
char acCmd[1024] = "";
DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS;
DWORD ulExitCode = 0;
DWORD ulSingleRet = 0;
int iRet = 0;


stSa.lpSecurityDescriptor = NULL;//NULL则使用默认的进程描述符
stSa.bInheritHandle = true;//安全描述的对象能否被新创建的进程继承返回句柄,若为TRUE 则新进程继承该句柄
stSa.nLength = sizeof(SECURITY_ATTRIBUTES);


CreatePipe(&hStdOutRead, &hStdOutWrite, &stSa, 0);//创建子进程输出匿名管道
DuplicateHandle(::GetCurrentProcess(), hStdOutRead, ::GetCurrentProcess(), &hStdOutReadUp, 0, false,DUPLICATE_SAME_ACCESS);
::CloseHandle( hStdOutRead );


CreatePipe(&hStdInRead, &hStdInWrite, &stSa, 0);//创建子进程输入匿名管道
DuplicateHandle(::GetCurrentProcess(), hStdInWrite, ::GetCurrentProcess(), &hStdInWriteUp, 0, false,DUPLICATE_SAME_ACCESS);
::CloseHandle( hStdInWrite );


GetStartupInfo(&stSi);
stSi.hStdInput = hStdInRead;//重定向子进程输入
stSi.hStdOutput = hStdOutWrite;//重定向子进程输入
stSi.hStdError = GetStdHandle(STD_ERROR_HANDLE);
stSi.wShowWindow = SW_HIDE;
stSi.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
stSi.cb = sizeof(stSi);


if (strlen(stParam.acAppPath) > 0)//exe文件的绝对路径
{
iRet = CreateProcess(stParam.acAppPath, stParam.acAppParam, NULL, NULL, TRUE, dwCreationFlags, NULL, NULL, &stSi, &stPi);//在win2000下以此种方式启动失败
}
else if (strlen(stParam.acShellCmd) > 0)//带exe文件绝对路径的dos命令
{
SHGetSpecialFolderPath(NULL, acCmd, CSIDL_SYSTEM, FALSE);
strcat(acCmd, "\\cmd.exe /c ");
strcat(acCmd, stParam.acShellCmd);
iRet = CreateProcess(NULL, acCmd, NULL, NULL, TRUE, dwCreationFlags, NULL, NULL, &stSi, &stPi);//在win2000下以此种方式启动失败
}
if (0 == iRet)//CreateProcess()执行成功则返回非0值
{
return FALSE;
}


CloseHandle(hStdInRead);
if (strlen(stParam.acInput) > 0)//需要向管道输入的内容
{
WriteFile(hStdInWriteUp, stParam.acInput, strlen(stParam.acInput), &ulSingleRet, NULL);
}


iRet = TRUE;
ulSingleRet = WaitForSingleObject(stPi.hProcess, INFINITE);
if (ulSingleRet != WAIT_OBJECT_0 && !GetExitCodeProcess(stPi.hProcess, &ulExitCode))
{
if (ulSingleRet == WAIT_TIMEOUT)
{
sprintf_s(stParam.acRetInfo, sizeof(stParam.acRetInfo), "Process exit timeout, LastErr[%d]", GetLastError());
//return WAIT_TIMEOUT;
}
else
{
sprintf_s(stParam.acRetInfo, sizeof(stParam.acRetInfo), "Process exit error, WaitForSingleObject()=[%d], GetExitCodeProcess()=[%d], LastErr[%d]", ulSingleRet, ulExitCode, GetLastError());
}
iRet = FALSE;
}


CloseHandle(hStdOutWrite);//在调用ReadFile前必须关闭该句柄,否则由于父进程的管道写入端有未关闭的Write句柄,ReadFile最后无法返回0
if (iRet && stParam.bIsGetOutput)//是否需要获取输出,调用前已设置为TRUE
{
char acTmp[2048] = "";//保存读入数据的临时缓冲区
unsigned long ulWantedBytes = 2048;//欲读入的字节数
unsigned long ulRealReadBytes;//实际读取的字节数


memset(stParam.acOutput, 0, sizeof(stParam.acOutput));
while (ReadFile(hReadPipe, (LPVOID)acTmp, ulWantedBytes, &ulRealReadBytes, NULL) && ulRealReadBytes > 0)
{
strcat_s(stParam.acOutput, acTmp);
memset(acTmp, 0, sizeof(acTmp));
}
}
CloseHandle(hStdOutReadUp);
CloseHandle(hStdInWriteUp);
TerminateProcess(stPi.hProcess, 0); 
CloseHandle(stPi.hProcess);
CloseHandle(stPi.hThread);


if (strlen(stParam.acAppName) > 0)//exe文件名
{
KillWinProcess(stParam.acAppName);
}
if ((0 == strlen(stParam.acAppPath)) || (strlen(stParam.acShellCmd) > 0))
{
KillWinProcess("conhost.exe");//命令行程序的宿主进程
KillWinProcess("cmd.exe");
}

举报
Kloud
发帖于3年前 4回/636阅
共有4个答案 最后回答: 3年前

CreateProcess MSDN 上明确有说明 服务器版本是Windows 2003 or later 而Desktop是Windows XP or Later.
http://msdn.microsoft.com/en-us/library/ms682425.aspx

所以失败很正常了,你可以使用ShellExecute 这个支持Windows 2000。
http://msdn.microsoft.com/en-us/library/bb762153%28VS.85%29.aspx

当然如果你仅仅是开启一个程序,可以用WinExe()或者是system().

谢谢你的回复,我用ShellExecute(),WinExe(),system()均尝试过了,exe都执行出错了。如果修改子进程的错误弹出方式,可以看到下图的错误提示:

只是LsaLookupNames2()是第三方exe中调用的,错误框也是exe触发的,我的代码中没有调用LsaLookupNames2()。

问题原因找到了!
在windows2000下,父进程father.exe调用CreateProcess()启动子进程child.exe时,需要指定工作目录(倒数第三个参数,即child.exe的所在目录的绝对路径)。如果father.exe与child.exe不在同一目录下,工作目录参数就不能使用NULL(NULL代表使用父进程的工作目录)。
其他高版本windows操作系统下,即使father.exe与child.exe不在同一目录,而使用NULL,也没有问题。不过还是指定一下比较好,建议不要用NULL了。
顶部