从Delphi应用程序调用外部函数(C ++)时的访问冲突

时间:2009-11-10 00:26:49

标签: c++ delphi types dllimport access-violation

我有一个用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导出的其他函数工作正常,所以我的猜测是我在类型声明中犯了一个错误。任何提示都会受到高度赞赏,因为我现在已经死了。

3 个答案:

答案 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中的数据而不是尝试向其写入数据,那么您尝试将CS​​tring替换为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;