C++ 库问题

Posted

技术标签:

【中文标题】C++ 库问题【英文标题】:C++ library issue 【发布时间】:2011-03-02 01:46:44 【问题描述】:

我正在尝试从我的 C# 代码调用 C++ 库。我还有一个用 C++ 编写的示例应用程序,它调用相同的库,并且工作正常。但是,来自 C# 的调用会引发错误:

试图读取或写入受保护的内存。这通常表明其他内存已损坏。

c#代码为:

#region DllImports

[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
static extern int LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);

[DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

[DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
static extern bool FreeLibrary(int hModule);

[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
delegate string bondProbeCalc(string licenseFolder, string requestString);

#endregion


/// <summary>
/// Calls Bondprobe to calculate a formula
/// </summary>
internal static string DoBondProbeOperation(string requestString)

    if (string.IsNullOrEmpty(requestString)) throw new ArgumentNullException();

    //Reference library
    int hModule = LoadLibrary(ConfigurationManager.Instance.BondProbeSettings.AssemblyFilePath);

    if (hModule != 0)
    
        IntPtr intPtr = GetProcAddress(hModule, "bpStringCalc");

        bondProbeCalc funcDelegate = (bondProbeCalc)Marshal.GetDelegateForFunctionPointer(intPtr, typeof(bondProbeCalc));

        requestString = requestString.EndsWith("=") ? requestString.Substring(0, requestString.Length - 1) : requestString;

        string returnValue = funcDelegate(ConfigurationManager.Instance.BondProbeSettings.LicenseFilePath, requestString);

        FreeLibrary(hModule);

        return returnValue;
    

    return string.Empty;

此完全相同的代码适用于某些计算机,但会在其他计算机上引发错误。但是,示例 C++ 应用程序似乎可以在任何地方运行。

有什么想法吗?

谢谢,贡萨洛

【问题讨论】:

我推荐使用IntPtr而不是int(对于所有指针,这包括来自LoadLibrary的返回类型) 感谢您的建议,但很遗憾这不起作用。 究竟是哪个函数导致了崩溃?全部? @Sam Skuce 不知道你的意思,我只是在 c++ 程序集中调用一个函数。 【参考方案1】:

我猜崩溃发生在运行 64 位操作系统的计算机上。我不知道您的 DoBondProbeOperation 逻辑,但您的 PInvoke 方法应定义如下:

[DllImport("kernel32.dll", CharSet = CharSet.Auto]
static extern IntPtr LoadLibrary(string lpFileName);

[DllImport("kernel32.dll"]
static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

[DllImport("kernel32.dll"]
static extern bool FreeLibrary(IntPtr hModule);

在哪里比较 hModule0,而不是比较 IntPtr.Zero

编辑:修复了LoadLibraryCharSet

EDIT2:也尝试将您的 bondProbeCalc 定义更改为以下内容:

[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
delegate string bondProbeCalc(string licenseFolder, string requestString);

或者:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate [MarshalAs(UnmanagedType.LPStr)] string bondProbeCalc([MarshalAs(UnmanagedType.LPStr)] string licenseFolder, [MarshalAs(UnmanagedType.LPStr)] string requestString);

【讨论】:

不幸的是,这不起作用。这是 c++ 函数的签名: extern char *bpStringCalc(char *bpDirectory, char *issString); @Gonzalo 如果本机函数确实是 C++ 而不是 C,那么在该签名中缺少 extern "C" 将意味着函数导出的名称实际上不是 bpStringCalc,而是一些损坏的姓名。本机代码实际上是 C,还是在此处粘贴签名时省略了 extern "C" @Gonzalo 我已经编辑了我的帖子,对您的bondProbeCalc 代表提出了一些建议。【参考方案2】:

Comments on Marshal.GetDelegateForFunctionPointer 表示不支持 Stdcall 以外的调用约定,即在您的委托类型上指定 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 无效。如果确实如此,您需要切换到使用 PInvoke 调用 bpStringCalc,重新编译本机库以使用 __stdcall 修饰符导出函数,或者在 C++/CLI 中编写一个 shim(因为 IJW 互操作会没有这个调用约定限制)。

【讨论】:

我记得,x64 CLR 对具有不匹配的 Cdecl/Stdcall 调用约定的封送函数调用具有自动堆栈更正; x86 CLR 没有。因此,遇到崩溃的计算机可能正在运行 x86 操作系统。无论如何,您的 PInvoke 签名之前肯定是错误的,应该更正,即使这样做没有明显效果。

以上是关于C++ 库问题的主要内容,如果未能解决你的问题,请参考以下文章

C#总结调用C++动态库

手把手写C++服务器:C++编译常见问题编译优化方法C++库发布方式

手把手写C++服务器:C++编译常见问题编译优化方法C++库发布方式

从 c# 调用 c++ 库

通过继承扩展 C++ 标准库?

想【C++ 】高手发起挑战,请教一个【动态链接库 dll 和 类成员函数 显式链接】问题