【发布时间】:2018-06-12 13:22:56
【问题描述】:
我正在尝试使用我已获得的外部 C++ dll(我没有源代码)。
DLL 有一个函数,它返回指向struct 的指针。
struct 定义了一系列函数指针,供我的应用程序用作回调。
根据我收到的“文档”,我只是通过将指针设置为我自己的回调方法来“注册”我的回调,如下所示:
server->OnConnectionRequest = &myObj.OnConnectionRequest;
但是,我试图在 C# 中实现这一点。 我已经部分成功了。我可以:
- 加载 DLL;
- 从函数中获取
struct*指针; - 调用对象上预定义的一些方法。
我不能做的是在对象上设置自己的回调:编译器不抱怨,运行时不抱怨,但仍然没有调用回调。
我这样定义了委托类型和类(请注意,以前这被定义为结构,但它的工作方式不一样):
// Original C++ signature from the header:
// void (*OnRemoteConnectionRequest)(void *caller, const char *ip, int &accept);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void OnRemoteConnectionRequestDelegate(IntPtr caller, string ip, ref int accept);
[StructLayout(LayoutKind.Sequential)]
public class RemoteServerPluginI
{
public OnRemoteConnectionRequestDelegate OnRemoteConnectionRequest;
// another dozen callbacks omitted
}
我有一个静态助手来从 dll 中检索实例:
public static class RemoteControlPlugin
{
[DllImport("data/remoteplugin.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetServerPluginInterface();
private static RemoteServerPluginI _instance = null;
public static RemoteServerPluginI Instance
{
get
{
if (_instance != null)
return _instance;
var ptr = GetServerPluginInterface();
_instance = Marshal.PtrToStructure<RemoteServerPluginI>(ptr);
if (_instance == null)
throw new InvalidOperationException("Could not obtain the server instance");
return _instance;
}
}
}
最后,这是我用来注册回调的方式:
public class CallBacks
{
public CallBacks(RemoteServerPluginI server)
{
server.OnRemoteConnectionRequest = this.OnRemoteConnectionRequest;
}
public void OnRemoteConnectionRequest(IntPtr caller, string ip, ref int accept)
{
Console.WriteLine($"Remote connection request from {ip}");
// I try to force a reject to see an error on the client,
// but the client always connects successfully, implying
// we never get to run this
accept = 0;
}
}
static void Main()
{
var cb = new Callbacks(RemoteControlPlugin.Instance);
RemoteControlPlugin.Instance.StartServer();
}
然而,当我使用客户端应用程序尝试连接到我的服务器时,我的回调永远不会运行。如您所见,在我的回调中我拒绝连接,因此客户端应该退出并出现错误,但事实并非如此。
我做错了什么?
【问题讨论】:
-
数据流错了,你要告诉原生代码你的回调函数。所以 Marshal.StructureToPtr()。确保将结构的副本保存在静态变量中,它的委托对象不会被垃圾收集。
-
谢谢汉斯,但我不确定我们是否在同一页面上。该结构已由 C++ dll 分配,我应该在 他们 创建的实例上设置回调。除非我完全误解了你的答案?
-
您不是在“分配”一个结构,而是在更新它。我们无法猜测您是否还需要使用 Marshal.PtrToStructure() 来保留结构中您不想修改的现有字段。如果它只包含函数指针,则不会。
-
好吧,C++ dll 中的代码为结构找到了一些内存,它给了我指针。该结构仅包含函数指针:其中一些已由 dll 分配并指向我需要调用的代码,其他一些在我检索指针时为空,我需要将回调分配给它们。
标签: c# c++ .net interop dllimport