从Delphi“访问冲突”中正确调用DLL中的C ++函数

时间:2016-11-21 17:42:14

标签: c++ delphi dll

我试图从Delphi调用C ++代码。我是初学者。我一直收到访问冲突错误,虽然它是间歇性的(但非常常见)。

Access violation

只有在执行ConfigccDLLPriorTran时才会出现这种情况。从我读过的内容可能是调用约定不匹配,但我的印象是我在两者中使用stdcall代码库。我已经使用Dependency Walker创建了我创建的DLL,它将函数显示为_functionName。我不确定我是否应该使用前导下划线来呼叫它们。

我想尽可能少地更改Delphi代码,因为最终使用dll的代码我无法改变(我会说我根本无法改变它但我已经不得不更改PAnsiChar并添加AnsiStrings以便Delphi编译)。

德尔福代码:

unit dllTest;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, AnsiStrings;

procedure CloseDLL; stdcall; external 'cccontrol.dll';
procedure ConfigccDLL(Variables: PAnsiChar); stdcall; external 'cccontrol.dll';
procedure PrepareDLL; stdcall; external 'cccontrol.dll';
procedure PriorTran(Variables: PAnsiChar); stdcall; external 'cccontrol.dll';


type
  TdllTestForm = class(TForm)
    PrepareBtn: TBitBtn;
    Label1: TLabel;
    ConfigccDLLbtn: TBitBtn;
    TranTypeEntry: TEdit;
    TranAmountEntry: TEdit;
    Label2: TLabel;
    PriorTranBtn: TBitBtn;
    TranIDEntry: TEdit;
    Label3: TLabel;
    CloseDLLBtn: TBitBtn;
    Label4: TLabel;
    Memo1: TMemo;
    BitBtn1: TBitBtn;
    procedure CloseDLLBtnClick(Sender: TObject);
    procedure PriorTranBtnClick(Sender: TObject);
    procedure ConfigccDLLbtnClick(Sender: TObject);
    procedure PrepareBtnClick(Sender: TObject);

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  dllTestForm: TdllTestForm;

implementation

{$R *.dfm}

procedure TdllTestForm.PrepareBtnClick(Sender: TObject);
var
  AppHandle: HWND;
begin
  AppHandle := Application.Handle;
  PrepareDLL;
end;

procedure TdllTestForm.ConfigccDLLbtnClick(Sender: TObject);
var
  Variables: AnsiString;
  TransID, TransType, TranAmt: string;
begin
  TransType := TranTypeEntry.Text;
  TranAmt := TranAmountEntry.Text;
  Variables := TransType + '^' + TranAmt + '^';
  ConfigccDLL(PAnsiChar(Variables));
end;

procedure TdllTestForm.PriorTranBtnClick(Sender: TObject);
var
  Variables: AnsiString;
  TransID, TransType, TranAmt: string;
begin
  TransID := TranIDEntry.Text;
  Variables := TransID;
  PriorTran(PAnsiChar(Variables));
end;

procedure TdllTestForm.CloseDLLBtnClick(Sender: TObject);
begin
  CloseDLL;
end;

end.

C ++代码如下:

标题文件:

#pragma once

#ifndef ccControl
#define ccControl
#include <iostream>

#if defined DLL_EXPORT
#define DECLDIR __declspec(dllexport)
#else
#define DECLDIR __declspec(dllimport)
#endif

extern "C" {
    DECLDIR void __stdcall PrepareDLL();
    DECLDIR void __stdcall ConfigccDLL(char* pcharVar);
    DECLDIR void __stdcall PriorTran(char* pcharVar);
    DECLDIR void __stdcall CloseDLL();
}

#endif

Cpp文件:

#include "stdafx.h"

#include <iostream>
#include <windows.h>
#include <Winuser.h>
#include <stdexcept>

#define DLL_EXPORT

#include "ccControl.h"

#pragma warning( disable : 4996 ) 

using namespace std;

extern "C" {

    DECLDIR void __stdcall PrepareDLL()
    {

    }

    DECLDIR void __stdcall ConfigccDLL(char* pcharVar)
    {

    }

    DECLDIR void __stdcall PriorTran(char* pcharVar)
    {

    }

    DECLDIR void __stdcall CloseDLL()
    {

    }
}

从依赖性walker

中看到的Dll

enter image description here

Dependency Walker在打开dll时也会出现这些错误

enter image description here

1 个答案:

答案 0 :(得分:1)

我认为很明显,您没有调用您认为正在调用的DLL。如果你那时你的程序将无法启动,因为导出的函数名称不匹配。您在Dependency Walker中显示的DLL具有装饰名称。您不会在Delphi代码中使用修饰的名称。你的程序执行。您好,您正在链接到另一个DLL。至于你为什么会遇到访问冲突,我们当然不能说,因为我们对该DLL一无所知。

一旦对它进行了排序,以便调用正确的DLL(将DLL放在与可执行文件相同的目录中),我们就可以查看问题中的代码。互操作很好,无论如何你的DLL函数什么都不做。但Delphi代码并不好。请考虑以下代码:

Variables := AnsiStrAlloc(50);
AnsiStrings.StrPCopy(Variables, TransID);

在这里你分配一个长度为50的数组,并将一串长度的数据复制到其中,谁知道什么。如果您的源字符串太长,您将超出缓冲区。

如果必须使用动态分配,那么您需要采取措施确保缓冲区足够长。而且你还需要解除分配。您的代码目前像筛子一样泄漏。

但手动动态分配容易出错,而且很乏味。不要满足于单调乏味的生活。让编译器为您完成工作。在AnsiString类型的变量中构建文本。当您需要将其传递给C ++代码时,请使用PAnsiChar(...)强制转换。

var
  Variables: AnsiString;
....
Variables := TransType + '^' + TranAmt + '^';
ConfigccDLL(PAnsiChar(Variables));