【问题标题】:Delphi - modify variable from DLLDelphi - 从 DLL 修改变量
【发布时间】:2015-01-03 04:08:37
【问题描述】:

我想制作一个简单的程序,将 Edit1.Text 设置为“6”(例如,但使用 DLL - 这很重要)。这是代码: 单位:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  a:integer;

implementation
procedure test; external 'lib.dll' name 'test';

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
test;

Edit1.Text:=Inttostr(a);

end;

end.

还有DLL文件:

library lib;

uses
Winapi.Windows, System.SysUtils;

var
a:integer;
procedure test;
begin
  a:=6;
end;

exports
test;
{$R *.res}

begin
end.

问题是,Edit1.Text 还是 0。你能帮帮我吗?

【问题讨论】:

  • 如果需要在.exe和.dll之间共享一个变量,标准的解决方案是使用内存映射文件。
  • @user24 我不这么认为。映射用于跨进程内存共享。这是进程中的。
  • 如果您启动 2 个 .exe 副本,加载相同的 .dll,您将获得跨进程。但我相信你会不同意:)
  • @user24 不,这是两个进程。但我没有看到问题中的跨进程。

标签: delphi delphi-xe


【解决方案1】:

您有两个不同的变量,一个在 DLL 中,一个在可执行文件中。他们都被命名为a 是偶然的。设置一个对另一个没有影响。

使DLL导出一个返回值的函数:

function GetValue: Integer; stdcall;
begin
  Result := 6;
end;

像这样导入:

function GetValue: Integer; stdcall; external dllname;

然后这样称呼它:

Edit1.Text := IntToStr(GetValue);

毫无疑问,真正的代码将不仅仅返回值6,但这没问题。你可以退回任何你喜欢的东西。他们的关键点是您使用函数返回值将值从 DLL 传递到主机。

【讨论】:

    【解决方案2】:

    效果很好:

    在主单元中

     implementation
    
      procedure Test(Edit1: TEdit); stdcall; external 'dll_proj.dll';
    

    在 DLL 中

    exports Test;
    
    Procedure Test(Object1: TEdit); stdcall;
    var i:integer;
    begin
        for i:= 0 to 100 do
          begin
           Object1.Text:= IntToStr(i);
           Application.Processmessages();
           Sleep(100);
        end;
    end;
    

    【讨论】:

      【解决方案3】:

      以您的原始代码为基础并添加更多按钮,这里演示了您如何使用过程(或您喜欢的函数)并让它们与 DLL。

      请注意,name 选项不是必需的,除非您希望更改函数的名称或使用重载 - 所以我已将其注释掉。

      implementation
      procedure test(var a : integer); external 'lib.dll' {name 'test'};
      procedure test2(ptr_a : pinteger); external 'lib.dll';
      procedure test3(ptr_a : pinteger); external 'lib.dll';
      procedure test4(ptr_a : pinteger = nil); external 'lib.dll';
      
      {$R *.dfm}
      
      procedure TForm14.Button1Click(Sender: TObject);
      begin
        test(a);
      
        Edit1.Text:=Inttostr(a);
      
      end;
      
      procedure TForm14.Button2Click(Sender: TObject);
      begin
        test2(@a);
      
        Edit1.Text:=Inttostr(a);
      
      
      end;
      
      procedure TForm14.Button3Click(Sender: TObject);
      begin
        test3(@a);
      
        Edit1.Text:=Inttostr(a);
      
      end;
      
      procedure TForm14.Button4Click(Sender: TObject);
      begin
        test4(@a);
        test4;
      
        Edit1.Text:=Inttostr(a);
      
      end;
      
      end.
      

      ...和库体...

      var
        local_a:integer;
        local_Ptr_a:pinteger;
      procedure test(var a : integer);
      begin
        a:=6;
      end;
      
      procedure test2(ptr_a : pinteger);
      begin
        inc(ptr_a^);
      end;
      
      procedure test3(ptr_a : pinteger);
      begin
        inc(local_a);
        ptr_a^:=local_a;
      end;
      
      procedure test4(ptr_a : pinteger = nil);
      begin
        if ptr_a = nil then
          inc(local_ptr_a^)
        else
          local_ptr_a := ptr_a;
      end;
      
      exports test;
      exports test2;
      exports test3;
      exports test4;
      {$R *.res}
      
      begin
        local_a := 4;
      end.
      

      所以 - 稍微解释一下。

      第一个测试:使用 var 参数从过程中返回一个值。没问题。

      第二次测试:将接收变量的地址作为指针传递。我在这里添加了一点卷曲 - 增加娱乐价值,...价值。

      第三次测试:这显示了如何使用 DLL 拥有的 local 值。 local 值由最后的4 赋值初始化。该过程本身使用与第二个测试相同的机制将值从 DLL 的局部变量返回给主例程的变量。

      请注意,test3 分配给程序的变量(1 + DLL 内存中存储的值),因此按下 button3 实际上会更改 a;因此按下按钮 1-2-2-3-2 将使用 3 中的值进行最后一次更改,而不是使用 2 中的先前值。

      最终测试:在这里我们可以变得更聪明一些。它使用可选参数机制来改变详细的操作。

      首先,您执行过程使用一个参数,作为适当类型的变量的地址(或指向的指针)。该过程将该地址存储在 DLL 的内存区域中。

      接下来您可以使用 no 参数执行该过程,它将递增存储指针所指向的整数。纯粹为了方便起见,我在每次按下按钮时都建立了变量的地址,但是一旦存储了地址,无论该地址是在几微秒前还是一周之前设置的,test4; 都会增加整数该地址的价值——不管它是什么。使用test4(@b); 设置地址,然后test4; 将增加b - 执行过程时最后指向的整数使用参数。

      执行 test4; 而不事先执行 test4(@something)something 现在超出范围(例如可能是过程中的局部变量)很可能会导致访问冲突。

      【讨论】:

      • 投反对票,因为提议的解决方案类似于 C 并且有点危险。
      • OP: IOW,因为 DLL 通常是用 C 编写的,所以通常在过程头 procedure test(var a : integer);cdecl; [external filename] 中添加 cdecl; 以告诉 Delphi 应该使用 C 调用约定,而不是 Delphi 的调用约定。 Delphi 很高兴提供可执行文件的约定与 DLL 相匹配,但它是一个罕见且明智的 C 程序员可以接受这个概念。由于我从未愤怒地用 C 语言编写过(仅作为学术练习),并且几乎完全使用 Delphi 已经有 20 年了,所以它是我自己的代码中的第二天性和自动。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-16
      • 1970-01-01
      • 1970-01-01
      • 2014-01-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多