从 Delphi 应用程序调用外部函数 (C++) 时发生访问冲突
我有一个用 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 导出的其他函数工作正常,所以我猜测我在类型声明中犯了一个错误。任何提示都将受到高度赞赏,因为我现在已经陷入困境。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
主要问题是C++ CString 和Delphi String 是不兼容的类型。
如果您想以这种方式传递数据,您应该使用固定长度字符数组或 C 风格的 null 终止字符串(Delphi 中的 PChar)。
C++ 会是这样的:
如果错误,请编辑 - 我已经很多年没有进行任何 C 编码
Delphi
或
或者如果使用 C 字符串(API 代码中的 TCHAR)
另请注意,String、Char(以及 PChar)从单字节更改为在 Delphi 2009 中为双字节 (UCS 16)。
其他数据类型也可能不同,例如在 Delphi 中 Word 是 16 位,但在 C++ 中可能不同。如果可能,请使用 Windows API 中常见的特定类型,例如 USHORT 而不是“unsigned int”和 Word
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:
Please edit if wrong - it been many years since I done any C coding
Delphi
or
or if using C-string (TCHAR in API code)
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
您需要做的第一件事是确保您的结构定义相同。除非您使用 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++ 语言更友好的结构。
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'sWord
type is. UseCardinal
instead. If you have Delphi 2009 or later, then yourChar
type is a two-byte type; useAnsiChar
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'sstring
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.
只要您只从 DLL 中读取数据而不尝试向其写入数据,那么您可以尝试用 PAnsiChar 替换 CString(如果 DLL 是为 Unicode 编译的,则替换为 PWideChar),即:
话虽如此,您遇到的崩溃是更有可能是您传递给 ScraperScrape() 的未初始化指针的结果。您需要更改 Delphi 代码来初始化该变量,即:
最好完全删除 pCurState 变量:
最好完全删除 pSScraperState 别名:
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:
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:
Better would be to get rid of the pCurState variable altogether:
Better would be to get rid of the pSScraperState alias altogether: