使用 DLL 函数时的访问冲突异常

Posted

技术标签:

【中文标题】使用 DLL 函数时的访问冲突异常【英文标题】:Access violation exception when using a DLL function 【发布时间】:2012-09-14 16:24:45 【问题描述】:

我在 C++ 练习中创建了一个简单的 DLL,但在调用 DLL 函数时遇到了访问冲突异常。这是DLL的头文件(我怀疑CPP在这里有用):

#pragma once

namespace MathFuncs

class MyMathFuncs

public:
    // Returns a + b
    static __declspec(dllexport) double Add(double a, double b);

    // Returns a - b
    static __declspec(dllexport) double Subtract(double a, double b);

    // Returns a * b
    static __declspec(dllexport) double Multiply(double a, double b);

    // Returns a / b
    // Throws DivideByZeroException if b is 0
    static __declspec(dllexport) double Divide(double a, double b);
;

这是我的主要内容:

#include <iostream>
#include "windows.h"

using namespace std;

int main(void)

    double (__cdecl *MYPROC)(double,double);
    /* get handle to dll */
    HINSTANCE hGetProcIDDLL = LoadLibrary("DLLExample.dll"); 
if(hGetProcIDDLL == NULL)
    throw;
   /* get pointer to the function in the dll*/
    FARPROC lpfnGetProcessID = GetProcAddress(hGetProcIDDLL,"Add"); 
    if(lpfnGetProcessID)
        throw;
    MYPROC = (double (__cdecl *)(double,double))lpfnGetProcessID;
    if(MYPROC)
        throw;

    double x = MYPROC(5.5,5);

    return 0;

有什么建议吗?谢谢!

【问题讨论】:

DLL 是否在运行主程序的同一文件夹中? 【参考方案1】:

你的问题在这里:

FARPROC lpfnGetProcessID = GetProcAddress(hGetProcIDDLL,"Add"); 
if(lpfnGetProcessID)  // <-- error!
    throw;
MYPROC = (double (__cdecl *)(double,double))lpfnGetProcessID;
if(MYPROC)  // <-- error!
    throw;

double x = MYPROC(5.5,5);

如果lpfnGetProcessId 有地址,则您将引发异常。如果没有,您稍后再尝试调用它(砰!)。

您将希望使用extern "C" 来装饰您的函数,以确保将它们导出为用户友好的名称。这意味着它们不能在一个类中:

namespace MathFuncs

   extern "C" __declspec(dllexport) double Add(double, double);

【讨论】:

【参考方案2】:

如果您的 DLL 没有代码,则只能进行推测,并且您选择的命名似乎在中途发生了变化,但看起来您正在执行以下操作:

1) 加载 dll 并在失败时抛出异常……好的。 2) 在 dll 中找到一个似乎将两个值相加的函数——但如果成功了,你会抛出异常吗?由于您没有抛出异常,因此返回了 NULL,因此您无法找到该函数。 3) 调用函数——它为 NULL,所以你会在这里失败。

我怀疑问题在于您没有考虑 C++ 名称修改。您可以在对 GetProcAddress() 的调用中更改函数名称以正确解释它,或者您可以使用 extern "C" 声明函数以消除重整。

【讨论】:

【参考方案3】:

您确定方法 MathFuncs::MyMathFuncs::Add() 实际上是作为简单的“添加”导出的,就像在您的 GetProcAddress 调用中一样?

我怀疑其中涉及某种形式的 C++ 名称​​mangling

您可能希望在命令行中使用 DUMPBIN /EXPORTS 来查看实际导出的方法名称。

另外,如果GetProcAddress的返回值不是NULL(看你的if测试),函数成功:你为什么throw

另请注意,如果您调整类头文件以便在导出和导入中都使用它,那么使用 DLL 会更简单,例如

#if defined(MATHFUNCS_EXPORT) // Inside DLL implementation
#define MATHFUNCS_API __declspec(dllexport)
#else   // Outside DLL
#define MATHFUNCS_API __declspec(dllimport)
#endif  // MATHFUNCS_EXPORT

class MyMathFuncs

public:

  static MATHFUNCS_API double Add(double a, double b);
  ...
;

这样客户端可以只#include你的头文件(并链接到相应的.lib文件),而不使用LoadLibrary/GetProcAddress

【讨论】:

@RiskX:不客气。如果您觉得我们的答案有用,您可能需要点击向上箭头。【参考方案4】:

我认为Access violation exception when using a DLL function 是正确答案,但有点难以解释 - 我看到的问题是:

    检查是向后的 - 如果我们未能获得函数指针,我们会继续,并且只有在成功获得函数指针时才会抛出。

    成员函数应该可以正常导出,因为函数是静态的,但我们无法获得函数指针,因为 DLL 没有导出名为“Add”的函数 - 实际名称将被破坏,并且可以在 .map 文件中找到(如果已生成)。

    由于我们没有找到函数指针,因此我们将 NULL 转换为函数指针类型并尝试调用它,这就是我们遇到访问冲突的地方。

代码应该是:

FARPROC lpfnGetProcessID = GetProcAddress(hGetProcIDDLL,"**MangledAddFunctionName - see .map file**");
if(lpfnGetProcessID == NULL)
    throw;
MYPROC = (double (__cdecl *)(double,double))lpfnGetProcessID;
if(MYPROC == NULL)
    throw;

double x = MYPROC(5.5,5);

【讨论】:

以上是关于使用 DLL 函数时的访问冲突异常的主要内容,如果未能解决你的问题,请参考以下文章

WindowsError:异常:使用从 C++ 到 Python 的 ctypes 创建 DLL 时出现访问冲突或 Windows 错误 193

DLL代理调用LoadLibrary导致异常:访问冲突读取位置0x00000250

javafx 致命错误异常访问冲突(问题框架:jfxmedia.dll)

ctypes,python3.8:OSError:异常:访问冲突写入0x00000000

来自 dll 的运行函数的访问冲突

将 std::vector 转换为数组然后 p/调用它会在编组期间导致 mscorlib.dll 中的访问冲突异常