当应用程序卸载包含 C++ 代码的 DLL 时,GetOpenFileName() 崩溃

问题描述 投票:0回答:1

我在 Windows 上遇到

GetOpenFileName()
的奇怪问题。如果我的应用程序使用
LoadLibrary()
加载 DLL,然后在调用
FreeLibrary()
之前再次使用
GetOpenFileName()
卸载它,则我的应用程序在调用
GetOpenFileName()
时会完全崩溃。这是 WinDbg 崩溃后的报告:

ModLoad: 000007fe`fe2d0000 000007fe`fe369000   C:\Windows\system32\CLBCatQ.DLL
ModLoad: 000007fe`ffd40000 000007fe`fff17000   C:\Windows\system32\SETUPAPI.dll
ModLoad: 000007fe`fd930000 000007fe`fd966000   C:\Windows\system32\CFGMGR32.dll
ModLoad: 000007fe`fd970000 000007fe`fd98a000   C:\Windows\system32\DEVOBJ.dll
ModLoad: 000007fe`fbdd0000 000007fe`fbefc000   C:\Windows\system32\propsys.dll
ModLoad: 000007fe`fb8a0000 000007fe`fb8cd000   C:\Windows\system32\ntmarta.dll
ModLoad: 000007fe`ff640000 000007fe`ff692000   C:\Windows\system32\WLDAP32.dll
ModLoad: 000007fe`fd790000 000007fe`fd79f000   C:\Windows\system32\profapi.dll
ModLoad: 000007fe`f1b90000 000007fe`f1c0f000   C:\Program Files\Common Files\microsoft shared\ink\tiptsf.dll
ModLoad: 000007fe`f6ff0000 000007fe`f71bb000   C:\Windows\system32\explorerframe.dll
ModLoad: 000007fe`fbca0000 000007fe`fbce3000   C:\Windows\system32\DUser.dll
ModLoad: 000007fe`fb950000 000007fe`fba42000   C:\Windows\system32\DUI70.dll
ModLoad: 000007fe`fd580000 000007fe`fd5d7000   C:\Windows\system32\apphelp.dll
ModLoad: 000007fe`f6750000 000007fe`f6ad7000   C:\Program Files\Microsoft Office\root\Office16\GROOVEEX.DLL
ModLoad: 000007fe`f6fd0000 000007fe`f6fe5000   C:\Program Files\Microsoft Office\root\Office16\VCRUNTIME140.dll
ModLoad: 000007fe`f6fc0000 000007fe`f6fc4000   C:\Program Files\Microsoft Office\root\Office16\api-ms-win-crt-runtime-l1-1-0.dll
ModLoad: 000007fe`f6650000 000007fe`f6744000   C:\Windows\system32\ucrtbase.DLL
ModLoad: 000007fe`f6530000 000007fe`f6533000   C:\Windows\system32\api-ms-win-core-timezone-l1-1-0.dll
ModLoad: 000007fe`f6480000 000007fe`f6483000   C:\Windows\system32\api-ms-win-core-file-l2-1-0.dll
ModLoad: 000007fe`f6470000 000007fe`f6473000   C:\Windows\system32\api-ms-win-core-localization-l1-2-0.dll
ModLoad: 000007fe`f6590000 000007fe`f6593000   C:\Windows\system32\api-ms-win-core-processthreads-l1-1-1.dll
ModLoad: 000007fe`f6580000 000007fe`f6583000   C:\Windows\system32\api-ms-win-core-file-l1-2-0.dll
ModLoad: 000007fe`f6570000 000007fe`f6573000   C:\Program Files\Microsoft Office\root\Office16\api-ms-win-crt-heap-l1-1-0.dll
ModLoad: 000007fe`f6560000 000007fe`f6564000   C:\Program Files\Microsoft Office\root\Office16\api-ms-win-crt-string-l1-1-0.dll
ModLoad: 000007fe`f6550000 000007fe`f6554000   C:\Program Files\Microsoft Office\root\Office16\api-ms-win-crt-stdio-l1-1-0.dll
ModLoad: 000007fe`f6540000 000007fe`f6544000   C:\Program Files\Microsoft Office\root\Office16\api-ms-win-crt-convert-l1-1-0.dll
ModLoad: 000007fe`f3c00000 000007fe`f3c9b000   C:\Program Files\Microsoft Office\root\Office16\MSVCP140.dll
ModLoad: 000007fe`f64d0000 000007fe`f64d3000   C:\Program Files\Microsoft Office\root\Office16\api-ms-win-crt-locale-l1-1-0.dll
ModLoad: 000007fe`f64c0000 000007fe`f64c5000   C:\Program Files\Microsoft Office\root\Office16\api-ms-win-crt-math-l1-1-0.dll
ModLoad: 000007fe`f64b0000 000007fe`f64b3000   C:\Program Files\Microsoft Office\root\Office16\api-ms-win-crt-filesystem-l1-1-0.dll
ModLoad: 000007fe`f64a0000 000007fe`f64a3000   C:\Program Files\Microsoft Office\root\Office16\api-ms-win-crt-time-l1-1-0.dll
ModLoad: 000007fe`f6490000 000007fe`f6493000   C:\Program Files\Microsoft Office\root\Office16\api-ms-win-crt-environment-l1-1-0.dll
ModLoad: 000007fe`f6460000 000007fe`f6463000   C:\Program Files\Microsoft Office\root\Office16\api-ms-win-crt-utility-l1-1-0.dll
(14e0.15a8): C++ EH exception - code e06d7363 (first chance)
(14e0.15a8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
<Unloaded_test.dll>+0x3e1250:
000007fe`dbc21250 ??              ???

我可以通过将对 DLL 的

FreeLibrary()
调用推迟到我的应用程序终止时来解决此问题。那么就不会再出现崩溃了。不过,我想知道为什么加载和卸载该 DLL 会影响
GetOpenFileName()
。我不明白这些事情之间有何关联。

这是否与加载DLL的应用程序和DLL本质上有些不同有关?加载DLL的应用程序是使用Visual C编译的纯C应用程序。但是,DLL使用C++代码并使用MSYS2框架编译。 WinDbg 的输出似乎在某种程度上暗示崩溃与 C++ 异常处理有关。但是,我的应用程序实际上并未调用 DLL 中的任何 C++ 代码。实际上,没有必要调用 DLL 中的任何代码。只需依次调用

LoadLibrary()
FreeLibrary()
就足以在
GetOpenFileName()
中产生问题。

此外,我不太明白外部 DLL 中的 C++ 异常处理应该如何影响

GetOpenFileName()
。因此,如果有人能够阐明这里实际发生的情况,我会很高兴。

我的问题是:有没有办法修复 DLL,以便可以安全地卸载它,而不会对调用程序造成严重破坏?目前,安全卸载 DLL 的唯一方法是在程序的最后卸载它,但我希望能够随时卸载 DLL,而不会导致

GetOpenFileName()
崩溃。请问我该怎么做?

编辑编辑编辑

这里有一个关于如何重现它的示例。这是文件

test.c
:

#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char *lpCmdLine, int nCmdShow)
{
    OPENFILENAME ofn;
    char szFile[1024];
    HINSTANCE hDLL;
    
    hDLL = LoadLibrary("test.dll");
    FreeLibrary(hDLL);
        
    *szFile = 0;
            
    memset(&ofn, 0, sizeof(OPENFILENAME));
        
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.Flags = OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|OFN_NOCHANGEDIR|OFN_EXPLORER;
    ofn.lpstrFilter = "All files (*.*)\0*.*\0\0";
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = sizeof(szFile);
    
    GetOpenFileName(&ofn);    // CRASHES!!!
    
    return 0;
}

这是文件

dll.c
:(请注意,
test.c
永远不会调用DLL导出
My_DLL_Function
,因此您实际上可以忽略这里的所有代码;它只是放在这里,以便ld在链接时提取所有需要的依赖项)

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_BITMAP_H

#include <pango/pangocairo.h>
#include <pango/pangoft2.h>

__attribute__((visibility("default"))) void My_DLL_Function(void)
{
    PangoContext *context;
    PangoLayout *layout;
    PangoFont *font;
    PangoFontDescription *fontdesc;
    PangoFontMap *fontmap;
    PangoRectangle rc;
    cairo_surface_t *surf;
    cairo_t *cr;
    cairo_matrix_t cm;
    
    fontmap = pango_cairo_font_map_new_for_font_type(CAIRO_FONT_TYPE_FT);
    fontdesc = pango_font_description_from_string("Bitstream Vera Sans 36px");
    
    context = pango_font_map_create_context(fontmap);
    layout = pango_layout_new(context);
        
    pango_layout_set_font_description(layout, fontdesc);
    pango_layout_set_width(layout, 640 * PANGO_SCALE);
    
    font = pango_font_map_load_font(fontmap, context, fontdesc);

    pango_layout_set_markup(layout, "Hello World", -1);             

    pango_layout_get_pixel_extents(layout, &rc, NULL);

    surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, rc.width, rc.height);

    cr = cairo_create(surf);
    
    cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
    
    cairo_matrix_init(&cm, 1, 0, 0, 1, -rc.x, -rc.y);
    cairo_set_matrix(cr, &cm);
    
    pango_cairo_show_layout(cr, layout);
    cairo_surface_flush(surf);
    
    cairo_surface_write_to_png(surf, "test.png");
    
    cairo_destroy(cr);
    cairo_surface_destroy(surf);
    
    g_object_unref(font);
    g_object_unref(layout);
    g_object_unref(context);
    g_object_unref(fontmap);
    pango_font_description_free(fontdesc);
}

这就是使用 MSYS2 编译所有内容的方法:

gcc test.c -o test.exe -lcomdlg32
gcc -DNDEBUG -DGLIB_STATIC_COMPILATION -DGOBJECT_STATIC_COMPILATION -DGIO_STATIC_COMPILATION -DGMODULE_STATIC_COMPILATION -I/c/msys64/mingw64/include/pango-1.0 -I/c/msys64/mingw64/include/cairo -I/c/msys64/mingw64/include/glib-2.0 -I/c/msys64/mingw64/include/harfbuzz -I/c/msys64/mingw64/include/freetype2 -I/c/msys64/mingw64/include -I/c/msys64/mingw64/lib/glib-2.0/include -fvisibility=hidden -c -o dll.o dll.c
gcc -static-libgcc -static-libstdc++ -shared -fPIC -o test.dll dll.o -L/c/msys64/mingw64/lib -Wl,-Bstatic -lpangocairo-1.0 -lpangoft2-1.0 -lpangowin32-1.0 -lpango-1.0 -lharfbuzz -lcairo -lpixman-1 -lgraphite2 -lfontconfig -lfreetype -lbrotlidec -lbrotlienc -lbrotlicommon -lexpat -lthai -ldatrie -lbz2 -lpng -lz -lfribidi -lgio-2.0 -lgobject-2.0 -lglib-2.0 -lpcre2-8 -lpcre2-16 -lpcre2-32 -lpcre2-posix -lffi -luuid -lintl -liconv -lstdc++ -lgcc_eh -lpthread -lgcc_eh -Wl,-Bdynamic -lshlwapi -ldwrite -lWs2_32 -lrpcrt4 -lshell32 -lmsimg32 -lusp10 -lgdi32 -lole32

现在从 MSYS2 控制台运行

test.exe
,它将打印“分段错误”并崩溃。每次在 Windows 10 上运行
test.exe
时都会发生这种情况。在
FreeLibrary()
之后调用
GetOpenFileName()
时,不会出现分段错误。

c++ exception winapi dll
1个回答
0
投票

我认为您的标志中可能有一条线索 - 您传递了 OFN_NOCHANGEDIR,但 GetOpenFileName 会忽略此标志 - 它更改您的应用程序的当前目录。

我敢打赌,您正在使用的众多 DLL 或其他调用之一会假设工作目录仍然是应用程序的启动目录(您的 LoadLibrary 调用假设这一点,所以我猜想它在源代码中的其他地方是相同的)。

我怀疑代码中某处有一个文件操作,在您调用 GetOpenFileName 时,其对文件名的假设不再有效,这导致了崩溃。

© www.soinside.com 2019 - 2024. All rights reserved.