开发者

Access violation when calling external function (C++) from Delphi application

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.


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


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.


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