如何使用正确的参数类型从 C# 正确调用C++ DLL

本文关键字:调用 C++ DLL 参数 何使用 类型 | 更新日期: 2023-09-27 17:56:02

我得到了一个DLL,由C#等人调用。DLL 包含两个方法,如下所示

extern "C" {
   __declspec(dllexport) BSTR GroupInit(LPCTSTR bstrIniFile, bool bDiagErr, bool bProcErr);
   __declspec(dllexport) void G();
} 
class GrouperServer {
public:
   BSTR GroupInit(LPCTSTR bstrIniFile, bool bDiagErr, bool bProcErr);
   void G();
}
BSTR GrouperServer::GroupInit(LPCTSTR bstrIniFile, bool bDiagErr, bool bProcErr) {
   CString strResult = "";
   char* sz;
   SetVars(bDiagErr, bProcErr);
   if (sz = ::GroupInit((char*)bstrIniFile, 1))
      strResult = sz;
   return strResult.AllocSysString();
}
void G() {
   MessageBox(0, "And text here", "MessageBox caption", MB_OK);
}

我尝试通过首先定义类从 C# 调用这些 DLL:

public class GrouperServer {
    [DllImport("GrouperServer.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void G();
    [DllImport("GrouperServer.dll", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.BStr)]
    public static extern string GroupInit(
        string strCmdFile, bool bAllowBadDiagCodes, bool bAllowBadProcCodes);
}

和做

this.strCommandFilePath = "C:''MyDir''MyCommandFile.txt";
Grouper.GrouperServer.G();
Grouper.GrouperServer.GroupInit(this.strCommandFilePath, true, true);

对方法G()的调用有效,我收到一个消息框,但是对于对GroupInit()的调用,我得到

DrGroupIN.exe 中发生了类型为"System.EntryPointNotFoundException"的未处理异常。其他信息:无法在 DLL "GrouperServer.dll"中找到名为"GroupInit"的入口点。

在这种情况下,如何使用正确的参数调用第二种方法GrouInit(...)


编辑 1.

我也试过

[DllImport("GrouperServer.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GroupInit(
    string strCmdFile, bool bAllowBadDiagCodes, bool bAllowBadProcCodes);

这称为通过:

IntPtr ptr = Grouper.GrouperServer.GroupInit(this.strCommandFilePath, true, true);
string strString = Marshal.PtrToStringBSTR(ptr);
Marshal.FreeBSTR(ptr);

但这也抛出了上面的错误。

编辑 2.

我也试过

[DllImport("GrouperServer.dll", CallingConvention = CallingConvention.Cdecl, CharSet=CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GroupInit(
    [MarshalAs(UnmanagedType.LPTStr)]string strCmdFile, 
    bool bAllowBadDiagCodes, 
    bool bAllowBadProcCodes);

但这也抛出了上面的错误。

如何使用正确的参数类型从 C# 正确调用C++ DLL

在我看来

,您似乎无法使用 C# 调用此 DLL。DLL 导出类的成员函数。并且您无法实例化该类。

我可以看到以下选项:

  1. 要求 DLL 供应商导出静态成员函数或非成员函数。然后应该可以使用 p/invoke 访问这些。
  2. 围绕 DLL 编写混合模式 C++/CLI 包装器。此包装器可以轻松使用非托管 DLL。反过来,它可以公开包装功能的托管 ref 类。然后,可以将 C++/CLI 包装器添加为 C# 项目的引用。

也就是说,这些声明似乎相互冲突:

extern "C" {
   __declspec(dllexport) BSTR GroupInit(LPCTSTR bstrIniFile, bool bDiagErr, bool bProcErr);
   __declspec(dllexport) void G();
} 
class GrouperServer {
public:
   BSTR GroupInit(LPCTSTR bstrIniFile, bool bDiagErr, bool bProcErr);
   void G();
}

第一对声明似乎是非成员函数。但是它们后面跟着一个具有相同名称的成员函数的类。您确实需要清楚您尝试调用哪些函数。

也许 DLL 已包含包装成员函数的非成员函数。在这种情况下,您只需要找出导出它们的名称。使用依赖沃克来做到这一点。

那么,如何声明 p/invoke。您需要知道正在使用的字符集以及导出函数的名称。让我们假设 Unicode。p/调用将是:

[DllImport("GrouperServer.dll", CallingConvention = CallingConvention.Cdecl,
    EntryPoint = "<exported name goes here>")]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GroupInit(
    [MarshalAs(UnmanagedType.LPWStr)]
    string strCmdFile, 
    bool bAllowBadDiagCodes, 
    bool bAllowBadProcCodes
);