2

我正在尝试将 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();
4

2 回答 2

4

你想多了。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);
}

所以,让我们把它们放在一起。在德尔福方面,您可以编写:

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);
    }
}
于 2013-04-18T22:25:46.197 回答
0

您应该使用System.Runtime.InteropServices.GCHandle该类,它的 MSDN 文档有示例。它涉及使用其静态方法在 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:请注意,在某些时候,您必须在被调用者或调用者中释放句柄。

于 2013-04-18T19:03:47.217 回答