Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/271.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C+上设置C#回调+;通过P/Invoke获取的结构 我尝试使用一个外部的C++ DLL(我没有源码)。_C#_C++_.net_Interop_Dllimport - Fatal编程技术网

在C+上设置C#回调+;通过P/Invoke获取的结构 我尝试使用一个外部的C++ DLL(我没有源码)。

在C+上设置C#回调+;通过P/Invoke获取的结构 我尝试使用一个外部的C++ DLL(我没有源码)。,c#,c++,.net,interop,dllimport,C#,C++,.net,Interop,Dllimport,DLL有一个函数,返回指向结构的指针。 struct定义了一系列函数指针,由我的应用程序用作回调 根据我收到的“文档”,我只是通过设置指向自己回调方法的指针来“注册”回调,如下所示: server->OnConnectionRequest = &myObj.OnConnectionRequest; 然而,我正试图在C#中实现这一点。 我取得了部分成功。我可以: 加载动态链接库 从函数中获取struct*指针 调用对象上预定义的一些方法 我不能做的是在对象上设置我自己的回调:编

DLL有一个函数,返回指向
结构的指针。
struct
定义了一系列函数指针,由我的应用程序用作回调

根据我收到的“文档”,我只是通过设置指向自己回调方法的指针来“注册”回调,如下所示:

server->OnConnectionRequest = &myObj.OnConnectionRequest;
然而,我正试图在C#中实现这一点。 我取得了部分成功。我可以:

  • 加载动态链接库
  • 从函数中获取
    struct*
    指针
  • 调用对象上预定义的一些方法
我不能做的是在对象上设置我自己的回调:编译器没有抱怨,运行时没有抱怨,但是回调仍然没有被调用

我这样定义了委托类型和类(注意,之前它被定义为一个struct,但它的工作原理不同):

我有一个静态助手从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;
        }
    }
}
然而,当我使用客户端应用程序尝试连接到服务器时,我的回调永远不会运行。如您所见,在我的回调中,我拒绝连接,因此客户端应该退出并出现错误,但它没有

我做错了什么?

这个:

_instance = Marshal.PtrToStructure<RemoteServerPluginI>(ptr);
其中,您应该将代理指针的偏移量放在
结构中,而不是
0

那么你没有给我们看回调的C签名。。。也许你在那里也犯了一些错误

正如Voigt所写,另一件非常重要的事情是,委托必须在本机库可以使用它的所有时间内保持活动状态。执行此操作的标准方法是将其放在对象的字段/属性中,然后确保对象处于活动状态(例如保留对它的引用)。您使用的是
类RemoteServerPluginI
。另一种方法是
GCHandle.Alloc(您的委托,GCHandleType.Normal)
然后
GCHandle.Free()

一些简单的示例代码

丙方:

extern "C"
{
    typedef struct _RemoteServerPluginI
    {
        void(*OnRemoteConnectionRequest)(void *caller, wchar_t *ip, int *accept);
        void(*StartServer)(void);
    } RemoteServerPluginI;

    void StartServer();

    RemoteServerPluginI _callbacks = { NULL, StartServer };

    void StartServer()
    {
        int accept = 0;
        _callbacks.OnRemoteConnectionRequest(NULL, L"127.0.0.1", &accept);
        wprintf(L"Accept: %d", accept);
    }

    __declspec(dllexport) RemoteServerPluginI* GetServerPluginInterface()
    {
        return &_callbacks;
    }
}
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetServerPluginInterface();

public static void RemoteConnectionRequestTest(IntPtr caller, string ip, ref int accept)
{
    Console.WriteLine("C#: ip = {0}", ip);
    accept = 1;
}

public class Callbacks
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void OnRemoteConnectionRequestDelegate(IntPtr caller, [MarshalAs(UnmanagedType.LPWStr)]string ip, ref int accept);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void StartServerDelegate();

    public OnRemoteConnectionRequestDelegate RemoteConnectionRequest { get; set; }

    public StartServerDelegate StartServer { get; set; }
}
C侧:

然后:

IntPtr rsp = GetServerPluginInterface();

var callbacks = new Callbacks
{
    RemoteConnectionRequest = RemoteConnectionRequestTest
};

Marshal.WriteIntPtr(rsp, 0, Marshal.GetFunctionPointerForDelegate(callbacks.RemoteConnectionRequest));
callbacks.StartServer = Marshal.GetDelegateForFunctionPointer<Callbacks.StartServerDelegate>(Marshal.ReadIntPtr(rsp, IntPtr.Size));

callbacks.StartServer();
然后

Callbacks callbacks = GetServerPluginInterface();
callbacks.RemoteConnectionRequest = RemoteConnectionRequestTest;
callbacks.StartServer();

while (true)
{
}
注意,然后我将使所有内容都强类型化,完全隐藏
IntPtr

