在传递给本机dll的c#回调中访问托管对象

在传递给本机dll的c#回调中访问托管对象,c#,delphi,pinvoke,C#,Delphi,Pinvoke,我试图将一个C#回调函数传递给一个本机dll。它工作正常,但我找不到访问回调方法的父对象的方法。下面的代码演示了我要做的事情: class MyForm: Form { public delegate void CallbackDelegate(IntPtr thisPtr); [DllImport("mylib.dll", CallingConvention = CallingConvention.StdCall)] public static extern void Test(

我试图将一个C#回调函数传递给一个本机dll。它工作正常,但我找不到访问回调方法的父对象的方法。下面的代码演示了我要做的事情:

class MyForm: Form {
  public delegate void CallbackDelegate(IntPtr thisPtr);

  [DllImport("mylib.dll", CallingConvention = CallingConvention.StdCall)]
  public static extern void Test(CallbackDelegate callback);

  int Field;

  static void Callback(IntPtr thisPtr)
  {
      // I need to reference class field Field here.
      MyForm thisObject = ?MagicMethod?(thisPtr);

      thisObject.Field = 10;
  }

  void CallExternalMethod()
  {
      Test(Callback);
  }
}
我尝试获取
this
的指针,但得到以下异常:“对象包含非基本数据或不可blittable数据。”。我可能应该提到父对象是WindowsForms表单

更新

dll是用Delphi编写的,测试函数的签名如下:

type
  TCallback = procedure of object; stdcall;

procedure Test(Callback: TCallback); stdcall;
当我试图用以下代码获取指向类的指针时,我收到了上述错误消息:

var ptr = GCHandle.Alloc(this, GCHandleType.Pinned).AddrOfPinnedObject();

您应该使用
System.Runtime.InteropServices.GCHandle
类。它涉及使用其静态方法从C#引用到非托管值以及从非托管值转换

我没有快速检查的方法,但类似的方法应该可以:

应按如下方式获取
IntPtr

IntPtr thisPtr = GCHandle.ToIntPtr(GCHandle.Alloc(thisObject)));
type
  TCallback = procedure; stdcall;
void Callback()
{
    // an instance method, so *this* is available
}
void CallExternalMethod()
{
    Test(Callback);
}

PS:请注意,在某些情况下,您必须释放被调用方或调用方中的句柄。

您应该使用
System.Runtime.InteropServices.GCHandle
类。它涉及使用其静态方法从C#引用到非托管值以及从非托管值转换

我没有快速检查的方法,但类似的方法应该可以:

应按如下方式获取
IntPtr

IntPtr thisPtr = GCHandle.ToIntPtr(GCHandle.Alloc(thisObject)));
type
  TCallback = procedure; stdcall;
void Callback()
{
    // an instance method, so *this* is available
}
void CallExternalMethod()
{
    Test(Callback);
}

PS:请注意,在某些时候,您必须释放被调用方或调用方中的句柄。

您考虑得太多了。C#编组将在您的代理周围产生一个响声。您不得试图这样做

在Delphi端,这样写:

IntPtr thisPtr = GCHandle.ToIntPtr(GCHandle.Alloc(thisObject)));
type
  TCallback = procedure; stdcall;
void Callback()
{
    // an instance method, so *this* is available
}
void CallExternalMethod()
{
    Test(Callback);
}
只需删除对象的

在C方,您的代表是:

public delegate void CallbackDelegate();
用于代理的方法如下所示:

IntPtr thisPtr = GCHandle.ToIntPtr(GCHandle.Alloc(thisObject)));
type
  TCallback = procedure; stdcall;
void Callback()
{
    // an instance method, so *this* is available
}
void CallExternalMethod()
{
    Test(Callback);
}
然后调用本机代码,如下所示:

IntPtr thisPtr = GCHandle.ToIntPtr(GCHandle.Alloc(thisObject)));
type
  TCallback = procedure; stdcall;
void Callback()
{
    // an instance method, so *this* is available
}
void CallExternalMethod()
{
    Test(Callback);
}
那么,让我们把它们放在一起。在Delphi端,您可以编写:

library MyLib;

type
  TCallback = procedure; stdcall;

procedure Test(Callback: TCallback); stdcall;
begin
  Callback;
end;

exports
  Test;

begin
end.
在C#端:


