我有一个用C ++编写的外部DLL。下面的部分声明了一个 struct 类型和一个函数,它被赋予一个指针,填充这种类型的变量:
enum LimitType { NoLimit, PotLimit, FixedLimit };
struct SScraperState
{
char title[512];
unsigned int card_common[5];
unsigned int card_player[10][2];
unsigned int card_player_for_display[2];
bool dealer[10];
bool sitting_out[10];
CString seated[10];
CString active[10];
CString name[10];
double balance[10];
bool name_good_scrape[10];
bool balance_good_scrape[10];
double bet[10];
double pot[10];
CString button_state[10];
CString i86X_button_state[10];
CString i86_button_state;
CString button_label[10];
double sblind;
double bblind;
double bbet;
double ante;
LimitType limit;
double handnumber;
bool istournament;
};
extern "C" {
SCRAPER_API int ScraperScrape(HWND hwnd, SScraperState *state);
}
我在 Delphi 应用程序中声明了一个类似的类型并调用上面的函数:
interface
type
LimitType = (NoLimit, PotLimit, FixedLimit);
SScraperState = record
title: Array [0..511] of Char;
card_common: Array [0..4] of Word;
card_player: Array [0..9, 0..1] of Word;
card_player_for_display: Array [0..1] of Word;
dealer: Array [0..9] of Boolean;
sitting_out: Array [0..9] of Boolean;
seated: Array [0..9] of String;
active: Array [0..9] of String;
name: Array [0..9] of String;
balance: Array [0..9] of Double;
name_good_scrape: Array [0..9] of Boolean;
balance_good_scrape: Array [0..9] of Boolean;
bet: Array [0..9] of Double;
pot: Array [0..9] of Double;
button_state: Array [0..9] of String;
i86X_button_state: Array [0..9] of String;
i86_button_state: String;
button_label: Array [0..9] of String;
sblind: Double;
bblind: Double;
bbet: Double;
ante: Double;
limit: LimitType;
handnumber: Double;
istournament: Boolean;
end;
pSScraperState = ^SScraperState;
function ScraperScrape(hWnd: HWND; State: pSScraperState): Integer; cdecl; external 'Scraper.dll';
implementation
var
CurState: SScraperState;
pCurState: pSScraperState;
if ScraperScrape(hWnd, pCurState) = 0 then
...
当调用该函数时,我得到调试器异常通知:
Project ...引发了异常类EAccessViolation,并在模块'Scraper.dll'中显示了地址为10103F68的访问冲突。读取地址FFFFFFFC'。流程已停止。
从同一个DLL导出的其他函数工作正常,所以我的猜测是我在类型声明中犯了一个错误。任何提示都会受到高度赞赏,因为我现在已经死了。
答案 0 :(得分:3)
C ++ CString 和Delphi String 的主要问题是不兼容的类型。
如果要以这种方式传递数据,则应使用固定长度字符数组或C-Style空终止字符串(Delphi中的PChar)。
C ++会是这样的:
char Dealer[100][10];
请编辑如果错误 - 我做了任何C编码已经很多年了
的Delphi
Dealer : packed array[0..9, 0..99] of char;
或
type
TDealer = packed array[0..99] of char;
...
Dealer : arry[0..9] of TDealer;
或者如果使用C-string(API代码中的TCHAR)
Dealer: array[0..9] of PAnsiChar; // or PWideChar if source is UCS-16
另请注意,在Delphi 2009中,String,Char(以及PChar)从单字节更改为双字节(UCS 16)。
其他数据类型也可能不同,例如在Delphi中Word是16位,但在C ++中可能有所不同。如果可能,请使用Windows API中常见的特定类型,例如USHORT而不是“unsigned int”和Word
答案 1 :(得分:3)
您需要做的第一件事是确保您的结构定义是相同的。除非您使用的是16位C ++编译器,否则类型unsigned int
绝对不是16位类型,而Delphi的Word
类型是。请改用Cardinal
。如果你有Delphi 2009或更高版本,那么你的Char
类型是一个双字节类型;请改用AnsiChar
。
即使有了这些变化,你也注定要失败。您的C ++类型使用特定于Microsoft的CString
类型。没有与Delphi或任何其他非Microsoft-C ++语言相同的内容。您试图在其位置使用Delphi的string
类型,但它们的名称相似。它们在内存中的二进制布局完全不同。
结构定义没有任何内容。
如果您或您组织中的其他人是该DLL的作者,则将其更改为更像您曾经使用过的所有其他DLL。传递字符指针或数组,而不是任何类类型。如果DLL来自另一方,则请求作者为您更改。 API的选择是不负责任和短视的。
如果你不能这样做,那么你将不得不用C ++编写一个包装器DLL,它接受C ++结构并将其转换为对非C ++语言更友好的另一个结构。
答案 2 :(得分:2)
只要您只读取DLL中的数据而不是尝试向其写入数据,那么您尝试将CString替换为PAnsiChar(如果DLL是针对Unicode编译的,则使用PWideChar),即:
type
LimitType = ( NoLimit, PotLimit, FixedLimit );
SScraperState = record
title: array[0..511] of AnsiChar;
card_common: array[0..4] of Cardinal;
card_player: array[0..9, 0..1] of Cardinal;
card_player_for_display: array[0..1] of Cardinal;
dealer: array[0..9] of Boolean;
sitting_out: array[0..9] of Boolean;
seated: array[0..9] of PAnsiChar;
active: array[0..9] of PAnsiChar;
name: array[0..9] of PAnsiChar;
balance: array[0..9] of Double;
name_good_scrape[0..9] of Boolean;
balance_good_scrape[0..9] of Boolean;
bet: array[0..9] of Double;
pot: array[0.99]: Double;
button_state: array[0.9] of PAnsiChar;
i86X_button_state: array[0..9] of PAnsiChar;
i86_button_state: PAnsiChar;
button_label: array[0..9] of PAnsiChar;
sblind: Double;
bblind: Double;
bbet: Double;
ante: Double;
limit: LimitType;
handnumber: Double;
istournament: Boolean;
end;
话虽如此,您遇到的崩溃更可能是您传递给ScraperScrape()的未初始化指针的结果。您需要更改Delphi代码以初始化该变量,即:
...
pSScraperState = ^SScraperState;
function ScraperScrape(wnd: HWND; state: pSScraperState): Integer; cdecl; external 'Scraper.dll';
...
var
CurState: SScraperState;
pCurState: pSScraperState;
begin
pCurState := @CurState;
if ScraperScrape(hWnd, pCurState) = 0 then
...
end;
最好完全摆脱pCurState变量:
var
CurState: SScraperState;
begin
if ScraperScrape(hWnd, @CurState) = 0 then
...
end;
最好完全摆脱pSScraperState别名:
function ScraperScrape(wnd: HWND; var state: SScraperState): Integer; cdecl; external 'Scraper.dll';
var
CurState: SScraperState;
begin
if ScraperScrape(hWnd, CurState) = 0 then
...
end;