在DLL中使用另一个过程的组件中的过程

时间:2013-05-18 22:40:44

标签: delphi dll

我在尝试为我的组件创建一个过程时遇到问题,该过程使用DLL(TExecute)中包含的过程,该过程在当前代码中也需要声明。因此,此过程有一个指针作为参数,以了解如何处理评估。 下面的代码工作正常,但我需要在组件内部的过程eval使用此组件中的私有变量。工作代码如下,请注意,在这种情况下,eval过程是全局的。

TExecute = procedure(eval: pointer, var variableArray: double);cdecl
TMyComponent = Class(TComponent)
public
    FHandle: THandle;
    FExecute: TExecute;
    procedure Calculate;

var
    n: integer;
    x: array of double;

procedure eval(var x: double);

implementation
procedure eval(var x:double);
var
    mx: Array[0..200] of double absolute x;
begin
    mx[0]:= 2*mx[0];
end;

TMyComponent.Calculate;
begin
    FHandle:= LoadLibrary(.....);
    FExecute:= GetProcAddress(FHandle, 'main');

    n:=2;
    setlength(x,n);

    FExecute(@eval,x[0]);
end;

当我将程序eval放在TMyComponent中时,我遇到了一个问题:

TExecute = procedure(eval: pointer, var variableArray: double);cdecl
TMyComponent = Class(TComponent)
public
    FHandle: THandle;
    FExecute: TExecute;
    procedure Calculate;
    procedure eval(var x: double);

var
    n: integer;
    x: array of double;



implementation
procedure TMyComponent.eval(var x:double);
var
    mx: Array[0..200] of double absolute x;
begin
    mx[0]:= 2*mx[0];
end;

TMyComponent.Calculate;
begin
    FHandle:= LoadLibrary(.....);
    FExecute:= GetProcAddress(FHandle, 'main');

    n:=2;
    setlength(fx,n);

    FExecute(@TMyComponent.eval,x[0]);
end;

我得知该项目出现错误消息:0x65900381f处的访问冲突:写入地址0x0000005c。流程已停止。使用“运行设置”继续。

我对此问题一无所知,我已经尝试改变几乎所有内容,但我没有得到解决方案。如果有人能帮助我,我将不胜感激。

1 个答案:

答案 0 :(得分:4)

全局过程和类方法不是一回事。类方法有一个隐藏的Self参数,当类方法传递给DLL时,DLL不会将其考虑在内。这就是你的代码崩溃的原因 - 调用堆栈设置不正确。

鉴于你的工作"代码,您的组件代码需要如下所示:

TExecute = procedure(eval: pointer; var variableArray: double); cdecl;

TMyComponent = Class(TComponent)
public
    FHandle: THandle;
    FExecute: TExecute;
    procedure Calculate;
    class procedure eval(var x: double); static;
end;

var
    n: integer;
    x: array of double;

implementation

class procedure TMyComponent.eval(var x:double);
var
    mx: Array[0..200] of double absolute x;
begin
    mx[0]:= 2*mx[0];
end;

procedure TMyComponent.Calculate;
begin
    FHandle:= LoadLibrary(.....);
    FExecute:= GetProcAddress(FHandle, 'main');

    n:=2;
    setlength(fx,n);

    FExecute(@eval,x[0]);
end;

由于您的eval()方法只访问全局变量,因此可以正常工作。但是,如果需要访问组件的成员,则会遇到问题,因为static指令会删除Self参数。在这种情况下,您有三个选择。

  1. 如果可以,请更改DLL函数以接受组件可以传递其Self值的其他参数,然后让DLL将该值传递给eval()作为参数,例如:

    TExecute = procedure(eval: pointer, var variableArray: double; userdata: pointer); cdecl;
    
    TMyComponent = Class(TComponent)
    public
        FHandle: THandle;
        FExecute: TExecute;
        procedure Calculate;
        class procedure eval(var x: double; userdata: pointer); static;
    end;
    
    var
        n: integer;
        x: array of double;
    
    implementation
    
    class procedure TMyComponent.eval(var x: double; userdata: pointer);
    begin
        // use TMyComponent(userdata) as needed...
    end;
    
    procedure TMyComponent.Calculate;
    begin
        FHandle:= LoadLibrary(.....);
        FExecute:= GetProcAddress(FHandle, 'main');
    
        n:=2;
        setlength(fx,n);
    
        FExecute(@eval, x[0], Self);
    end;
    
  2. 如果#1不可能,并且如果组件中只有一个实例一次调用DLL函数,则使用指向组件的全局指针,例如:

    TExecute = procedure(eval: pointer, var variableArray: double); cdecl;
    
    TMyComponent = Class(TComponent)
    public
        FHandle: THandle;
        FExecute: TExecute;
        procedure Calculate;
        class procedure eval(var x: double); static;
    end;
    
    var
        n: integer;
        x: array of double;
    
    implementation
    
    var
        MyComp: TMyComponent;
    
    class procedure TMyComponent.eval(var x: double);
    begin
        // use MyComp as needed...
    end;
    
    procedure TMyComponent.Calculate;
    begin
        FHandle:= LoadLibrary(.....);
        FExecute:= GetProcAddress(FHandle, 'main');
    
        n:=2;
        setlength(fx,n);
    
        MyComp := Self;
        FExecute(@eval, x[0]);
    end;
    
  3. 如果因为多个组件实例需要同时调用DLL而无法进行#2,那么剩下的唯一选择就是使用动态代理。分配一块可执行内存并在其中存储特殊的存根代码以及组件Self指针,然后将该内存块传递给DLL,就像它是正常的过程一样。当DLL调用"过程"时,调用它的存根代码,它可以从代理中提取组件指针并根据需要使用它。这是VCL本身用于将非静态TWinControl.WndProc()方法指定为Win32 API窗口过程回调的方法。我现在无法在此处提供该代码,但请查看VCL在Classes.MakeObjectInstance()函数的源代码中的示例。