【问题标题】:Access violation when calling external function (C++) from Delphi application从 Delphi 应用程序调用外部函数 (C++) 时访问冲突
【发布时间】:2010-12-14 20:42:52
【问题描述】:

我有一个用 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
  ...

调用该函数时,我会收到调试器异常通知:

项目 ... 引发异常类 EAccessViolation,并带有消息“模块“Scraper.dll”中地址 10103F68 的访问冲突。读取地址 FFFFFFFC'。进程已停止。

从同一个 DLL 导出的其他函数工作正常,所以我猜我在类型声明中犯了一个错误。任何提示都将受到高度赞赏,因为我现在已经陷入困境。

【问题讨论】:

  • 您能否将调试器附加到您的 Scraper.dll 文件并找出ScraperScrape 函数中的哪一行代码触发了异常?
  • 您应该显示 SCRAPER_API 宏是什么,以便我们检查您的调用约定是否匹配。
  • Adam,通过注释/取消注释 C++ 代码,我发现问题是由 CString 变量引起的,我在 Delphi 代码中将其声明为 PAnsiChar 数组。
  • dangph,调用约定不是问题。他们曾经是,但在我之前的问题中已经解决了:-)

标签: c++ delphi types dllimport access-violation


【解决方案1】:

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

如果您想以这种方式传递数据,您应该使用固定长度的字符数组或 C 样式以空字符结尾的字符串(Delphi 中的 PChar)。

C++ 类似于:

char Dealer[100][10];

如有错误请编辑 - 我已经多年没有编写任何 C 代码了

德尔福

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

另请注意,在 Delphi 2009 中,String、Char(以及 PChar)从单字节更改为双字节 (UCS 16)。

其他数据类型也可能不同,例如在 Delphi 中 Word 是 16 位的,但在 C++ 中可能不同。如果可能,请使用 Windows API 中常见的特定类型,例如 USHORT 而不是“unsigned int”和 Word

【讨论】:

  • 是的,问题似乎是由CString变量引起的。我想我将不得不请求对 C++ 代码进行一些更改。谢谢。
【解决方案2】:

您需要做的第一件事是确保您的结构定义是相同的。除非您使用的是 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++ 语言更友好的结构。

【讨论】:

    【解决方案3】:

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

    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;
    

    【讨论】:

    • 我已经根据上述更改了类型声明以及函数的声明和调用,但仍然收到访问冲突错误消息(尽管现在地址不同)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多