2010-07-20 23 views
5

我有一个非常复杂的程序失败了,我已经用批处理文件和C程序将它简化为这个测试集。为什么我无法在Windows 7中测试返回码?

我的C程序使用ExitProcess将错误级别传递回批处理文件。有时在Windows 7(Microsoft Windows [版本6.1.7600])上,错误级别未被正确解释。

我认为这应该永远运行。在Windows XP上它似乎永远运行。在两台不同的双核Windows 7机器上(一台64位,一台32位),几分钟内就会失败。

我无法想象自己做错了什么,但如果在Windows 7上有关于ExitProcess的一些有趣的事情,我想我会问。这里有什么我非法做的?为CMD.EXE

test.bat批处理文件:

@ECHO OFF 
SET I=0 
:pass 
SET /A I=I+1 
Title %I% 
start/wait level250 
if errorlevel 251 goto fail 
if errorlevel 250 goto pass 
:fail 

计划level250.c:

#include "windows.h" 

static volatile int Terminate = 0; 

static unsigned __stdcall TestThread(void * unused) 
    { 
    Terminate = 1; 
    return 0; 
    } 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) 
    { 
    CreateThread(NULL, 0, TestThread, NULL, 0, NULL); 

    while (Terminate == 0) Sleep(1); 
    ExitProcess(250); 
    } 

我的编译器版本和调用是:

微软(R) 32位C/C++ Optimizing Compiler Version 12.00.8804 for 80x86

版权所有(C)微软公司1984-1998。版权所有。

CL/MT level250.c

其他信息:我也尝试下JPSoft的TCC运行并获得相同的行为,使用CMD。我正在使用一个直接的.c程序,而不是.cpp。我没有看到任何单线程版本的失败。我把源代码和二进制文件放在http://jcook.info/win7fail上,zip文件MD5是579F4FB15FC7C1EA454E30FDEF97C16B,CRC32是C27CB73D。

编辑经过建议后,我进一步改变了测试用例,仍然看到了失败。在我们的实际应用中,有数百个线程。有些线程会以各种重要的返回码退出,有些线程会永远运行,有些线程挂在操作系统调用或dll中,并且很难(如果不是不可能的话)终止。

#include "windows.h" 

static unsigned __stdcall TestThread(void * unused) 
    { 
    return 0; 
    } 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) 
    { 
    CreateThread(NULL, 0, TestThread, NULL, 0, NULL); 
    return(250); 
    } 
+1

在调用ExitProcess()之前,您是否在等待线程完成之后加入? – IanNorton 2010-07-20 22:58:40

+0

您应该使用WaitForSingleObject()而不是繁忙循环来等待线程终止。此外,只需从WinMain()返回250而不是直接调用ExitProcess()。编译器的启动代码将在WinMain()退出后调用ExitProcess()。 – 2010-07-21 00:16:08

+0

@IanNorton:我不认为有必要等待线程终止和/或加入。我们的整个程序有一些线程无法终止,无论是由于I/O挂起或系统调用还是其他原因。是否有一些来自Microsoft的文档可以让我指出,它显示了重新加入线程的要求? @Remy:我们的实际程序要复杂得多,而且有直接测试的原因。事实证明,删除所有提到的Terminate变量并不能解决问题。即一个简单返回的线程和一个创建该线程然后退出的WinMain也失败。 – piCookie 2010-07-21 13:20:08

回答

1

打印内容的返回码实际上是。不能保证,如果出现问题,您将获得您期望的251和250,例如通过分段错误或其他您不知道的错误。此外,我无法看到你的代码中返回251。警惕高退出代码,我相信255是安全的便携式限制,在某些系统上它可能小于64,或者< = 127。(这可能与您显然使用Windows时无关,但值得注意)

也尝试调用调试器,或在进程意外死亡时加载核心转储。

+0

如果错误级别为251或更高,则在批处理文件“if errorlevel 251”中执行操作。因此,下面的“如果errorlevel 250”是测试准确的250的方法。我们对segfault等有各种处理,但是我已经减少了数千行问题,希望能够了解为什么这个简单案件失败。 – piCookie 2010-07-21 13:07:25

1

它看起来是在它失败的时候返回线程的结果。我将线程的返回值更改为37,并将echo %errorlevel%添加到批处理文件的末尾。当它停在我的电脑上时,它打印了37张。所以似乎有某种同步问题。为了解决这个问题,我改变了代码,主要为以下:

HANDLE h = CreateThread(NULL, 0, TestThread, NULL, 0, NULL); 
while (Terminate == 0) Sleep(1); 
WaitForSingleObject(h, INFINITE); 
ExitProcess(250); 

ExitProcess的文档明确表示,退出代码是“为过程和所有线程”。所以看起来有一个bug,然而,依靠ExitProcess来杀死所有线程似乎并不是最好的计划。所以等待他们完成可能是一个合理的行动。

我构建了该程序,并重现了VC6(我相信您使用的版本),VS2005和VS2008的问题。我出于好奇,在2核win7笔记本电脑和4核win7台式机上运行它。它没有在老式的单核心超线程XP机器上重现,但这并不是说它最终不会失败;也许它需要在那里跑得更久。

编辑这将是一个混乱,但也许解决方法是将退出代码存储在应用程序中的全局变量并从所有线程返回该值。然后在发生此问题/错误的情况下,应用程序的退出代码仍然是所需的值。

+0

非常感谢您的努力!不幸的是,我们不能等待线程在我们的实际程序中完成(有些挂在操作系统等),但我会在调用ExitProcess之前探索更多关于要做的事情。我们的程序在Windows NT 4.0上运行了多年,没有像这样的错误级别失败;我怀疑你的XP机器永远不会失败。 – piCookie 2010-07-21 13:43:12

+0

唉,您的编辑kludge在我们的实时应用程序中也不可行,因为有些线程在返回值中返回信息。感谢您的持续创意! – piCookie 2010-07-21 16:35:40

+0

@piCookie:现实世界阴谋反对简单的解决方案:)我认为你是正确的,它特别是一个Win7的问题。或者至少我无法证明你错了。我让它在一台双核XP机器上运行超过50万次而没有失败。这是一个有趣的问题(对我来说比我更确定,因为它不是我的直接问题)。 – 2010-07-21 17:54:01

相关问题