首先 - 我知道如何调用非托管函数。我在非托管DLL中遇到了接口方法。我认为,编写包装器 - 就是我需要的。
请帮我提供真实的代码。
我有:
以下是.h文件中的一些代码(兴趣点 - DECLARE INTERFACE):
#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使用类型库导入
我需要什么:
我之前就是这样做的(它是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包含一些数据,但下一步是什么 - 如何描述后面的接口并调用其方法?
答案 0 :(得分:1)
我会创建一个C#等价的C接口,所有相应的函数都使用dll-imports。您甚至可以定义与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-pointer。调用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是什么类型,我们如何为它们获取适当的值,但我希望这至少可以让你开始解决这个问题。
答案 1 :(得分:1)
您应该编写一个托管C ++包装器来调用c ++函数,然后在.Net项目中使用。因为没有正常的方法来从.Net上调用非托管对象实例上的函数。