我在尝试为我的组件创建一个过程时遇到问题,该过程使用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。流程已停止。使用“运行设置”继续。
我对此问题一无所知,我已经尝试改变几乎所有内容,但我没有得到解决方案。如果有人能帮助我,我将不胜感激。
答案 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
参数。在这种情况下,您有三个选择。
如果可以,请更改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;
如果#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;
如果因为多个组件实例需要同时调用DLL而无法进行#2,那么剩下的唯一选择就是使用动态代理。分配一块可执行内存并在其中存储特殊的存根代码以及组件Self
指针,然后将该内存块传递给DLL,就像它是正常的过程一样。当DLL调用"过程"时,调用它的存根代码,它可以从代理中提取组件指针并根据需要使用它。这是VCL本身用于将非静态TWinControl.WndProc()
方法指定为Win32 API窗口过程回调的方法。我现在无法在此处提供该代码,但请查看VCL在Classes.MakeObjectInstance()
函数的源代码中的示例。