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

时间:2013-04-18 18:52:35

标签: 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(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编写的,Test函数的签名如下:

type
  TCallback = procedure of object; stdcall;

procedure Test(Callback: TCallback); stdcall;

当我尝试使用以下代码获取指向类的指针时,我收到了上述错误消息:

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

2 个答案:

答案 0 :(得分:3)

你在想这个。 C#编组将在您的代理周围创建一个thunk。你不能试图这样做。

在Delphi方面写这样:

type
  TCallback = procedure; stdcall;

只需删除of object

在C#端,您的代表是:

public delegate void CallbackDelegate();

您用于代理的方法如下所示:

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#方面:

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

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

    int Field;

    static void Callback()
    {
        Field = 10;
    }

    void CallExternalMethod()
    {
        Field = 0;
        Test(Callback);
        Debug.Assert(Field == 10);
    }
}

答案 1 :(得分:0)

您应该使用System.Runtime.InteropServices.GCHandle课程its MSDN documentation has examples。它涉及使用其静态方法从C#引用转换为非托管值和从非托管值转换。

我没有快速检查方法,但这样的事情应该有效:

static void Callback(IntPtr thisPtr)
{
    MyForm thisObject = (MyForm)GCHandle.FromIntPtr(thisPtr).Target;

    thisObject.Field = 10;
}

应该像这样获得IntPtr

IntPtr thisPtr = GCHandle.ToIntPtr(GCHandle.Alloc(thisObject)));

PS:请注意,在某些时候,您必须在被叫方或来电者中释放句柄。