2011-03-14 76 views
2

我有一個Delphi單元,它使用{$L xxx}指令靜態鏈接一個C .obj文件。 C文件使用C++ Builder的命令行編譯器進行編譯。爲了滿足C文件的運行時庫依賴性(_assert,memmove等),我包括crtl單元Allen Bauer提到的here如何在C++ Builder項目中的Delphi單元中使用Crtl? (或鏈接到C++ Builder C運行時庫)

unit FooWrapper; 

interface 

implementation 

uses 
Crtl; // Part of the Delphi RTL 

{$L FooLib.obj} // Compiled with "bcc32 -q -c foolib.c" 

procedure Foo; cdecl; external; 

end. 

如果我在Delphi項目(.dproj)中編譯該單元,everthing可以正常工作。

如果我編譯單元在C++ Builder項目(.cbproj)失敗,出現錯誤:

[ILINK32 Error] Fatal: Unable to open file 'CRTL.OBJ' 

事實上,沒有一個crtl.obj文件中的RAD Studio的安裝文件夾。有一個.dcu,但沒有.pas。嘗試將crtdbg添加到uses子句(定義了_assert的C頭)時出現錯誤,它找不到crtdbg.dcu

如果我刪除使用條款,它,而不是失敗,並__assert_memmove都沒有發現錯誤。

因此,在C++ Builder項目中的Delphi單元中,如何從C運行時庫中導出函數以便它們可用於鏈接?

我已經知道Rudy Velthuis的article。如果可能的話,我想避免手動編寫Delphi包裝器,因爲我不需要它們在Delphi中,並且C++ Builder必須已經包含必要的功能。

編輯

的人誰願意在家裏自己玩,代碼是在https://tpabbrevia.svn.sourceforge.net/svnroot/tpabbrevia/trunk Abbrevia的Subversion倉庫中。我已經採取了David Heffernan的建議,並添加了一個「AbCrtl.pas」單元,它在C++ Builder中編譯時模仿crtl.dcu。這得到了PPMD支持工作,但LZMA和WavPack的庫都失敗,連接錯誤:

[ILINK32 Error] Error: Unresolved external '_beginthreadex' referenced from ABLZMA.OBJ 
[ILINK32 Error] Error: Unresolved external 'sprintf' referenced from ABWAVPACK.OBJ 
[ILINK32 Error] Error: Unresolved external 'strncmp' referenced from ABWAVPACK.OBJ 
[ILINK32 Error] Error: Unresolved external '_ftol' referenced from ABWAVPACK.OBJ 

AFAICT,所有的人都正確地宣佈,與_beginthreadex之一AbLzma.pas實際上宣告,所以它的使用通過純Delphi編譯。要自己查看它,只需下載trunk(或者僅僅是「source」和「packages」目錄),禁用AbDefine.inc底部的{$ IFDEF BCB}塊,然後嘗試編譯C++代碼, + Builder「Abbrevia.cbproj」項目。

回答

4

我認爲你只需要Delphi版本的Delphi項目。

在C++ builder版本中,您只需編譯並鏈接foolib.c就好像它是一個C文件(它是!)在程序的Delphi版本中,您使用bcc32創建.obj,使用ctrl等。

爲什麼你想把它封裝在一個C庫中,並在Delphi包裝器中使用C++?

編輯1

你在評論中添加澄清說明。

另一個可以考慮的選擇是避免crtl並在FooWrapper中實現缺少的函數。我這樣做,而不是使用crtl,因爲這給了我更多的控制權,並且我明白了被調用的內容。例如,我不希望任何電話printf()泄漏到我的GUI應用程序或我的DLL。

如果您只缺少一些功能,這可能是一個有吸引力的選擇。通常最好的方法是將它們從msvcrt.dll鏈接到目前的標準系統組件。當然,msvcrt.dll鏈接似乎有點重量只是爲了得到memset(),memcpy()等。

