Violação de acesso ao chamar função externa (C ++) da aplicação Delphi
-
19-09-2019 - |
Pergunta
Eu tenho uma DLL externo escrito em C ++. A peça abaixo declara um struct tipo e uma função, que, sendo dado um ponteiro, preenche uma variável deste tipo:
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);
}
Eu declarar um tipo semelhante na minha Delphi aplicação e chamar a função acima:
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
...
Quando a função é chamada fico Debugger Notificação Exceção:
Project ... levantou exceção classe EAccessViolation com a mensagem 'Violação de acesso no endereço 10103F68 no módulo 'Scraper.dll'. Leia de endereço FFFFFFFC'. Processo parou.
Outras funções exportadas a partir da mesma multa trabalho DLL, então o meu palpite é que cometi um erro na declaração do tipo. Todas as dicas serão muito apreciados, como eu estou morto preso neste momento.
Solução
A principal id problema que C ++ CString e Delphi Cordas são tipos incompatíveis.
Se você quiser passar dados dessa maneira, você deve usar arrays de caracteres de comprimento, quer fixos ou C-Style terminada nula cordas (PChar em Delphi).
C ++ seria algo como:
char Dealer[100][10];
Por favor edite se errado - Há muitos anos que eu fiz qualquer C codificação
Delphi
Dealer : packed array[0..9, 0..99] of char;
ou
type
TDealer = packed array[0..99] of char;
...
Dealer : arry[0..9] of TDealer;
ou se estiver usando C-string (TCHAR em código API)
Dealer: array[0..9] of PAnsiChar; // or PWideChar if source is UCS-16
De referir ainda que a corda, Char (e, portanto, PChar) alterado de byte para byte duplo (UCS 16) em Delphi 2009.
Outros tipos de dados podem ser diferentes, por exemplo, como bem Em Delphi de palavra é de 16 bits, mas pode ser diferente em C ++. Se os tipos possíveis utilização específicos que são comuns na API do Windows, tais como USHORT em vez de "int não assinado" e Word
Outras dicas
A primeira coisa que você precisa fazer é se certificar de suas definições de struct são os mesmos. A menos que você estiver usando um compilador de 16 bits C ++, o tipo unsigned int
definitivamente não é um tipo de 16 bits, e ainda tipo Word
da Delphi é. Use Cardinal
vez. Se você tiver Delphi 2009 ou mais tarde, então o seu tipo Char
é um tipo de dois bytes; uso AnsiChar
vez.
Mesmo com essas mudanças, no entanto, você está condenado. Seu tipo C ++ usa o tipo de CString
específico da Microsoft. Não há equivalente a isso em Delphi ou qualquer outra linguagem não-Microsoft-C ++. Você já tentou usar tipo string
da Delphi em seu lugar, mas eles são apenas semelhante em seus nomes. Seu layout binário na memória não é o mesmo em todos.
Não há nada que você pode com essa definição struct.
Se você ou alguém em sua organização é o autor desse DLL, então alterá-lo a olhar mais como qualquer outro DLL que você já usou. Passe ponteiros de caracteres ou matrizes, e não qualquer tipo de classe. Se o DLL é de outro partido, em seguida, solicitar o autor a alterá-lo para você. Essa escolha de API foi irresponsável e míope.
Se você não pode fazer isso, então você terá que escrever um wrapper DLL em C ++ que leva o struct C ++ e converte-lo para outro struct que é mais amigável para não-C ++ idiomas.
Enquanto só a leitura de dados a partir da DLL e não tentando gravar dados a ele, em seguida, tentar substituir CString com PAnsiChar (ou PWideChar se a DLL foi compilado para Unicode), ou seja:
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;
Com o que disse, o acidente você está enfrentando é mais provável resultado do ponteiro não inicializado que você está passando para ScraperScrape (). Você precisa mudar o seu código Delphi para inicializar essa variável, isto é:
...
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;
Melhor seria se livrar da variável pCurState por completo:
var
CurState: SScraperState;
begin
if ScraperScrape(hWnd, @CurState) = 0 then
...
end;
Melhor seria para se livrar do alias pSScraperState por completo:
function ScraperScrape(wnd: HWND; var state: SScraperState): Integer; cdecl; external 'Scraper.dll';
var
CurState: SScraperState;
begin
if ScraperScrape(hWnd, CurState) = 0 then
...
end;