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

I've an external DLL written in C++. The piece below declares a struct type and a function, which, being given a pointer, fills a variable of this type:

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);
}

I declare a similar type in my Delphi application and call the above function:

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

When the function is called I get Debugger Exception Notification:

Project ... raised exception class EAccessViolation with message 'Access violation at address 10103F68 in module 'Scraper.dll'. Read of address FFFFFFFC'. Process stopped.

Other functions exported from the same DLL work fine, so my guess is I made a mistake in the type declaration. Any tips will be highly appreciated, as I'm dead stuck at this point.

#0

The main problem id that C++ CString and Delphi String are incompatible types.

If you want to pass data in this manner, you should use either fixed length character arrays or C-Style null terminated strings (PChar in Delphi).

C++ would be something like:

char Dealer[100][10];

Please edit if wrong - it been many years since I done any C coding

Delphi

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

or

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

or if using C-string (TCHAR in API code)

Dealer: array[0..9] of PAnsiChar; // or PWideChar if source is UCS-16

Also note that String, Char (and hence PChar) changed from single byte to double byte (UCS 16) in Delphi 2009.

Other data types may be different as well e.g. In Delphi Word is 16bit, but may be different in C++. If possible use specific types that are common in the Windows API, such as USHORT instead of "unsigned int" and Word

#1

The first thing you need to do is make sure your struct definitions are the same. Unless you're using a 16-bit C++ compiler, the type unsigned int is definitely not a 16-bit type, and yet Delphi's Word type is. Use Cardinal instead. If you have Delphi 2009 or later, then your Char type is a two-byte type; use AnsiChar instead.

Even with those changes, though, you're doomed. Your C++ type uses the Microsoft-specific CString type. There is no equivalent to that in Delphi or any other non-Microsoft-C++ language. You've attempted to use Delphi's string type in its place, but they are only similar in their names. Their binary layout in memory is not the same at all.

There is nothing you can with that struct definition.

If you or someone else in your organization is the author of that DLL, then change it to look more like every other DLL you've ever used. Pass character pointers or arrays, not any class type. If the DLL is from another party, then request the author to change it for you. That choice of API was irresponsible and short-sighted.

If you can't do that, then you'll have to write a wrapper DLL in C++ that takes the C++ struct and converts it to another struct that's more friendly to non-C++ languages.

#2

As long as you only reading data from the DLL and not trying to write data to it, then you try replacing CString with PAnsiChar (or PWideChar if the DLL was compiled for Unicode), ie:

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; 

With that said, the crash you are experiencing is more likely a result of the uninitialized pointer that you are passing to ScraperScrape(). You need to change your Delphi code to initialize that variable, ie:

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

Better would be to get rid of the pCurState variable altogether:

var
  CurState: SScraperState;        
begin        
  if ScraperScrape(hWnd, @CurState) = 0 then  
    ...
end;

Better would be to get rid of the pSScraperState alias altogether:

function ScraperScrape(wnd: HWND; var state: SScraperState): Integer; cdecl; external 'Scraper.dll';  

var
  CurState: SScraperState;        
begin        
  if ScraperScrape(hWnd, CurState) = 0 then  
    ...
end;

推荐文章

使用不同的日期格式将CSV导入MySQL

使用不同的日期格式将CSV导入MySQL

推荐文章

从一个应用程序控制另一个

从一个应用程序控制另一个

推荐文章

tomcatv7.0无法在Eclipse上启动,servlet映射中的无效

tomcatv7.0无法在Eclipse上启动,servlet映射中的无效

推荐文章

如何在使用java准备语句批处理时获取表的自动生成键?

如何在使用java准备语句批处理时获取表的自动生成键?

推荐文章

在Rails的datetime列中存储ISO8601的最佳方法是什么?

在Rails的datetime列中存储ISO8601的最佳方法是什么?

推荐文章

SQL联接以获取最新记录

SQL联接以获取最新记录

推荐文章

NSTimer或其他什么东西来解锁图像?

NSTimer或其他什么东西来解锁图像?

推荐文章

AS3-检测鼠标是否向下,即使重叠

AS3-检测鼠标是否向下,即使重叠

推荐文章

将int数据存储和读取到char数组中

将int数据存储和读取到char数组中

推荐文章

如何从Microsoft测试管理器的测试用例中读取参数值

如何从Microsoft测试管理器的测试用例中读取参数值

推荐文章

将图像下载到android应用程序的coverflow时出错

将图像下载到android应用程序的coverflow时出错

推荐文章

如何从外部程序调用游戏中的函数/设置数据。(天际网)

如何从外部程序调用游戏中的函数/设置数据。(天际网)

推荐文章

Facebook评论代码-是什么?

Facebook评论代码-是什么?

推荐文章

Android用另一个片段替换当前片段

Android用另一个片段替换当前片段

推荐文章

将数据导出到cisco call manager 8.0

将数据导出到cisco call manager 8.0

推荐文章

表单编辑与内联编辑到同一个jqgrid

表单编辑与内联编辑到同一个jqgrid