将方法指针从 C# 传递到 Delphi DLL

发布于 2024-10-01 23:20:18 字数 995 浏览 4 评论 0原文

我在将字符串作为 PChar 传递到 Delphi 构建的 DLL 时遇到了一些问题,感谢 Jens Mühlenhoff 解决了它。

现在我有另一个问题 -

如果 Delphi 声明是常规类型过程,则在传递给 DLL 时我已成功回调 c# 方法,但如果 Delphi 声明是方法类型过程,我会收到“尝试读取或写入受保护的内存”错误。

我尝试搜索...

这是 Delphi 声明

TCallBack = procedure ( s : String) of object;stdcall;

C# 代码

[DllImport(
    "DLLTest.dll",
    CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Ansi,
    EntryPoint = "DLL_Test"
)]
public static extern void DLL_Test(IntPtr p, [MarshalAs(UnmanagedType.LPStr)] string Location, int AIntValue);

public delegate void MethodCallBackEvent(string s);
public event MethodCallBackEvent Info;

public void GetInfo(string s)
{
    MessageBox.Show("Info: " + s);
}

这是用作的

            Info = GetInfo; //or Info = new MethodCallBackEvent(GetInfo);
            IntPtr p = Marshal.GetFunctionPointerForDelegate(Info);

            DLL_Test(p, "location message", 10);

I've had some problems passing string as PChar to Delphi built DLL, and resolved it thanks to Jens Mühlenhoff.

Now I have another issue -

I've made successful callback of c# method when passed to DLL if the Delphi declaration is a regular type procedure, but if Delphi declaration is a method type procedure I get "Attempted to read or write protected memory" error.

I tried searching...

Here is Delphi declaration

TCallBack = procedure ( s : String) of object;stdcall;

Here is C# code

[DllImport(
    "DLLTest.dll",
    CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Ansi,
    EntryPoint = "DLL_Test"
)]
public static extern void DLL_Test(IntPtr p, [MarshalAs(UnmanagedType.LPStr)] string Location, int AIntValue);

public delegate void MethodCallBackEvent(string s);
public event MethodCallBackEvent Info;

public void GetInfo(string s)
{
    MessageBox.Show("Info: " + s);
}

used as

            Info = GetInfo; //or Info = new MethodCallBackEvent(GetInfo);
            IntPtr p = Marshal.GetFunctionPointerForDelegate(Info);

            DLL_Test(p, "location message", 10);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情场扛把子 2024-10-08 23:20:18

这是一个工作示例。 DllTest1 使用普通函数回调。 DllTest2 期望回调作为直接的 C# 函数指针(需要在 Delphi 端进行一些小修改),DllTest3 期望一个 Delphi 方法回调指针(需要在 C# 端进行一些小修改)。

// Delphi
library test;

uses
  SysUtils;

{$R *.res}

type
  TCallback = procedure (P: PChar); stdcall;
  TMethodCallback = procedure (P: PChar) of object; stdcall;

procedure DllTest1(Callback: TCallback; P: PChar; I: Integer); stdcall;
var
  S: string;
begin
  S := Format('DllTest1 ''%s'' %d', [P, I]);
  if Assigned(Callback) then
    Callback(PChar(S));
end;

procedure DllTest2(_Callback: Pointer; P: PChar; I: Integer); stdcall;
var
  Callback: TMethodCallback absolute _Callback;
  S: string;
begin
  S := Format('DllTest2 ''%s'' %d', [P, I]);
  if Assigned(Callback) then
    Callback(PChar(S));
end;

procedure DllTest3(Callback: TMethodCallback; P: PChar; I: Integer); stdcall;
var
  S: string;
begin
  S := Format('DllTest3 ''%s'' %d', [P, I]);
  if Assigned(Callback) then
    Callback(PChar(S));
end;

exports
  DllTest1,
  DllTest2,
  DllTest3;

begin
end.

