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

发布于 2024-08-10 13:37:06 字数 2561 浏览 10 评论 0原文

我有一个用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

请爱~陌生人 2024-08-17 13:37:06

主要问题是C++ CString 和Delphi String 是不兼容的类型。

如果您想以这种方式传递数据,您应该使用固定长度字符数组或 C 风格的 null 终止字符串(Delphi 中的 PChar)。

C++ 会是这样的:

char Dealer[100][10];

如果错误,请编辑 - 我已经很多年没有进行任何 C 编码

Delphi

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

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

或者如果使用 C 字符串(API 代码中的 TCHAR)

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

另请注意,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:

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

你好,陌生人 2024-08-17 13:37:06

您需要做的第一件事是确保您的结构定义相同。除非您使用 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'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.

时间你老了 2024-08-17 13:37:06

只要您只从 DLL 中读取数据而不尝试向其写入数据,那么您可以尝试用 PAnsiChar 替换 CString(如果 DLL 是为 Unicode 编译的,则替换为 PWideChar),即:

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; 

话虽如此,您遇到的崩溃是更有可能是您传递给 ScraperScrape() 的未初始化指针的结果。您需要更改 Delphi 代码来初始化该变量,即:

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

最好完全删除 pCurState 变量:

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

最好完全删除 pSScraperState 别名:

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

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

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文