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

时间:2022-09-01 17:10:07

I have been provided with a DLL which is to be called by C# et al. The DLL contains two methods as follows

我已经得到了一个DLL,它是由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();
}

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);
}

I am attempting to call these DLLs from C# by first defining the class:

我试图通过定义这个类来调用c#中的DLLs:

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);
}

and doing

和做

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

The call to method G() works and I get a message box, but for the call to GroupInit(), I get

对方法G()的调用工作,我得到一个消息框,但是对于GroupInit()的调用,我得到。

An unhandled exception of type 'System.EntryPointNotFoundException' occurred in DrGroupIN.exe. Additional information: Unable to find an entry point named 'GroupInit' in DLL 'GrouperServer.dll'.

一个未处理的类型系统异常。发生在DrGroupIN.exe EntryPointNotFoundException”。附加信息:无法在DLL的GrouperServer.dll中找到名为“GroupInit”的入口点。

How can I call the second method GrouInit(...) with the correct parameters in this case?

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


Edit 1.

编辑1。

I have also tried

我也试过

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

where this is called via:

这是通过:

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

But this too throws the error above.

但这也会导致上述错误。

Edit 2.

编辑2。

I have also tried

我也试过

[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);

But this too throws the error above.

但这也会导致上述错误。

1 个解决方案

#1


2  

It looks to me as though you cannot call this DLL using C#. The DLL exports member functions of a class. And you cannot instantiate that class.

在我看来,你不能用c#来调用这个DLL。DLL导出一个类的成员函数。不能实例化该类。

I can see the following options:

我可以看到以下选项:

  1. Ask the DLL vendor to export either static member functions, or non-member functions. These should then be accessible using p/invoke.
  2. 要求DLL供应商导出静态成员函数或非成员函数。这些应该可以使用p/invoke访问。
  3. Write a mixed mode C++/CLI wrapper around the DLL. This wrapper can readily consume the unmanaged DLL. In turn it can then expose a managed ref class that wraps the functionality. The C++/CLI wrapper can then be added as a reference to the C# project.
  4. 在DLL中编写一个混合模式c++ /CLI包装器。这个包装器可以很容易地使用非托管DLL。然后,它可以公开一个托管的ref类来包装这个功能。然后可以将c++ /CLI包装器添加到c#项目中。

That said, these declarations seem to be in conflict:

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

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();
}

The first pair of declarations seem to be non-member functions. But they are followed by a class with member functions having the same names. You do need to be clear which functions you are attempting to call.

第一对声明似乎是非成员函数。但是它们后面是一个具有相同名称的成员函数的类。您需要明确您要调用的函数。

Perhaps the DLL already contains non-member functions that wrap up the member functions. In which case you just need to find out the names by which they are exported. Use Dependency Walker to do that.

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

So, how to declare the p/invoke. You need to know the character set in use, and the name by which the function is exported. Let's assume Unicode. The p/invoke would be:

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

[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
);

#1


2  

It looks to me as though you cannot call this DLL using C#. The DLL exports member functions of a class. And you cannot instantiate that class.

在我看来,你不能用c#来调用这个DLL。DLL导出一个类的成员函数。不能实例化该类。

I can see the following options:

我可以看到以下选项:

  1. Ask the DLL vendor to export either static member functions, or non-member functions. These should then be accessible using p/invoke.
  2. 要求DLL供应商导出静态成员函数或非成员函数。这些应该可以使用p/invoke访问。
  3. Write a mixed mode C++/CLI wrapper around the DLL. This wrapper can readily consume the unmanaged DLL. In turn it can then expose a managed ref class that wraps the functionality. The C++/CLI wrapper can then be added as a reference to the C# project.
  4. 在DLL中编写一个混合模式c++ /CLI包装器。这个包装器可以很容易地使用非托管DLL。然后,它可以公开一个托管的ref类来包装这个功能。然后可以将c++ /CLI包装器添加到c#项目中。

That said, these declarations seem to be in conflict:

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

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();
}

The first pair of declarations seem to be non-member functions. But they are followed by a class with member functions having the same names. You do need to be clear which functions you are attempting to call.

第一对声明似乎是非成员函数。但是它们后面是一个具有相同名称的成员函数的类。您需要明确您要调用的函数。

Perhaps the DLL already contains non-member functions that wrap up the member functions. In which case you just need to find out the names by which they are exported. Use Dependency Walker to do that.

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

So, how to declare the p/invoke. You need to know the character set in use, and the name by which the function is exported. Let's assume Unicode. The p/invoke would be:

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

[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
);