2015-05-04 132 views
2

我有一個以下的DLL源代碼。從C調用Delphi函數#

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)返回值。

我不明白。

我的錯在哪裏?

謝謝你這麼多

回答

6
[return: MarshalAs(UnmanagedType.AnsiBStr)] 

這是不對的。你沒有返回一個ANSI編碼的字符串,在COM堆上分配。您正在返回一個普通的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); 
+0

非常感謝您的回覆和關注。 @David Heffernan 現在我明白了邏輯,我會嘗試。 –

+0

Hi @David Heffernan 如何實現該類型TByteArray = Byte數組;在C#中? –

+0

這是一個字節數組。 'C#中的byte []'。Delphi字節數組不能用於互操作。 –

1

是德爾福的DLL可見爲COM Interop C#代碼?如果沒有,最簡單的方法是使用「Add Existing Item」菜單選項將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); 
+2

這裏沒有COM。那麼,有一個非託管函數返回一個COM接口。但爲什麼需要註冊? –

+0

@DavidHeffernan我的不好。我試圖確定Delphi中的DLL是否可以通過ComVisible屬性使得.NET dll變得可見。但我會糾正並編輯答案以反映我的真正含義。 –