2013-12-16 63 views
1

我想從C#VS2013程序中使用Pinvoke來訪問現有的DelphiXE2 dll程序。 Delphi程序需要一個xml文件和一個IB數據庫文件,並根據xmlfile更新數據庫。 DelphiXE2 dll程序成功從另一個Delphi程序中調用。在C#中,我試圖調用GETxml程序。我得到一個{「外部組件引發異常。」}錯誤。嘗試從VS2013調用DELPHI XE2 DLL錯誤C#程序

因爲我是C#和DELPHI的新手,所以我需要語法檢查我正在嘗試做什麼。

C#

[DllImport("MSA.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, EntryPoint = "GetXML")] 

extern static int GetXML([MarshalAs(UnmanagedType.LPStr)] string a, [MarshalAs(UnmanagedType.LPStr)] string b, Boolean c); 

int retval = 0; 
retval = GetXML(txtPath.Text.ToString().Trim(), txtCabFile.Text.ToString().Trim(), false); 

現有的Delphi程序:(我注意到,打開數據庫未在此DLL函數發生事實上它是在調用的Delphi程序調用的getXML之前發生的不知道。這是一個問題或沒有,因爲我是從C#程序調用)

function GetXML(DatabasePath: pChar; OFileName: pChar; Silent: Boolean = True): Integer; stdcall; 
    var 
    xmlDatabase: TIBDatabase; 
    xmlTransaction: TIBTransaction; 
    objXML: TXML; 
    begin 
    Result := -1; 
    if FileExists(oFilename) then 
    begin 
    objXML := nil; 
    xmlDatabase := nil; 
try 
    xmlDatabase := TIBDatabase.Create(nil); 
    xmlTransaction := TIBTransaction.Create(xmlDatabase); 
    xmlTransaction.DefaultDatabase := xmlDatabase; 

    xmlDatabase.DatabaseName := DatabasePath; 
    xmlDatabase.Params.Add('user_name=SYSTASS'); 
    xmlDatabase.Params.Add('password=pswdf88'); 
    xmlDatabase.LoginPrompt := False; 
    xmlDatabase.DefaultTransaction := xmlTransaction; 
    xmlDatabase.Connected := True; 
    xmlTransaction.StartTransaction; 
    try 
    objXML := TXML.Create(XMLDatabase, IsSecGDB(XMLDatabase)); //uses IBQUERY to start transaction 
    objXML.XMLIntoDB(oFilename, Silent); 
    xmlTransaction.Commit; 
    result := 1; 
    except 
    on e: Exception do 
    begin 
     xmlTransaction.Rollback; 
     raise Exception.Create('GetXML Exception: ' + e.Message); 
    end; 
    end; 
finally 
    FreeAndNil(objXML); 
    if xmlDatabase <> nil then 
    xmlDatabase.Close; 
    FreeAndNil(xmlDatabase); 
end; 

末 其他 開始 提高Exception.Create(「文件名」 + oFilename +「參數沒有被發現。」)。 結束; 結束;

+0

爲什麼寫'int retval = 0; retval = GetXML(...);'?當然你的意思是'int retval = GetXML(...);' –

+0

是的,改變了它。謝謝。 –

回答

2

首先,我們來看看互操作邊界兩側的錯配。您正在從C#傳遞ANSI字符串,但Delphi代碼需要UTF-16。

[DllImport("MSA.dll", CallingConvention = CallingConvention.StdCall, 
    CharSet = CharSet.Unicode, EntryPoint = "GetXML")] 
extern static int GetXML(
    [MarshalAs(UnmanagedType.LPStr)] 
    string a, 
    [MarshalAs(UnmanagedType.LPStr)] 
    string b, 
    Boolean c 
); 

雖然你指定CharSet.Unicode然後你繼續告訴編組明確地使用UnmanagedType.LPStrPAnsiChar元帥。對於UTF-16,你可以使用UnmanagedType.LPWStr

但是,你的p/invoke是不必要的複雜。我會寫你的P/Invoke是這樣的:

[DllImport("MSA.dll", CharSet = CharSet.Unicode)] 
extern static int GetXML(string a, string b, bool c); 

注意CallingConvention.StdCall是默認的。這裏沒有必要指定EntryPoint,因爲它只是重複了函數名稱。自從您指定CharSet.Unicode以來,您不需要MarshalAs屬性。


現在,你面對的一個更大的問題是,你在整個互操作邊界上拋出原生的Delphi異常。這就是爲什麼的P/Invoke層反對,說:

外部組件引發的異常

過這個互操作邊界拋出異常是絕對不行的。別那樣做。你必須用錯誤代碼來處理舊派的錯誤。

+0

OMGOSH它工作!大衛你是最棒的。感謝您教我並幫助我瞭解我正在嘗試做什麼。就跨越邊界的異常而言,我確實看到了這個問題,需要將它與Delphi應用程序的管理者一起提出來。 –