我在网上找到了这个代码并且它正在运行,但是我不确定是否可以从另一个线程直接读取主线程中的变量。在此示例中,标志(变量)是 CancelCopy 。 一般来说,我想知道如何从另一个线程中的主线程中读取变量的状态,但是立即无需等待。
dostuff
答案 0 :(得分:2)
从技术上讲,您显示的代码很好,并且可以按预期工作。
然而,它有一个小错误。您将错误的指针值传递给pbCancel
的{{1}}参数。但是,您的代码不会崩溃,因为您传递的指针实际上设置为CopyFileEx()
,而nil
将接受pbCancel
指针,因此nil
将忽略该参数
您假设要做的是传递CopyFileEx()
变量的地址,您可以随时将其设置为BOOL
以取消复制。 TRUE
将为您监视该变量,当设置变量时,您不需要从回调中手动返回CopyFileEx()
(如果您的回调遇到与副本无关的错误,则返回PROGRESS_CANCEL
本身,并且您希望因错误而中止副本)。不过,我不会使用全局变量。我会使用一个执行副本的Form的本地变量。
尝试更像这样的东西:
PROGRESS_CANCEL
说完了,还有其他需要注意的事项 - 你将type
TFormMain = class(TForm)
...
private
CancelCopy: BOOL; // <-- BOOL, not Boolean
...
end;
...
type
TCopyEx = record
Source: String;
Dest: String;
Handle: HWND;
PCancelCopy: PBOOL;
end;
PCopyEx = ^TCopyEx;
const
CFEX_CANCEL = WM_USER + 1;
function CopyFileProgress(TotalFileSize, TotalBytesTransferred, StreamSize,
StreamBytesTransferred: LARGE_INTEGER; dwStreamNumber, dwCallbackReason: DWORD;
hSourceFile, hDestinationFile: THandle; lpData: Pointer): DWORD; stdcall;
begin
// no need to watch CancelCopy here...
// do normal status handling here as needed...
// use PCopyEx(lpData)^ as needed...
end;
function CopyExThread(p: PCopyEx): Integer;
begin
try
if not CopyFileEx(PChar(p.Source), PChar(p.Dest), @CopyFileProgress, p, p.PCancelCopy, COPY_FILE_NO_BUFFERING) then
begin
if GetLastError() = ERROR_REQUEST_ABORTED then
SendMessage(p.Handle, CFEX_CANCEL, 0, 0);
end;
finally
Dispose(p);
end;
Result := 0;
end;
procedure TFormMain.ButtonCopyClick(Sender: TObject);
var
Params: PCopyEx;
ThreadID: Cardinal;
begin
New(Params);
Params.Source := EditOriginal.Text;
Params.Dest := EditCopied.Text;
Params.Handle := Handle;
Params.PCancelCopy := @CancelCopy; // <-- pass address of CancelCopy here...
CancelCopy := FALSE;
CloseHandle(BeginThread(nil, 0, @CopyExThread, Params, 0, ThreadID));
end;
procedure TFormMain.ButtonCancelClick(Sender: TObject);
begin
CancelCopy := TRUE;
end;
从HWND
属性传递给线程。如果TForm.Handle
在线程仍在运行时由于任何原因(并且是,它可能发生)导致TForm
被破坏/重新创建,则HWND
值将指向无效窗口(或者更糟糕的是,重新使用旧的TCopyEx.Handle
值的新窗口。)
通常,HWND
属性不是线程安全的,因此仅仅因为这个原因,将TWinControl.Handle
对象的HWND
传递给一个不是一个好主意。工作线程,除非你能保证在线程运行时不会销毁TWinControl
(在这个例子中,这是不可保证的)。
在这个例子中,我将使用一个不同的HWND
,保证在线程的生命周期内是持久的,例如HWND
窗口(发送到此窗口的消息可以通过{处理) {3}}),或调用TApplication.HookMainWindow()
的结果。
例如:
TApplication.Handle