【问题标题】:C# wrapper for unmanaged DLL非托管 DLL 的 C# 包装器
【发布时间】:2011-10-30 09:43:29
【问题描述】:

首先 - 我知道如何调用非托管函数。我坚持使用非托管 dll 中的接口方法。我认为,编写包装器 - 是我所需要的。

请帮我写真正的代码。

我有:

  • 旧 DLL (C);
  • 2个头文件;
  • lib 文件(有什么用?)。
  • !没有来源!

这是 .h 文件中的一些代码(兴趣点 - 声明接口):

#ifdef  __BUILD_DLL
#define BLNETPASS_EXPORT __declspec(dllexport)
#else
#define BLNETPASS_EXPORT
#endif

#include "BlNetpassUid.h"
extern "C"
{
 BLNETPASS_EXPORT HRESULT WINAPI BlNetPassCreateA
                 (LPCSTR pHostName, const GUID *, VOID **);

 BLNETPASS_EXPORT HRESULT WINAPI BlNetPassCreateW
                 (LPCWSTR pHostName, const GUID *, VOID **);

 BLNETPASS_EXPORT HRESULT WINAPI CanBeUnload (DWORD dTimeout );


...

#ifdef  UNICODE
#define BlNetPassCreate BlNetPassCreateW
#else
#define BlNetPassCreate BlNetPassCreateA
#endif

#undef INTERFACE
#define INTERFACE INetPass
DECLARE_INTERFACE_ (INetPass, IUnknown)
{
// IUnknown methods

 STDMETHOD  (    QueryInterface)(REFIID, VOID **)   PURE;
 STDMETHOD_ (ULONG,      AddRef)()          PURE;
 STDMETHOD_ (ULONG,     Release)()          PURE;

// INetPass methods

 STDMETHOD  ( CreateNetPassEnum)(VOID **, REFIID cid,
                         REFIID iid)    PURE;
};

我认为它不是 COM 对象,我不能为此 dll 使用类型库导入

我需要什么:

  • 调用 BlNetPassCreateA;
  • 获取指向接口 INetPass 的指针;
  • 调用接口方法CreateNetPassEnum。

这就是我之前的做法(它是 PInvoke):

[DllImport("BlNetpassApi")] 
public static extern int BlNetPassCreateA(string pHostName, ref Guid GUID, out IntPtr rINetPass);

...
...

 IntPtr something; 
 Guid temp_guid = Guid.Empty;

 int temp_int = BlNetPassCreateA(null, ref temp_guid, out something);

好的! IntPtr 包含一些数据,但接下来是什么 - 我如何描述背后的接口并调用其方法?

【问题讨论】:

  • DECLARE_INTERFACE_ 用于声明 COM 接口(@98​​7654325@ 的存在也是一个很大的好处)。所以我认为它是一个 COM 对象。
  • 也许你是对的......这是第三方软件:他们不能给我 C# 示例,但他们告诉我 - 你可以编写包装器
  • 为什么dont you simply try to add reference to this dll? If its COM,它将与COM生成的包装器一起导入。如果没有,请继续思考:)
  • 它不是 COM 组件
  • 我认为这里有一个很好的建议msdn.microsoft.com/en-us/library/ms973872.aspx。对于一些复杂的非托管 API,您可以在 C++ 中创建一个简单的包装器,然后通过 PInvoke 从您的代码中调用它。甚至创建托管 C++ 库,然后从 C# 本地调用它。从您的示例看来,编写 C++ 包装器将是一项简单的任务。

标签: c# wrapper unmanaged


【解决方案1】:

您应该编写一个托管 C++ 包装器来调用 c++ 函数,然后在您的 .Net 项目中使用。因为没有正常的方法可以从 .Net 调用非托管对象实例上的函数。