// C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace DllTest
{
    class Program
    {
        public struct Method
        {
            public IntPtr code;
            public IntPtr data;
        }
        [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "DllTest1")]
        public static extern void DllTest1(IntPtr p, [MarshalAs(UnmanagedType.LPStr)] string s, int i);
        [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "DllTest2")]
        public static extern void DllTest2(IntPtr p, [MarshalAs(UnmanagedType.LPStr)] string s, int i);
        [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "DllTest3")]
        public static extern void DllTest3(Method m, [MarshalAs(UnmanagedType.LPStr)] string s, int i);

        public delegate void Callback([MarshalAs(UnmanagedType.LPStr)] string s);
        public delegate void MethodCallback(IntPtr self, [MarshalAs(UnmanagedType.LPStr)] string s);
        public static void ShowInfo(string s)
        {
            Console.WriteLine("Info: " + s);
        }
        public static void ShowMethodInfo(IntPtr self, string s)
        {
            Console.WriteLine("Info: " + s);
        }


        static void Main(string[] args)
        {
            Method m;
            Callback info = ShowInfo;
            MethodCallback methodInfo = ShowMethodInfo;
            IntPtr p = Marshal.GetFunctionPointerForDelegate(info);
            IntPtr pm = Marshal.GetFunctionPointerForDelegate(methodInfo);

            // function callback example
            DllTest1(p, "test", 42);
            // method callback example 1
            DllTest2(pm, "test", 42);
            // method callback example 2
            m.code = pm;
            m.data = IntPtr.Zero;
            DllTest3(m, "test", 42);
        }
    }
}

Here is a working example. DllTest1 is using a normal function callback. DllTest2 expects the callback as a direct C# function pointer (requires a small hack on the Delphi side), and DllTest3 expects a Delphi method callback pointer (requires a small hack on the C# side).

// Delphi
library test;

uses
  SysUtils;

{$R *.res}

type
  TCallback = procedure (P: PChar); stdcall;
  TMethodCallback = procedure (P: PChar) of object; stdcall;

procedure DllTest1(Callback: TCallback; P: PChar; I: Integer); stdcall;
var
  S: string;
begin
  S := Format('DllTest1 ''%s'' %d', [P, I]);
  if Assigned(Callback) then
    Callback(PChar(S));
end;

procedure DllTest2(_Callback: Pointer; P: PChar; I: Integer); stdcall;
var
  Callback: TMethodCallback absolute _Callback;
  S: string;
begin
  S := Format('DllTest2 ''%s'' %d', [P, I]);
  if Assigned(Callback) then
    Callback(PChar(S));
end;

procedure DllTest3(Callback: TMethodCallback; P: PChar; I: Integer); stdcall;
var
  S: string;
begin
  S := Format('DllTest3 ''%s'' %d', [P, I]);
  if Assigned(Callback) then
    Callback(PChar(S));
end;

exports
  DllTest1,
  DllTest2,
  DllTest3;

begin
end.

// C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace DllTest
{
    class Program
    {
        public struct Method
        {
            public IntPtr code;
            public IntPtr data;
        }
        [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "DllTest1")]
        public static extern void DllTest1(IntPtr p, [MarshalAs(UnmanagedType.LPStr)] string s, int i);
        [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "DllTest2")]
        public static extern void DllTest2(IntPtr p, [MarshalAs(UnmanagedType.LPStr)] string s, int i);
        [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "DllTest3")]
        public static extern void DllTest3(Method m, [MarshalAs(UnmanagedType.LPStr)] string s, int i);

        public delegate void Callback([MarshalAs(UnmanagedType.LPStr)] string s);
        public delegate void MethodCallback(IntPtr self, [MarshalAs(UnmanagedType.LPStr)] string s);
        public static void ShowInfo(string s)
        {
            Console.WriteLine("Info: " + s);
        }
        public static void ShowMethodInfo(IntPtr self, string s)
        {
            Console.WriteLine("Info: " + s);
        }


        static void Main(string[] args)
        {
            Method m;
            Callback info = ShowInfo;
            MethodCallback methodInfo = ShowMethodInfo;
            IntPtr p = Marshal.GetFunctionPointerForDelegate(info);
            IntPtr pm = Marshal.GetFunctionPointerForDelegate(methodInfo);

            // function callback example
            DllTest1(p, "test", 42);
            // method callback example 1
            DllTest2(pm, "test", 42);
            // method callback example 2
            m.code = pm;
            m.data = IntPtr.Zero;
            DllTest3(m, "test", 42);
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文