[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern CallbacksPtr GetServerPluginInterface();

[StructLayout(LayoutKind.Sequential)]
public struct CallbacksPtr
{
    public IntPtr Ptr;
}

public class Callbacks
{
    public static implicit operator Callbacks(CallbacksPtr ptr)
    {
        return new Callbacks(ptr.Ptr);
    }

    private Callbacks(IntPtr ptr)
    {
        ...
}
添加一个
CallbacksPtr
,它是
IntPtr
的垫片,可以隐式转换为完整的
Callbacks
对象。

此:

_instance = Marshal.PtrToStructure<RemoteServerPluginI>(ptr);
其中,您应该将代理指针的偏移量放在
结构中,而不是
0

那么你没有给我们看回调的C签名。。。也许你在那里也犯了一些错误

正如Voigt所写,另一件非常重要的事情是,委托必须在本机库可以使用它的所有时间内保持活动状态。执行此操作的标准方法是将其放在对象的字段/属性中,然后确保对象处于活动状态(例如保留对它的引用)。您使用的是
类RemoteServerPluginI
。另一种方法是
GCHandle.Alloc(您的委托,GCHandleType.Normal)
然后
GCHandle.Free()

一些简单的示例代码

丙方:

extern "C"
{
    typedef struct _RemoteServerPluginI
    {
        void(*OnRemoteConnectionRequest)(void *caller, wchar_t *ip, int *accept);
        void(*StartServer)(void);
    } RemoteServerPluginI;

    void StartServer();

    RemoteServerPluginI _callbacks = { NULL, StartServer };

    void StartServer()
    {
        int accept = 0;
        _callbacks.OnRemoteConnectionRequest(NULL, L"127.0.0.1", &accept);
        wprintf(L"Accept: %d", accept);
    }

    __declspec(dllexport) RemoteServerPluginI* GetServerPluginInterface()
    {
        return &_callbacks;
    }
}
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetServerPluginInterface();

public static void RemoteConnectionRequestTest(IntPtr caller, string ip, ref int accept)
{
    Console.WriteLine("C#: ip = {0}", ip);
    accept = 1;
}

public class Callbacks
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void OnRemoteConnectionRequestDelegate(IntPtr caller, [MarshalAs(UnmanagedType.LPWStr)]string ip, ref int accept);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void StartServerDelegate();

    public OnRemoteConnectionRequestDelegate RemoteConnectionRequest { get; set; }

    public StartServerDelegate StartServer { get; set; }
}
C侧:

然后:

IntPtr rsp = GetServerPluginInterface();

var callbacks = new Callbacks
{
    RemoteConnectionRequest = RemoteConnectionRequestTest
};

Marshal.WriteIntPtr(rsp, 0, Marshal.GetFunctionPointerForDelegate(callbacks.RemoteConnectionRequest));
callbacks.StartServer = Marshal.GetDelegateForFunctionPointer<Callbacks.StartServerDelegate>(Marshal.ReadIntPtr(rsp, IntPtr.Size));

callbacks.StartServer();
然后

Callbacks callbacks = GetServerPluginInterface();
callbacks.RemoteConnectionRequest = RemoteConnectionRequestTest;
callbacks.StartServer();

while (true)
{
}
注意,然后我将使所有内容都强类型化,完全隐藏
IntPtr

[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern CallbacksPtr GetServerPluginInterface();

[StructLayout(LayoutKind.Sequential)]
public struct CallbacksPtr
{
    public IntPtr Ptr;
}

public class Callbacks
{
    public static implicit operator Callbacks(CallbacksPtr ptr)
    {
        return new Callbacks(ptr.Ptr);
    }

    private Callbacks(IntPtr ptr)
    {
        ...
}

添加一个
CallbacksPtr
,它是
IntPtr
的垫片,可以隐式转换为完整的
Callbacks
对象。

。还有一件更重要的事情需要注意,当使用
GetFunctionPointerForDelegate
时,必须有一些东西使委托实例保持活动状态,因为当它变得不可访问时,蹦床将随之释放。@BenVoigt我正要添加它。。。但是它有一个充当代理容器的
RemoteServerPluginI
类,所以他走的路是对的。在删除有害副本(
PtrToStructure
call)之后,他就不会有一个了@BenVoigt和我想的一样:cry:@MicheleIppolito:更改
GetServerPluginInterface
的返回类型可能会起作用,但是您的类与C结构不兼容,因此它没有您想象的那么有用。(委托类型的字段与具有与委托相同签名的函数指针不同——两者都是指针,但委托是指向数据对象的指针,而不是直接指向代码的指针)您无法避免调用
GetFunctionPointerForDelegate
。我只是想对副本和将委托转换为函数指针的失败进行注释——您已经解释了这两个问题。还有一件更重要的事情需要注意,当使用
GetFunctionPointerForDelegate
时,必须有一些东西使委托实例保持活动状态,因为当它变得不可访问时,蹦床将随之释放。@BenVoigt我正要添加它。。。但是它有一个充当代理容器的
RemoteServerPluginI
类,所以他走的路是对的。在删除有害副本(
PtrToStructure
call)之后,他就不会有一个了@BenVoigt和我想的一样:cry:@MicheleIppolito:更改
GetServerPluginInterface
的返回类型可能会起作用,但是您的类与C结构不兼容,因此它没有您想象的那么有用。(委托类型的字段与签名与委托相同的函数指针不同——两者都是指针,但委托是指向数据对象的指针,而不是直接指向代码)您无法避免调用
GetFunctionPointerForDelegate
。数据流错误,您必须告诉本机代码有关您的调用B