【讨论】:

    【解决方案2】:

    我将创建一个与 C 接口等效的 C#,并为所有相应的函数提供 dll 导入。您甚至可以定义与 C 端的结构相对应的结构。我将如何做的一个例子:

        // This defines a custom datatype present in our dll.
        [StructLayout(LayoutKind.Sequential)]
        public struct MyDllStruct
        {
            int aValue;
    
            [MarshalAs(UnmanagedType.Bool)]
            bool anotherValue;
        }
    
        public static class MyDllInterface
        {
            // The function in the interface can be customized by instead             
            // specifying the EntryPoint in the DllImport-attribute.
            [DllImport("MyDll.dll", EntryPoint = "mydll_function")]
            public static extern int MyDllFunction(
                    int param1,
                // We want this to become a C-style boolean 
                // (false == 0, true != 0)
                    [MarshalAs(UnmanagedType.Bool)]
                    bool param2,
                // We want the dll to write to our struct, and to be  
                // able to get the data written to it back we need to 
                // specify the Out-attribute. If we were just feeding the 
                // parameter to the dll we could just use the In-attribute
                // instead, or combine them if we want to achieve both.
                    [Out, MarshalAs(UnmanagedType.LPStruct)]
                    MyDllStruct resultStruct
                );
    
            [DllImport("MyDll.dll")]
            // Sometime we need to do a little magic on our own so we let 
            // this function return an IntPtr, and not the struct.
            public static extern IntPtr get_a_pointer_struct();
    
            // By not defining the EntryPoint-parameter the application 
            // expects that the dll function is named just as the
            // C#-function declaration.
            [DllImport("MyDll.dll")]
            // Here we specify that we are expecting a char-array 
            // back from the function, so the application should
            // marshal it as such.
            [return: MarshalAs(UnmanagedType.LPStr)]
            public static extern string mydll_get_errormsg();
        }
    
        // This class "converts" the C-style functions
        // into something more similar to the common
        // way C# is usually written.
        public class MyDllWrapper
        {
            // Calls the MyDllInterface.MyDllFunction and returns
            // the resulting struct. If an error occured, an
            // exception is thrown.
            public MyDllStruct CallFunction(int param1, bool param2)
            {
                MyDllStruct result = new MyDllStruct();
    
                int errorCode = MyDllInterface.MyDllFunction(
                                    param1, param2, result);
    
                if (errorCode < 0)
                    throw new Exception(String.Format(
                        "We got an error with code {0}. Message: ", 
                        errorCode, MyDllInterface.mydll_get_errormsg()));
    
                return result;
            }
    
            // Gets a pointer to a struct, and converts it 
            // into a structure.
            public MyDllStruct GetStruct()
            {
                IntPtr structPtr = MyDllInterface.get_a_pointer_struct();
    
                return (MyDllStruct)Marshal.PtrToStructure(
                    structPtr, typeof(MyDllStruct));
            }
        }
    

    上面的代码可能有很多错误,而且封送处理很可能都是错误的。目的主要是展示我通常如何在 C# 中实现 C 接口的模式。

    编辑:我不太确定你的 C 代码是如何工作的,但是看看标题我会说你从调用 BlNetPassCreateA 中得到的 IntPtr 是你在随后调用接口时的 this 指针。调用 CreateNetPassEnum 可能会以这种方式完成:

        [DllImport("BlNetPassApi")]
        public static extern void CreateNetPassEnum(IntPtr this, int cid, int reffid);
    
        ...
        IntPtr something; 
        Guid temp_guid = Guid.Empty;
    
        int temp_int = BlNetPassCreateA(null, ref temp_guid, out something);
    
        CreateNetPassEnum(in something, 10, 10);
    

    我真的不知道 REFIID 是什么类型,我们如何为它们获取适当的值,但我希望这至少能让你开始解决问题。

    【讨论】:

    • 谢谢。在这种情况下,真正的代码更好!但是...在我的情况下,首先我需要调用静态函数(BlNetPassCreate),它将 OUT 参数作为指向接口的指针返回给我。我不明白如何将我现有的指针链接到 C# 接口实现对不起,如果我没有看到常见的东西(
    • 感谢您的耐心和帮助。我不能调用 CreateNetPassEnum 因为 - {"Can't find Entry point \"CreateNetPassEnum\" in DLL \"BlNetpassApi\".":""} 它在接口后面(((我给 3-d 方写了一封信...也许他们会说如何编写包装器 IntPtr something; Guid temp_guid = Guid.Empty; IntPtr something2; int temp_int = BlNetPassCreateA(null, ref temp_guid, out something); temp_int = CreateNetPassEnum(out something2, ref CLSID_MatchBook, ref IID_INetPassEnum );
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-30
    • 1970-01-01
    • 2011-04-27
    • 2011-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多