【问题标题】:Call Delphi Function From C#从 C# 调用 Delphi 函数
【发布时间】:2015-05-04 23:04:37
【问题描述】:

我有一个下面的 DLL 源代码。

library Project1;

uses
  System.SysUtils,
  System.Classes;

type

  IStringFunctions = interface
    ['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
    function GetMethodValueAsString():PAnsiChar; stdcall;
  end;

  TStringFunctions = class(TInterfacedObject, IStringFunctions)
  public
    function GetMethodValueAsString():PAnsiChar; stdcall;
  end;

{$R *.res}

function  TStringFunctions.GetMethodValueAsString():PAnsiChar; stdcall;
begin
  Result := 'test';
end;


procedure GetImplementation(out instance:IStringFunctions); stdcall; export;
begin
  instance := TStringFunctions.Create;
end;

exports GetImplementation;

begin
end.

我想在 C# 中这样使用

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    [ComVisible(true)]
    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("240B567B-E619-48E4-8CDA-F6A722F44A71")]
    public interface IStringFunctions
    {
        [MethodImplAttribute(MethodImplOptions.PreserveSig)]
        [return: MarshalAs(UnmanagedType.AnsiBStr)]
        string GetMethodValueAsString();
    }

    class Program
    {
        [DllImport("kernel32.dll", EntryPoint = "LoadLibrary", CallingConvention = CallingConvention.StdCall)]
        static extern int LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);

        [DllImport("kernel32.dll", EntryPoint = "GetProcAddress", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

        [DllImport("kernel32.dll", EntryPoint = "FreeLibrary", CallingConvention = CallingConvention.StdCall)]
        static extern bool FreeLibrary(int hModule);

        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        delegate void GetImplementation([MarshalAs(UnmanagedType.Interface)] out IStringFunctions instance);

        static void Main(string[] args)
        {
            const string dllName = "Project1.dll";
            const string functionName = "GetImplementation";

            int libHandle = LoadLibrary(dllName);
            if (libHandle == 0) throw new Exception(string.Format("Could not load library \"{0}\"", dllName));

            var delphiFunctionAddress = GetProcAddress(libHandle, functionName);
            if (delphiFunctionAddress == IntPtr.Zero) throw new Exception(string.Format("Can't find function \"{0}\" in library \"{1}\"", functionName, dllName));

            GetImplementation getImplementation = (GetImplementation)Marshal.GetDelegateForFunctionPointer(delphiFunctionAddress, typeof(GetImplementation));

            if (getImplementation != null)
            {
                IStringFunctions instance = null;
                getImplementation(out instance);

                if (instance != null)
                {
                    //!!! don't return value !!!!
                    String result = instance.GetMethodValueAsString();
                    Console.WriteLine(result);
                }
            }
            Console.ReadLine();
        }
    }
}

但是 instance.GetMethodValueAsString 方法不起作用。并退出代码。

我想在 c# 中使用 dll 函数(GetMethodValueAsString)的返回值。

我不明白。

我的错在哪里?

非常感谢

【问题讨论】:

    标签: c# delphi pinvoke delphi-xe2 marshalling


    【解决方案1】:
    [return: MarshalAs(UnmanagedType.AnsiBStr)]
    

    这是错误的。您没有返回在 COM 堆上分配的 ANSI 编码字符串。您正在返回一个纯 C 字符串,一个指向以空字符结尾的 ANSI 字符数组的指针。

    你的接口声明应该是:

    [MethodImplAttribute(MethodImplOptions.PreserveSig)]
    IntPtr GetMethodValueAsString();
    

    调用方法必须是这样的:

    IntPtr ptr = instance.GetMethodValueAsString();
    string result = Marshal.PtrToStringAnsi(ptr);
    

    当然,当您需要返回动态分配的字符串时,您的界面设计变得相当不切实际。您还需要导出释放器。处理这个问题的干净方法是使用BSTR。像这样:

    德尔福

    IStringFunctions = interface
      ['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
      procedure GetMethodValueAsString(out value: WideString); stdcall;
    end;
    

    C#

    [MethodImplAttribute(MethodImplOptions.PreserveSig)]
    void GetMethodValueAsString([MarshalAs(UnmanagedType.BStr)] out string result);
    

    【讨论】:

    • 非常感谢您的回复和关注。 @David Heffernan 现在我理解了逻辑。我会尝试。
    • 嗨@David Heffernan 如何实现该类型 TByteArray = 字节数组;在 C# 中?
    • 这是一个字节数组。 byte[] 在 C# 中。 Delphi 字节数组不能用于互操作。
    【解决方案2】:

    对于 COM 互操作的 C# 代码,该 Delphi DLL 是否可见?如果没有,最简单的方法是使用“添加现有项”菜单选项将 dll 附加到 C# 类库项目。然后在此 dll 的属性窗口中将“BuildAction”设置为“无”,将“复制到输出目录”设置为“始终复制”

    然后你可以在 C# 代码中做这样的事情。

    [DllImport("Project1.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public static extern string GetMethodValueAsString();
    

    然后你可以在任何需要调用该函数的地方进行

    var outputMessage = GetMethodValueAsString();
    Console.WriteLine(outputMessage);
    

    【讨论】:

    • 这里没有 COM。嗯,有一个返回 COM 接口的非托管函数。但是为什么需要注册呢?
    • @DavidHeffernan 我的错。我试图确定 Delphi 的 DLL 是否可以通过 ComVisible 属性使 .NET dll 可见。但我已更正并编辑了答案以反映我的真实意思。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-23
    • 2011-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多