从C#调用Delphi函数
我有一个下面的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)的返回值。
我不明白。
哪里是我的错?
非常感谢
[return: MarshalAs(UnmanagedType.AnsiBStr)]
这是错的。 您没有返回在COM堆上分配的ANSI编码字符串。 您将返回一个普通的C字符串,一个指向以null结尾的ANSI字符数组的指针。
您的接口声明应该是:
[MethodImplAttribute(MethodImplOptions.PreserveSig)] IntPtr GetMethodValueAsString();
调用方法必须像这样:
IntPtr ptr = instance.GetMethodValueAsString(); string result = Marshal.PtrToStringAnsi(ptr);
当然,当您需要返回动态分配的字符串时,您的界面设计变得相当不切实际。 您还需要导出解除分配器。 解决这个问题的干净方法是使用BSTR
。 像这样:
delphi
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);
COM Interop的C#代码是否可以看到Delphi DLL? 如果没有,最简单的方法是使用“添加现有项”菜单选项将dll附加到C#类库项目。 然后在此dll的属性窗口中将“BuildAction”设置为None,将“Copy to Output Directory”设置为“Copy always”
然后你可以在C#代码中做这样的事情。
[DllImport("Project1.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] public static extern string GetMethodValueAsString();
然后,只要你需要调用该function,你就可以做到
var outputMessage = GetMethodValueAsString(); Console.WriteLine(outputMessage);