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;
精彩评论