【问题标题】:How to Load a DLL in multiple threads in Delphi?如何在 Delphi 的多个线程中加载 DLL?
【发布时间】:2013-12-13 12:09:40
【问题描述】:

也许我错过了什么,我不知道这里发生了什么。

我正在尝试在 TThread 对象的多个实例中加载相同的 DLL。

这是我的 DLL 代码:

library MyCalcFor32;

uses
  SysUtils,
  Classes,
  uRunner in 'uRunner.pas';

Exports EVal;

{$R *.res}

begin
end.

这是 uRunner.pas:

unit uRunner;

interface

uses SysUtils,
     Classes;

function EVal(Valor: WideString): WideString; stdcall; export;

implementation

function EVal(Value: WideString): WideString; stdcall; export;
begin
  Result := Value+' xxx';
end;

initialization

finalization

end.

这是加载 DLL 的程序:

procedure TfrmMain.FormCreate(Sender: TObject);
var I: Integer;
begin
  SetLength(Threads, 10);
  for I:= 0 to 9 do
  begin
    Threads[I] := TWorker.Create(Self.Handle, I+1, Memo1.Text, ExtractFilePath(ParamStr(0)));
  end;
end;


procedure TfrmMain.btnExecuteThreadsClick(Sender: TObject);
var I: Integer;
begin
  ClearMemos([MT1, MT2, MT3, MT4, MT5, MT6, MT7, MT8, MT9, MT10]);

  for I:= 0 to 0 do //to 9, for multiple
  begin
    if Threads[I].Suspended then
      Threads[I].Resume
    else
      ShowMessage('Thread already in execution');
  end;
end;

procedure TWorker.Execute;
var I: Integer;
    J: Cardinal;

    Ret: WideString;

    A,B,C: Extended;       
begin
  CoInitialize(nil);
  try
    LoadDll;

    while not Terminated do
    begin
      if not (Suspended or Terminated) then
      begin
        A := 310132041025;
        B := 17592186044416;
        C := 0;

        for I:= 0 to 10 do
        begin

          if (Terminated) then begin
            Break;
          end;

          for J:= 0 to 9999999 do
          begin

            if (Terminated) then begin
              Break;
            end;

            A:= Sqrt(A);

            if A <= 0 then begin
              A:= 310132041025;
            end
            else begin
              A:= Math.Power(A, 2);
            end;

            C:= C + (B-34 / 4);

            B:= B / 2;

            if B <= 0 then begin
              B:= 17592186044416;
            end;
          end;

          Ret := FEvalProcAddress(FEValValue);

          NotifyMainForm(Format('Evaluate %s, resulted in %s', [IntToStr(I), Ret]));
        end;
        Suspend;
      end;

      Sleep(5000);
    end;
  finally
    CoUninitialize;
  end;
end;  

procedure TWorker.LoadDll;
begin
  //GlobalLock.Enter;
  //try
    FDLLHandle := LoadLibraryA(PChar(FPathApp + 'MyCalcFor32.dll'));
  //finally
  //  GlobalLock.Leave;
  //end;

  if GetLastError <> 0 then
  begin
    NotifyTerminateThread;
  end
  else
  begin
    FEvalProcAddress := GetProcAddress(FDLLHandle, PChar('EVal'));

    if GetLastError <> 0 then
    begin
      NotifyTerminateThread;
    end;
  end;
end;

当我只有 1 个线程时,它工作得很好,但是当我使用多个线程时,它会引发以下异常:

System Error.  Code: 87.
Incorrect Parameter

注意:以上代码仅供复现;
我知道 WideString + AnsiString 问题。

【问题讨论】:

  • export 毫无意义。你也可以删除它。而register 调用约定意味着您只能从 Delphi 调用此函数。
  • 我忘记了,我只是放了stdcall。我将编辑问题
  • 如果线程被挂起,检查线程内部是没有用的。如果你挂起一个线程,那么这个线程内就没有更多的动作了。这就是为什么 TThread.Suspend/TThread.Resume 现在已被弃用的原因。你应该更好地使用事件
  • @SirRufo 如您所见,我使用的是 Delphi 6,因此不推荐使用 Suspend/Resume。
  • 虽然它在 Delphi 6 中没有被弃用,但使用它是明智的。并检查线程内的TThread.Suspended,你不明白TThread.Suspend发生了什么

标签: windows multithreading delphi dll delphi-6


【解决方案1】:

您执行的错误检查不正确。如果函数失败,您只需要调用GetLastError。我希望您在 API 调用成功后调用 GetLastError,并且并非所有 API 调用在返回成功时都会调用 SetLastError(0)。因此,您收到了一个不适用于您所做的函数调用的陈旧错误代码。

要检查失败,对于这些函数,您需要检查返回值。

  • LoadLibrary 通过返回 0 报告失败。
  • GetProcAddress 通过返回 nil 报告失败。

您必须仔细阅读函数的文档,但这是一个非常常见的主题。每个 Win32 API 函数可能以不同的方式处理错误。分别阅读每个函数的文档。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2011-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多