3

我有一个用 C++ 编写的外部 DLL。下面的部分声明了一个结构类型和一个函数,给它一个指针,填充这个类型的变量:

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
  ...

调用该函数时,我收到调试器异常通知:

项目 ... 引发异常类 EAccessViolation,并带有消息“模块“Scraper.dll”中地址 10103F68 的访问冲突。读取地址 FFFFFFFC'。进程停止。

从同一个 DLL 导出的其他函数工作正常,所以我猜我在类型声明中犯了一个错误。任何提示都将受到高度赞赏,因为我在这一点上已经陷入困境。

4

3 回答 3

3

主要问题是 C++ CString和 Delphi String是不兼容的类型。

如果您想以这种方式传递数据,您应该使用固定长度的字符数组或 C 样式以空字符结尾的字符串(Delphi 中的 PChar)。

C++ 会是这样的:

char Dealer[100][10];

如果有错误请编辑——我已经有很多年没有做任何 C 编码了

德尔福

Dealer : packed array[0..9, 0..99] of char; 

或者

type 
  TDealer = packed array[0..99] of char;
  ...
  Dealer : arry[0..9] of TDealer;

或者如果使用 C 字符串(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

于 2009-11-10T01:07:40.347 回答
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++ 语言更友好的结构。

于 2009-11-10T01:10:30.670 回答
2

只要您只从 DLL 读取数据而不尝试向其写入数据,那么您可以尝试将 CString 替换为 PAnsiChar(或 PWideChar,如果 DLL 是为 Unicode 编译的),即:

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;
于 2009-11-10T01:31:53.900 回答