你想得太多了。C#编组将在您的代理周围产生一个响声。您不得试图这样做

在Delphi端,这样写:

IntPtr thisPtr = GCHandle.ToIntPtr(GCHandle.Alloc(thisObject)));
type
  TCallback = procedure; stdcall;
void Callback()
{
    // an instance method, so *this* is available
}
void CallExternalMethod()
{
    Test(Callback);
}
只需删除对象的

在C方,您的代表是:

public delegate void CallbackDelegate();
用于代理的方法如下所示:

IntPtr thisPtr = GCHandle.ToIntPtr(GCHandle.Alloc(thisObject)));
type
  TCallback = procedure; stdcall;
void Callback()
{
    // an instance method, so *this* is available
}
void CallExternalMethod()
{
    Test(Callback);
}
然后调用本机代码,如下所示:

IntPtr thisPtr = GCHandle.ToIntPtr(GCHandle.Alloc(thisObject)));
type
  TCallback = procedure; stdcall;
void Callback()
{
    // an instance method, so *this* is available
}
void CallExternalMethod()
{
    Test(Callback);
}
那么,让我们把它们放在一起。在Delphi端,您可以编写:

library MyLib;

type
  TCallback = procedure; stdcall;

procedure Test(Callback: TCallback); stdcall;
begin
  Callback;
end;

exports
  Test;

begin
end.
在C#端:



mylib.dll中的本机测试方法是如何声明的?我不是要定义,只是要方法的签名。你能提供更多的代码吗?@David Heffernan我实际上是在编代码,而不是复制原始代码以避免不相关的细节。我想我写了所有相关的代码。缺少什么?我有一些p/invoke interop到Delphi的经验。和回调。但我完全不知道你想做什么。我希望看到一个完整的程序来演示这个错误。p/invoke部分工作得非常好。我不知道如何从静态回调方法引用MyForm实例的字段。当我尝试使用
GCHandle.Alloc(这个,GCHandleType.pinted)
获取指向MyForm实例的指针时,会引发异常。但我不确定这是否是做我想做的事情的正确方法。我已经更新了代码。您在mylib.dll中的本机测试方法是如何声明的?我不是要定义,只是要方法的签名。你能提供更多的代码吗?@David Heffernan我实际上是在编代码,而不是复制原始代码以避免不相关的细节。我想我写了所有相关的代码。缺少什么?我有一些p/invoke interop到Delphi的经验。和回调。但我完全不知道你想做什么。我希望看到一个完整的程序来演示这个错误。p/invoke部分工作得非常好。我不知道如何从静态回调方法引用MyForm实例的字段。当我尝试使用
GCHandle.Alloc(这个,GCHandleType.pinted)
获取指向MyForm实例的指针时,会引发异常。但我不确定这是否是做我想做的事情的正确方法。我已经更新了代码。我已经更新了我的问题。您能提供执行此操作的代码吗?我试图使用这个类,但没有成功。@Max我没有快速的方法来测试它是否有效,但这只是一般的想法。这就是我正在做的,只是我使用了
GCHandleType.pinted
参数。我认为不允许将未固定的指针传递给本机代码。您的主要问题是使用
addrofPindedObject
而不是
ToIntPtr
GCHandle
不是一个原始指针,它更像是一个cookie,用于重建托管指针(并在对象上维护一个“人工”引用,因此它不是GC的)。我已经更新了我的问题。您能提供执行此操作的代码吗?我试图使用这个类,但没有成功。@Max我没有快速的方法来测试它是否有效,但这只是一般的想法。这就是我正在做的,只是我使用了
GCHandleType.pinted
参数。我认为不允许将未固定的指针传递给本机代码。您的主要问题是使用
addrofPindedObject
而不是
ToIntPtr
GCHandle
不是一个原始指针,它更像是一个cookie,用于重建托管指针(并在对象上维护一个“人工”引用,因此它不是GC的)。哇!这真的很奇怪。所以这个ptr就像一个虚拟参数。非常感谢你!这确实有效。您需要删除两侧的thisPtr。让马歇尔帮你敲打它。关于这个话题,这里还有一个问题:我忘了提到的一件事是,我不能从delphi declarion中删除对象的
,因为它也被用于其他部分。但是你的编辑前解决方案很好,这是个问题。