編譯沒有crtl的Delphi單元時有多少缺少的函數?

EDIT 2

我加入這個答案顯示一些代碼。從我自己的代碼庫我提供這樣的:

const 
    __turboFloat: Longint=0; 
    (* We don't actually know the type but it is 4 bytes long and initialised to zero. This can be determined 
    using tdump initcvt.obj. It doesn't actually matter how we define this since it is ultimately not 
    referred to and is stripped from the executable by the linker. *) 

對於ftol我ftol.obj這我相信我從BCC55編譯器的lib文件,我使用的一個提取得到的鏈接。

我認爲strncmp應該是相當常規的實現在普通的帕斯卡。

sprintf更難完全一般化,但您可能會發現它僅用於一些瑣碎的事情,如整數到字符串。在這種情況下,你可以使用C代碼來調用一個專用的例程並實現它。

說實話,我認爲'msvcrt.dll'看起來非常有吸引力!

編輯3

難道我很快就說話?你可以從user32.dll中取出一個完全可用的sprintf,幾乎所有進程都已加載。確保你選擇wsprintfA如果它是你需要的ANSI版本。

EDIT 4

我注意到_beginthreadex。你說這是在一個不同的德爾福單位定義的。爲了讓編譯器看到它,你需要在AbCtrl.pas中重新聲明它,並從那裏調用AbLzma.pas中的真實版本。

在Delphi .pas文件中包含.obj時,編譯器必須能夠解析鏈接到.obj的Delphi單元內的.obj文件中的所有引用。整個遊戲由編譯器處理,而不是鏈接器。

有時你會因爲包含.obj文件的順序而陷入糾結,解決方案是使用前向聲明,但那是另一回事。

+0

我向TurboPower Abbrevia(Delphi zip/tar/gz庫)添加了Lzma支持。所有與Lzma(C)庫交互的代碼都是Delphi。我只是想讓Abbrevia也支持C++ Builder。 – 2011-03-14 20:02:09

+0

我有點超出我的深度。 Delphi單元可以訪問C++ Builder項目中的.c/.h文件嗎?也就是說,如果我在直接訪問原始項目頭文件的.c文件中創建了一個重複的「LzmaDecompress()」函數,我將如何在Delphi單元中引用它? – 2011-03-14 20:04:41

+0

將C代碼靜態鏈接到Delphi的唯一方法是使用$ L。 – 2011-03-14 20:11:41

0

在這種情況下,假設您感興趣的函數可直接從C RTL獲得,因此使用虛擬(空)obj文件僞裝鏈接器應該可以工作,因爲它將滿足查找obj文件的鏈接器Delphi告訴它你需要它,但是仍然可以在RTL中找到這些函數。

+0

我不確定。如果是這種情況,那麼刪除crtl也會起作用。 – 2011-03-14 22:26:21

+0

DavidH是對的,它的失敗與「未解析的外部'Crtl :: _ memmove'類型錯誤相同 – 2011-03-14 23:27:07

+0

」無法解析的外部「實際上是有幫助的,我幾乎在我的原始答覆中提到它,看起來像delphi單元將方法在一個不同的命名空間中,它可以被解析,但它看起來像你已經找到了一個可行的解決方案 – 2011-03-15 04:49:01

0

遲了,但更完整: crtl。從D2005直到XE2,dcu都可以毫無問題地工作。

對於D6和D7,依賴於midaslib.dcu。 呃,不是真的,這個dcu是用一個髒使用條款來分發的。

對於D6和D7你應該創建一個空的midaslib.pas替代,如:

unit midaslib; 
interface 
implementation 
end. 

現在你可以使用crtl.dcu沒有內部錯誤!

+1

這個問題是關於在C++ Builder中使用Crtl.dcu。特別是使用.pas中的.c文件用C++ Builder編譯的文件。在使用Delphi時我已經有了它的工作。 – 2012-01-08 17:35:42