从其他dll中抑制或拦截来自CRT实例的CRT检查

时间:2011-05-24 16:45:51

标签: c winapi exception-handling crt

我的程序加载了几个dll并调用它们的函数。 dll可以使用不同版本的CRT。

当C运行时检查参数的有效性并发现问题时,它会调用无效参数句柄,无论是否有“发送 - 不发送”对话框,它都会关闭应用程序。

我尝试调用* _set_invalid_parameter_handler *,但只有从坏dll中调用它才有效。我尝试了 SetErrorMode ,但我设法做的就是在没有对话框的情况下杀死进程。

有没有办法处理这些异常?我不在乎某些资源是否受到损害。我想要的只是允许用户保存配置。如果出现对话框,则会单击该对话框并终止该过程。


修改 事实证明,加载所有版本的CRT或枚​​举所有DLL失败的解决方案。为了清楚起见,这里有一个小例子:

这将是我的主要应用程序(让我们调用文件 application.c ):

#include <windows.h>

void myInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) {
   wprintf(L"Invalid parameter detected in function %s. File: %s Line: %d\n", function, file, line);
   wprintf(L"Expression: %s\n", expression);
}

void fixMyProblem() {
}

int main(int argc, char **argv) {
    HMODULE hModule = LoadLibrary("extension.dll");
    void (WINAPI *function)() = GetProcAddress(hModule, "function");
    fixMyProblem();
    function();
}

这个应用程序加载一个做坏事的dll(它不是由我开发的,所以我不会接受任何解决方法告诉我修复那里的bug)。让我们称之为文件 extension.c

#include <stdio.h>

__declspec(dllexport) void function() {
    printf("do bad stuff");
    fopen(NULL, "r");
}

要编译,请执行:

cl extension.c /link /OUT:extension.dll /DLL
cl application.c

问题是我在函数 fixMyProblem()中该怎么办,所以我没有在XP或<上发送发送/不发送对话框em>应用程序已停止工作对话框7。

David Gladfelter认为我应该这样做

void fixMyProblem() {
    _set_invalid_parameter_handler(myInvalidParameterHandler);
}

并为每个版本 CRT提供此功能。事实证明,即使使用单个版本的CRT(我对exe和dll使用相同的版本),它仍然无效。他们都使用CRT的相同版本,但似乎他们没有使用相同的 CRT。

如果是这种情况,我认为我必须更改的内容是在DLL内部。当然,它不会导出* _set_invalid_parameter_handler *。

但为了公平对待David Heffernan,这是他的解决方案的实施:

#include <Psapi.h>
#pragma comment(lib, "Psapi.lib")
void fixMyProblem() {
    HANDLE hProcess = GetCurrentProcess();
    HMODULE *hModules;
    DWORD requiredSize = 0;
    DWORD secondRequiredSize = 0;
    if (!EnumProcessModules(hProcess, NULL, 0, &requiredSize)) {
        printf("oops\n");
        return;
    }
    hModules = malloc(requiredSize);
    if (EnumProcessModules(hProcess, hModules, requiredSize, &secondRequiredSize)) {
        int i;
        int loadedModules = min(requiredSize, secondRequiredSize) / sizeof(HMODULE);
        for (i = 0; i < loadedModules; i++) {
            void *(WINAPI *_set_invalid_parameter_handler_function)(void *) = (void *(WINAPI *)(void *)) GetProcAddress(hModules[i], "_set_invalid_parameter_handler");
            if (_set_invalid_parameter_handler_function != NULL) {
                _set_invalid_parameter_handler_function(myInvalidParameterHandler);
                printf("fixed dll %d\n", i);
            }
        }
    } else {
        printf("oops\n");
    }
    free(hModules);
}

对于我的真实应用程序,不是这个测试,我得到1 dll修复(msvcp90.dll)。它仍然无法解决我的问题。

我很感激你解决这个问题的任何帮助。

3 个答案:

答案 0 :(得分:4)

如果使用静态链接的CRT构建dll,则CRT的状态和功能将是该dll实例的本地和函数。 我假设CRT使用的无效参数处理程序正在从OS调用UnhandledExceptionFilter函数,以显示“nice”错误对话框。

您可以尝试挂钩UnhandledExceptionFilterTerminateProcess等函数,使dll使用您自己的函数。您可以通过解析加载的dll的导入地址表,搜索您感兴趣的函数名称,并将地址更改为指向您的函数来完成此操作。

答案 1 :(得分:1)

您始终可以枚举进程中的模块,如果它是C运行时,则通过调用GetProcAddress来获取无效参数处理程序。

但是你最好尝试修复根目录下的错误。试图忽略这些问题大多只会导致进一步的问题,因为内存会被破坏等等。

答案 2 :(得分:0)

您可以创建另一个DLL,它使用与DLL使用相同版本的CRT导致无效参数处理程序被调用,并在该新DLL中注册无效参数处理程序。无效参数处理程序对进程/ CRT版本组合是全局的。

如果你不知道DLL正在使用什么版本并且你无法弄清楚,最糟糕的情况是你创建几个DLL,每个CRT版本一个:

  • VS 6静态/动态/多线程/单线程
  • VS.NET静态/动态/多线程/单线程
  • VS 2003静态/动态/多线程/单线程
  • VS 2005静态/动态
  • VS 2008静态/动态
  • VS 2010静态/动态

您可以将它们创建为静态.lib文件,并将它们全部链接到一个(非常混乱的)DLL中。