1

我需要在 Delphi 中启动一个线程并为此使用以下代码:

function ThreadFunc(tp: PThreadParams): Integer;
var
    I: Integer;
begin
    OutputDebugString(PChar('ThreadFunc, 1'));
    for I := 0 to 10000 do
    begin
        if (I MOD 100) = 0 then
        begin
                OutputDebugString(PChar('Sample Delphi DLL ' + IntToStr(I)));
        end;

    end;

    Dispose(tp);
end;

procedure RunThread;
var
tp : PThreadParams;
Thread : THandle;
ThreadID : Cardinal;
ExitCode : Cardinal;
begin
    New(tp);

    OutputDebugString(PChar('RunThread, 1'));

    Thread := BeginThread(nil, 0, @ThreadFunc, tp, 0, ThreadID);

    OutputDebugString(PChar('RunThread, 2. ThreadID: ' + IntToStr(ThreadID)));

    WaitForSingleObject(Thread, INFINITE);

    GetExitCodeThread(Thread, ExitCode);

    CloseHandle(Thread);
end;

当我运行 RunThread 时,日志文件包含条目

运行线程,1

运行线程,2。线程 ID:...

但没有来自ThreadFunc.

我应该如何更改此代码才能执行实际的线程函数(ThreadFunc)?

4

2 回答 2

5

为什么不使用 TThread?使实现线程变得非常容易。;-)

于 2012-09-18T11:59:14.387 回答
0

在输出调试字符串上

您的问题可能与 OutputDebugString 以及 Delphi IDE 如何处理它有关。你的线程没有问题。它运行正常。它的唯一缺点是 ThreadFunc 必须返回一个值,而在您的情况下它不会。请参阅https://msdn.microsoft.com/en-us/library/windows/desktop/ms686736 (v=vs.85).aspx(仅返回零 - 最简单的情况)。

要检查 Delphi 是否正确处理 OutputDebugString,请使用替代应用程序来显示调试消息。

退出 Delphi,在https://technet.microsoft.com/en-us/sysinternals/debugview.aspx从 Windows Sysinternals 获取 DebugView ,运行它,并在运行时运行您的应用程序(只要确保 Delphi 不是跑步)。

在 DebugView 日志中,您将看到类似的内容:

[8484] RunThread, 1
[8484] RunThread, 2. ThreadID: 8388
[8484] ThreadFunc, 1
[8484] Sample Delphi DLL 0
[8484] Sample Delphi DLL 100
[8484] Sample Delphi DLL 200
<<< 96 lines skipped >>>
[8484] Sample Delphi DLL 9900
[8484] Sample Delphi DLL 10000

OutputDebugString 太晦涩难懂,无法依赖它。只是更简单的方法来确保您的线程正在运行,例如创建文件或调用哔声。所以,这个想法是,如果 DebugView 没有帮助,只需从您的线程中输出调试字符串并使用其他方法确保线程正在运行。例如,将以下字符串添加到 ThreadFunc 的开头:

MessageBox(0, 'Thread Text', 'Thread Caption', MB_OK);

运行您的应用程序。如果您将看到消息对话框,那么您的线程运行正常。

关于召唤约定

调用约定(stdcall vs register)在这里不是问题。从这个意义上说,您的代码是正确的。在 System.pas 中查找 BeginThread 函数的实现(以及 TThreadFunc 的定义,如果它存在)。

您没有提及您使用的是什么版本的 Delphi,或者您可能正在使用其他兼容的编译器 - 这可能会影响类型,但这种可能性极低。Delphi 10.2 使用 ThreadWrapper 函数将其传递给 Windows API CreateThread()。这个 ThreadWrapper 是使用“stdcall”调用约定定义的,这是 Win32 规范所要求的,因为它直接传递给运行的 CreateThread()。ThreadWrapper 又调用您具有注册调用约定的 TThreadFunc。TThreadFunc 的定义方式如下(简单明了):

TThreadFunc = function(Parameter: Pointer): Integer;

因此,正如上面的代码所示,您的 ThreadFunc 必须与 TThreadFunc 的声明完全匹配。它不必像某些人在评论中所说的那样是“stdcall”。如果您的 Delphi 版本在 System.pas 中没有 TThreadFunc,请查找 BeginThread 的第三个参数所需的类型,以确保一切正确。

如果您将编译器选项“Typed @ operator”打开(默认情况下为关闭),并且如果问题出在调用约定不匹配,则编译器会给出错误。我建议始终选中“Typed @ operator”选项。或者,在您的 CreateThread 调用之前添加 {$T+} (或者在文件开头更好)。因此,您将确保没有不匹配。

正如我所解释的,事情表明调用约定在这里不是问题,并且您的代码中的一切都是正确的。如果调用约定不合适,则 ThreadFunc 将一直运行到最后,因为在退出 (Dispose(tp)) 之前不需要任何参数,因此在执行所有 OutputDebugString 调用后会出现访问冲突错误– 在 Dispose(tp) 或从 ThreadFunc 退出时。您会在调试器中看到输出,然后出现访问冲突。由于没有访问冲突 - 调用约定一切正常。

于 2017-05-10T15:45:00.653 回答