Is there any way to name my threads for debugging? lazarus / 62-General ---------------------------------------------------------------------- avra · 2016-05-25 09:45 I have tried to do it Delphi way but unfortunately this is not possible yet. According to http://www.freepascal.org/docs- html/rtl/classes/tthread.namethreadfordebugging.html it seams that NameThreadForDebugging() "is not implemented for any FPC platform and is supported for Delphi compatibility only". Is there any other way to name my threads so that I can see their names in debugger? Right now I just care for Win32. ---------------------------------------------------------------------- balazsszekely · 2016-05-25 10:11 Perhaps this: http://stackoverflow.com/a/9090608 ---------------------------------------------------------------------- skalogryz · 2016-05-25 13:28 I'm wondering if something like this could be achieved via using "threadvars" with using a variable (of shortstring type?) to stored the name and inspecting the value of the variable during the debugging. Should be a cross-platform solution ---------------------------------------------------------------------- avra · 2016-06-07 12:31 Quote from: GetMem on May 25, 2016, 12:11:20 pmPerhaps this: http://stackoverflow.com/a/9090608Unfortunately not. MS_VC_EXCEPTION is raised and app ends, but thread name is not updated in threads debug window. Just tested on WinXP SP3 with CT 5.7. ---------------------------------------------------------------------- marcov · 2016-06-07 12:37 Try recompiling FPC with -dTEST_WIN32_SEH ---------------------------------------------------------------------- Pascal · 2018-01-08 13:30 This does not seem to work for win64/x86_64. Any hint to set the names for the thread window? I use this code to set the name in the Execute procedure:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure NameThreadForDebugging(const pName: String);  type    TThreadNameInfo =      record        RecType: LongWord;        Name: PAnsiChar;      ThreadID: LongWord;        Flags: LongWord;      end;var    info:TThreadNameInfo;    AnsiName: AnsiString; begin    AnsiName:= pName;    info.RecType := $1000;    info.Name := PAnsiChar(AnsiName);    info.ThreadID := $FFFFFFFF;    info.Flags := 0;    try        RaiseException($406D1388, 0,        SizeOf(info) div SizeOf(LongWord), PDWord(@info));    except    end;end; ---------------------------------------------------------------------- Thaddy · 2018-01-08 15:07 Quote from: marcov on June 07, 2016, 02:37:25 pmTry recompiling FPC with -dTEST_WIN32_SEH does not matter: not implemented. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$ifdef THREADNAME_IS_ANSISTRING}{ the platform implements the AnsiString variant and the UnicodeString variant  simply calls the AnsiString variant }class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);begin  NameThreadForDebugging(AnsiString(aThreadName), aThreadID);end;   {$ifndef HAS_TTHREAD_NAMETHREADFORDEBUGGING}class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);begin  { empty }end;  {$endif}{$else}  {$ifndef HAS_TTHREAD_NAMETHREADFORDEBUGGING}{ the platform implements the UnicodeString variant and the AnsiString variant  simply calls the UnicodeString variant }class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);begin  { empty }end;  {$endif}  class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);begin  NameThreadForDebugging(UnicodeString(aThreadName), aThreadID);end;{$endif} At least on Windows, iit should not be too diificult: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx or better: https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a- thread-name-in-native-code ---------------------------------------------------------------------- Pascal · 2018-01-08 15:20 Quote from: Thaddy on January 08, 2018, 04:07:05 pmAt least on Windows, iit should not be too diificult: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx or better: https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a- thread-name-in-native-code That's what i did. Don't get confused by the Name i used. I used a procedure in an common unit and und used unit name to reference. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i myunit.NameThreadForDebugging('myname'); ---------------------------------------------------------------------- Thaddy · 2018-01-08 15:38 Quote from: Pascal on January 08, 2018, 04:20:26 pmQuote from: Thaddy on January 08, 2018, 04:07:05 pmAt least on Windows, iit should not be too diificult: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx or better: https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a- thread-name-in-native-code That's what i did. Don't get confused by the Name i used. I used a procedure in an common unit and und used unit name to reference. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i myunit.NameThreadForDebugging('myname'); you are not paying attention: The implementation for this is missing in the RTL: This is the relevant part if you analyze codeflow: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);begin  { empty }end; You you can call what you like: nothing happens. ---------------------------------------------------------------------- Pascal · 2018-01-08 17:17 Quote from: Thaddy on January 08, 2018, 04:38:02 pmQuote from: Pascal on January 08, 2018, 04:20:26 pmQuote from: Thaddy on January 08, 2018, 04:07:05 pmAt least on Windows, iit should not be too diificult: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx or better: https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a- thread-name-in-native-code That's what i did. Don't get confused by the Name i used. I used a procedure in an common unit and und used unit name to reference. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i myunit.NameThreadForDebugging('myname'); you are not paying attention: The implementation for this is missing in the RTL: This is the relevant part if you analyze codeflow: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);begin  { empty }end; You you can call what you like: nothing happens. Sorry, Thaddy, in this case you didn't pay attention to my original post. I didn't call the procedure you mentioned. I called my own, which just has the same name! ---------------------------------------------------------------------- Pascal · 2018-01-09 11:38 If i understand the Microsoft documentaion aright, this is only a hack to pass the name to the debugger. Is GDB aware of this special exception? Has anyone manged to get this working? ---------------------------------------------------------------------- Thaddy · 2018-01-09 11:59 The pragma is simply the same as {$PUSH} {$ALIGN 8} The record {$POP}. SEH expects 8 byte alignment for the records. Of course it works. And of course only on windows. ---------------------------------------------------------------------- Pascal · 2018-01-09 12:51 Sorry, i didn't get it working: Here is the procedure i use in the threads execute method: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$PUSH} {$ALIGN 8}procedure NameThreadForDebugging(const pName: String); type  TThreadNameInfo = record    RecType: LongWord;    Name: PAnsiChar;    ThreadID: LongWord;    Flags: LongWord;  end;var  info:TThreadNameInfo;  AnsiName: AnsiString;begin  AnsiName:= pName;  info.RecType := $1000;  info.Name := PAnsiChar(AnsiName);  info.ThreadID := $FFFFFFFF;  info.Flags := 0;  try      RaiseException($406D1388, 0,      SizeOf(info) div SizeOf(LongWord), LPDWORD(@info));  except  end;  DebugLn('NameThreadForDebugging(''' + pName + ''')');end;{$POP} gdb 8.0.1 x86_64 fpc is compiled with OPT="-gl -dTEST_WIN32_SEH" ---------------------------------------------------------------------- Thaddy · 2018-01-09 15:16 That's because the translation is incorrect. I don't have my code ready here, but this should come close: - It needs to be a member of a TThread derived class - You can't allocate nor define the record inside the procedure, it needs to be on the heap (global) and not on the stack (local) anyway = You should use stdcall - You should use PWideChar (the routine calls the widechar version anyway as per MSDN) - You should copy in the Pwidechar, not assign the pointer. AND this need SEH enabled for your complete FPC build (*everything* full fpc build, full library build) and I seem to remember I also protected the WideChar with a critical section, although that should not be necessary. At least w/o being able to test this is much closer. I'll give the proper code later. First get this outside the procedure:  type {$push}{align 8}   TThreadNameInfo = record     RecType: LongWord;     Name: PAnsiChar;     ThreadID: LongWord;     Flags: LongWord;   end; {$pop} Note it was not an easy task to get it to work. ---------------------------------------------------------------------- Pascal · 2018-01-09 19:50 According compiler\options.pas SEH is used by default for x86_64/win64 and it only has to be enabled for i386_win32: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$ifndef DISABLE_WIN64_SEH}    if target_info.system=system_x86_64_win64 then    def_system_macro('FPC_USE_WIN64_SEH');{$endif DISABLE_WIN64_SEH} {$ifdef TEST_WIN32_SEH}    if target_info.system=system_i386_win32 then      def_system_macro('FPC_USE_WIN32_SEH');{$endif TEST_WIN32_SEH} Is it possible that this only works for i386/win32? ---------------------------------------------------------------------- Remy Lebeau · 2018-01-09 22:54 Quote from: Thaddy on January 09, 2018, 04:16:04 pm- It needs to be a member of a TThread derived class Not necessary.  All you need is a Thread ID, or -1 for the current thread. Quote from: Thaddy on January 09, 2018, 04:16:04 pm- You can't allocate nor define the record inside the procedure, it needs to be on the heap (global) and not on the stack (local) anyway Not true.  Look at the MSDN example, it allocates on the stack.  So does Delphi. Quote from: Thaddy on January 09, 2018, 04:16:04 pm- You should use stdcall Not necessary. Quote from: Thaddy on January 09, 2018, 04:16:04 pm- You should use PWideChar (the routine calls the widechar version anyway as per MSDN) Wrong.  The name must be specified in ANSI, not in Unicode.  Look at the MSDN example again, it uses 'const char*', which is ANSI. Quote from: Thaddy on January 09, 2018, 04:16:04 pm- You should copy in the Pwidechar, not assign the pointer. Not necessary.  Copying the pointer is fine. Quote from: Thaddy on January 09, 2018, 04:16:04 pmAND this need SEH enabled for your complete FPC build (*everything* full fpc build, full library build) Is there a way to detect in app code whether SEH support is enabled at compile-time? Quote from: Thaddy on January 09, 2018, 04:16:04 pmI seem to remember I also protected the WideChar with a critical section, although that should not be necessary. Nope. ---------------------------------------------------------------------- engkin · 2018-01-10 04:38 According to GDB news page June 4th, 2017 - GDB 8.0: "Support for thread names on MS-Windows" I believe the version of GDB that ships with Lazarus is old (2014?), so I don't think it supports MS thread naming. Recompiling everything just to get SEH support to set thread names seems a bit too much. I decided to do it in assembly. Someone already did something similar. I used that code to do a quick test: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TThreadNameInfo =    record      RecType: DWord;      Name: pointer;//PAnsiChar;      ThreadID: DWord;      Flags: DWord;    end; var  info: TThreadNameInfo; procedure NameThreadForDebugging(const pName: pointer);begin    info.RecType := $1000;    info.Name := pName;    info.ThreadID := $FFFFFFFF;    info.Flags := 0;    //try        Windows.RaiseException($406D1388, 0, SizeOf(info) div SizeOf(DWord), Align(PDWord(@info),8));    //except  //end;end; {$AsmMode intel} function NameThread(vName:pointer):boolea n;assembler;nostackframe;stdcall;labelCLEANUP_label,next_instruction;a sm  PUSH EBP //set up function's stack frame  MOV EBP, ESP   CALL next_instruction //(next_instruction) //get EIP of next instruction (after 5-byte CALL) - MASM-syntaxnext_instruction:  POP EAX //EAX now equals whatever memory location THIS instruction is loaded at  ADD EAX, 01Ch //apply relative offset so EAX now points to our exception- handler entry point  PUSH EAX //build EXCEPTION_REGISTRATION structure on stack, first with our handler  PUSH DWORD PTR FS:[0] // followed by previous handler in chain  MOV DWORD PTR FS:[0], ESP //install our exception_frame to be first in chain   MOV EAX, DWORD PTR SS:[EBP+8] //eax = pointer passed as argument #1  call NameThreadForDebugging  //MOV EAX, DWORD PTR DS:[EAX] //eax = *eax / dereference pointer - can we read DWORD memory?  XOR EAX, EAX //if we got here, no exception occurred, memory is readable, return zero (eax=0)   //JMP SHORT CLEANUP_label (0000000Fh) //skip past exception_handler code  JMP SHORT CLEANUP_label //skip past exception_handler code   XOR EAX, EAX //exception_handler entry point  INC EAX //return 1   MOV ESP, DWORD PTR FS:[0] // **** SWALLOW EXCEPTION BY ****  MOV ESP, DWORD PTR SS:[ESP] // **** RESTORING ESP ****CLEANUP_label:  POP DWORD PTR FS:[0] //CLEANUP_label - restore previous exception handler  ADD ESP, 4 //remove our exception_handler from stack  POP EBP //restore caller's stack frame  RETN 4 //remove STDCALL argument and return from functionend; I don't know if it works, as I don't have a debugger that supports it.  Calling NameThread(@s[1]); supposed to change the name of the current thread. Edit: OllyDbg 2.0 supports "Thread names (MS_VC_EXCEPTION)". Tested the code and it showed the name of the thread. Check attached image. ---------------------------------------------------------------------- Pascal · 2018-01-10 05:30 I use gdb 8.0.1 for x86_64. Can't find an executable for i386 atm. @remy: Can you provide a sample implemantation for x86_64? @engkin: How to adapt the assembly code to work on x86_64? ---------------------------------------------------------------------- engkin · 2018-01-10 05:38 Quote from: Pascal on January 10, 2018, 06:30:28 am@engkin: How to adapt the assembly code to work on x86_64? Maybe this helps as a starting point? It seems that MS added a new function for Windows 10 SetThreadDescription ---------------------------------------------------------------------- Pascal · 2018-01-10 07:06 Quote from: engkin on January 10, 2018, 06:38:10 amIt seems that MS added a new function for Windows 10 SetThreadDescription Yes, i know from 1607 on, but then gdb or at least Lazarus has to call GetThreadDescription for thread window. ---------------------------------------------------------------------- Pascal · 2018-01-10 13:05 Does anyone have working code for x86_64 to name threads for debugging? ---------------------------------------------------------------------- balazsszekely · 2018-01-10 16:07 Quote@Pascal Does anyone have working code for x86_64 to name threads for debugging?I just noticed that while debugging a specific thread,  the gdb sends back the ThreadID(see screenshot). This can be used to add unique names to each thread created from your application. The name is virtual, but it will show up in Debug Windows-->Threads dialog. All you have to do is add a TList/TObjectList to TDebuggerIntf class(components/debuggerintf/dbgintfdebuggerbase.pp), the list will hold the name and ID of every thread you wish to monitor. When the gdb sends the notify message, you compare the ids with the internal list and assign the name accordingly. I'm not saying it's easy to implement but it can be done. As a second screenshot I attach all the relevant units. ---------------------------------------------------------------------- Thaddy · 2018-01-10 16:51 Tnx Remy. Appreciated. ---------------------------------------------------------------------- Pascal · 2018-01-10 19:08 Quote from: GetMem on January 10, 2018, 05:07:21 pmQuote@Pascal Does anyone have working code for x86_64 to name threads for debugging?I just noticed that while debugging a specific thread,  the gdb sends back the ThreadID(see screenshot). This can be used to add unique names to each thread created from your application. The name is virtual, but it will show up in Debug Windows-->Threads dialog. All you have to do is add a TList/TObjectList to TDebuggerIntf class(components/debuggerintf/dbgintfdebuggerbase.pp), the list will hold the name and ID of every thread you wish to monitor. When the gdb sends the notify message, you compare the ids with the internal list and assign the name accordingly. I'm not saying it's easy to implement but it can be done. As a second screenshot I attach all the relevant units. I also had this idea. But will it need IPC to share the information or is there an object which is accessible from Lazarus and my app? ---------------------------------------------------------------------- balazsszekely · 2018-01-10 19:38 QuoteI also had this idea. But will it need IPC to share the information The communication between Lazarus and gdb is already implemented. Gdb sends the information about each thread as a long string. The IDE splits the string and display the values in ThreadDlg dialog.  Unfortunately the name of the thread is eaten by gdb, the only link is the threadid.  Quoteis there an object which is accessible from Lazarus and my app?In order to be accessible from your application, you have to implement it through debuggerintf interface. When you create a thread you must pass the name and the id to a function which stores the values in a TList(or something else). The values in the list will be compared to the strings received from gdb. ---------------------------------------------------------------------- Pascal · 2018-01-10 19:47 I've understand that so far. The question is: How do i get a reference to the debuggerintf interface in my app? I need the same as in Lazarus which handles the messages from gdb. Or do i have to setup some kind of IPC to send the information from my app to Lazarus or to receive the reference to the interface from Lazarus? ---------------------------------------------------------------------- balazsszekely · 2018-01-10 19:54 By adding DebuggerIntf package to the "Required Packages", or by creating a package(IDE add-on) similar to LazProfiler. Please note the DebuggerIntf most likely must be extended. ---------------------------------------------------------------------- Pascal · 2018-01-10 19:59 And that is really the same instance, not an app local interface with no connection to Lazarus? In a package like LazProfiler it is obvious, it's linked into Lazarus. But in an standalone app i do not see the connection to Lazarus. Edit: If i add DebuggerIntf package to my app DebugBossManager is not instantiated, so no link to Lazarus! ---------------------------------------------------------------------- balazsszekely · 2018-01-10 20:08 Your application is debugged by lazarus, there is a connection. Just open lazarus.lpi, then but a break point to Threaddlg.pp(line 102) and debug it step by step. ---------------------------------------------------------------------- Pascal · 2018-01-10 20:15 GetMem, many thanks  for your help so far. I'll try to implement it the next days, even though i am skeptical it'll work with debugging apps other than Lazarus itself. ---------------------------------------------------------------------- balazsszekely · 2018-01-10 20:50 Pascal there is no need for thanks. If the DebuggerIntf idea is not working you can try something else, like sending a message to ThreadDlg window with PostMessage for example.  Unfortunately we are limited by the gdb, a custom debugger would be the ideal solution. ---------------------------------------------------------------------- balazsszekely · 2018-01-12 10:01 Hi Pascal, Please apply the attached patch(LazFiles.zip), then test the attached project(ThreadName.zip). The patch modifies the following files, please do backup before start:    $(LazarusDir)\components\debuggerintf\dbgintfdebuggerbase.pp    $(LazarusDir)\debugger\threaddlg.pp    $(LazarusDir)\ide\debugmanager.pas With the patch, I also attach the units if you need them. After IDE rebuild, open the project, put a break point in the thread execute method, run the project then press the "Create thread" button. You should see the thread name as in the attached screenshot. Tested with 64 bit Lazarus too. regards, GetMem ---------------------------------------------------------------------- Pascal · 2018-01-12 11:07 Great  And now integrate it into TThread.NameThreadForDebugging and we have it working crossplatform ---------------------------------------------------------------------- balazsszekely · 2018-01-12 12:17 QuoteAnd now integrate it into TThread.NameThreadForDebugging and we have it working crossplatform It is cross-platform already. I will ask the core developers if this or a similar solution is accepted in Lazarus. TThread.NameThreadForDebugging is in FPC, adding the name there it's useless because of the gdb. ---------------------------------------------------------------------- Pascal · 2018-01-12 12:48 Quote from: GetMem on January 12, 2018, 01:17:52 pmQuoteAnd now integrate it into TThread.NameThreadForDebugging and we have it working crossplatform It is cross-platform already. I will ask the core developers if this or a similar solution is accepted in Lazarus. TThread.NameThreadForDebugging is in FPC, adding the name there it's useless because of the gdb. Yes, but you can integrate it in fpc and if NameThreadForDebugging is used it'll work. All you need is in fpc. Edit: Of cause this is only usefull if it's used with Lazarus. ---------------------------------------------------------------------- Pascal · 2018-01-12 13:24 Have a look at the attched patch for fpc. It's neither tested nor build so far. ---------------------------------------------------------------------- balazsszekely · 2018-01-12 15:16 QuoteHave a look at the attched patch for fpc.Looks good but I seriously doubt it will be accepted by FPC developers. The code is only useful when you debug an application from Lazarus, why should be present in classes.inc? At least this is how I see it. ---------------------------------------------------------------------- Pascal · 2018-01-12 16:11 Quote from: GetMem on January 12, 2018, 04:16:57 pmLooks good but I seriously doubt it will be accepted by FPC developers. The code is only useful when you debug an application from Lazarus, why should be present in classes.inc? At least this is how I see it. Yes, but that's another story. Maybe there could be implemented an interface class for Lazarus which can be instantiated by Lazarus apps and fpc classes use it if assigned. I'll update the patch and test it. ---------------------------------------------------------------------- Thaddy · 2018-01-12 16:25 Quote from: Pascal on January 12, 2018, 05:11:47 pmQuote from: GetMem on January 12, 2018, 04:16:57 pmLooks good but I seriously doubt it will be accepted by FPC developers. The code is only useful when you debug an application from Lazarus, why should be present in classes.inc? At least this is how I see it. Yes, but that's another story. No it is not: classes unit should be clean. It is the base unit for classes. Basically introducing something like this is a sin. QuoteMaybe there could be implemented an interface class for Lazarus which can be instantiated by Lazarus apps and fpc classes use it if assigned. I'll update the patch and test it. The implementation can only be for platform(s) that support it which I count as one (one). So it should be in the windows specific code. ---------------------------------------------------------------------- balazsszekely · 2018-01-12 16:30 Quote@Thaddy The implementation can only be for platform(s) that support it which I count as one (one). So it should be in the windows specific code.Just apply the patch(see above), then test the project. In theory it should work in every platform. In fact this is a good chance to test it on raspberry pi. Can you please do a quick test? PS: I already tested on Linux mint and works fine. PS1: If it's not working then IPC fails on that specific platform. IPC is a built in FPC feature. ---------------------------------------------------------------------- Thaddy · 2018-01-12 21:07 10:00 CET tomorrow morning ---------------------------------------------------------------------- Pascal · 2018-01-13 06:52 I integrated an interface class to fpc and fpc compiles fine with -dFPC_USE_LAZINTF. (See atached patches). Unit lazintf is placed in /rtl/objpas. But it's not build during build of fpc. What do i have to do to make it build during fpc build? ---------------------------------------------------------------------- balazsszekely · 2018-01-13 07:25 QuoteBut it's not build during build of fpc. What do i have to do to make it build during fpc build?Try to add lazintf to fpmake from /rtl/. ---------------------------------------------------------------------- Pascal · 2018-01-15 07:00 Quote from: Thaddy on January 12, 2018, 10:07:01 pm10:00 CET tomorrow morning  Good morning Thaddy, did you test it? ---------------------------------------------------------------------- Pascal · 2018-01-18 06:55 Quote from: Pascal on January 15, 2018, 08:00:23 amQuote from: Thaddy on January 12, 2018, 10:07:01 pm10:00 CET tomorrow morning  Good morning Thaddy, did you test it? Thaddy, did you had a chance to test it? ---------------------------------------------------------------------- Thaddy · 2018-01-18 07:35 Not yet. I couldn't build lazarus from trunk for  a day or two (37962 issue). I'll have a look. ---------------------------------------------------------------------- Pascal · 2018-01-18 09:11 Quote from: Thaddy on January 18, 2018, 08:35:27 amNot yet. I couldn't build lazarus from trunk for  a day or two (37962 issue). I'll have a look. This is fixed in the meantime. But you do not need latest fpc. Just GetMem's patches to Lazars. Forget about my fpc integration, it doesn't work atm. ---------------------------------------------------------------------- Bi0T1N · 2020-04-08 18:23 Quote from: Bi0T1N on April 07, 2020, 10:25:13 pmI've had a look into the ThreadManager mechanism because I thought about implementing thread naming as TThread.NameThreadForDebugging doesn't do anything yet. It's quite easy to implement if you write an own function (see below) but TThread.NameThreadForDebugging is defined as class procedure which makes it much more difficult as you cannot access the needed class variables. The new SetThreadDescription needs a HANDLE to the thread (also works without debugger, see screenshot) instead of the ThreadID which could be circumvented by using GetCurrentThread if it's -1 (default value). But if a ThreadID is given this won't work so that you probably have to use OpenThread. And then another fallback is needed if SetThreadDescription is not available (older Windows) which checks if IsDebuggerPresent and raises an exception but therefore Lazarus has to update its gdb version to at least 8.0 or newer as engkin pointed out. There it almost seems easier to implement it for most unix platforms via pthread_setname_np. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program threads; {$mode Delphi} uses  SysUtils, Classes;  function SetThreadDescription(threadHandle: THandle; pcwstr: PWideChar): HResult; stdcall; external KernelDLL name 'SetThreadDescription'; type  TMyThread = class(TThread)  protected  procedure Execute; override;    procedure NameThreadForDebugging1(aThreadName: UnicodeString; aThreadID: TThreadID = TThreadID(-1));    procedure NameThreadForDebugging1(aThreadName: AnsiString; aThreadID: TThreadID = TThreadID(-1));  end;  procedure TMyThread.Execute;begin  writeln('hello from thread '+ThreadID.ToString);  sleep(30000);end; procedure TMyThread.NameThreadForDebugging1(aThreadName: AnsiString; aThreadID: TThreadID);begin  writeln('ansi '+ThreadID.ToString);  NameThreadForDebugging1(UnicodeString(aThreadName), aThreadID);end; procedure TMyThread.NameThreadForDebugging1(aThreadName: UnicodeString; aThreadID: TThreadID);begin  writeln('uni threadid '+ThreadID.ToString);  writeln('uni threadhandle '+Handle.ToString);  SetThreadDescription(Handle, @aThreadName[1]);end; var  threads: TArray;  i: Integer;  thread: TMyThread; begin  writeln('start');  SetLength(threads, 5);  for i := Low(threads) to High(threads) do  begin    threads[i] := TMyThread.Create(True);    threads[i].NameThreadForDebugging1(IntToStr(i));  end;    for thread in threads do  begin    thread.Start;  end;   for thread in threads do  begin    thread.WaitFor;  end;   writeln('done');  readln;end. EDIT: My Lazarus 2.0.6 installation via fpcupdeluxe comes with GNU gdb (GDB) 7.7.1 (win32) and GNU gdb (GDB) 7.3.50.20110510-cvs (win64). ---------------------------------------------------------------------- ASerge · 2020-04-08 18:28 Quote from: Bi0T1N on April 08, 2020, 08:23:53 pmThe new SetThreadDescription needs a HANDLE to the thread... As a result, the code should be more complex. Because in this form the program will not even start on Windows 7. ---------------------------------------------------------------------- Bi0T1N · 2020-04-08 18:35 Quote from: ASerge on April 08, 2020, 08:28:15 pmQuote from: Bi0T1N on April 08, 2020, 08:23:53 pmThe new SetThreadDescription needs a HANDLE to the thread... As a result, the code should be more complex. Because in this form the program will not even start on Windows 7. That's why I wrote: Quote from: Bi0T1N on April 08, 2020, 08:23:53 pmAnd then another fallback is needed if SetThreadDescription is not available (older Windows) which checks if IsDebuggerPresent and raises an exception... Use GetProcAddress for trying to load the SetThreadDescription function and if the result is null, do what I've mentioned before. ---------------------------------------------------------------------- ASerge · 2020-04-08 19:37 Quote from: Bi0T1N on April 08, 2020, 08:35:41 pmUse GetProcAddress for trying to load the SetThreadDescription function and if the result is null, do what I've mentioned before. This will help avoid loading error, but it will not change the situation in principle for WIndows 7. ---------------------------------------------------------------------- Bi0T1N · 2020-04-09 10:05 Quote from: ASerge on April 08, 2020, 09:37:58 pmQuote from: Bi0T1N on April 08, 2020, 08:35:41 pmUse GetProcAddress for trying to load the SetThreadDescription function and if the result is null, do what I've mentioned before. This will help avoid loading error, but it will not change the situation in principle for WIndows 7. Could you please explain what exactly you mean? ---------------------------------------------------------------------- PascalDragon · 2020-04-09 11:22 Quote from: ASerge on April 08, 2020, 09:37:58 pmQuote from: Bi0T1N on April 08, 2020, 08:35:41 pmUse GetProcAddress for trying to load the SetThreadDescription function and if the result is null, do what I've mentioned before. This will help avoid loading error, but it will not change the situation in principle for WIndows 7. Yes, it will, because raising a (specific) exception is the way to name the thread (see here). ---------------------------------------------------------------------- Bi0T1N · 2020-04-09 20:06 This versions works with both (old & new) variants on a recent Windows 10 Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program threads; {$mode Delphi} uses  SysUtils, Classes; type  TSetThreadDescription = function(threadHandle: THandle; pcwstr: PWideChar): HResult; stdcall; //function SetThreadDescription(threadHandle: THandle; pcwstr: PWideChar): HResult; stdcall; external KernelDLL name 'SetThreadDescription';function GetCurrentThread(): THandle; stdcall; external KernelDLL name 'GetCurrentThread';function OpenThread(dwDesiredAccess: DWord; bInheritHandle: Boolean; dwThreadId: DWord): THandle; stdcall; external KernelDLL name 'OpenThread';function IsDebuggerPresent(): Boolean; stdcall; external KernelDLL name 'IsDebuggerPresent';procedure RaiseException(dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWord; const lpArguments: PUInt64); stdcall; external KernelDLL name 'RaiseException';function GetModuleHandle(lpModuleName: PAnsiChar): THandle; stdcall; external KernelDLL name 'GetModuleHandleA'; var  KernelHandle : THandle;  SetThreadDescription: TSetThreadDescription; type  TMyThread = class(TThread)  private    i: Integer;  protected    procedure Execute; override;    procedure SetNumber(const aNum: Integer);    procedure NameThreadForDebugging1(aThreadName: UnicodeString; aThreadID: TThreadID = TThreadID(-1));    procedure NameThreadForDebugging1(aThreadName: AnsiString; aThreadID: TThreadID = TThreadID(-1));    class procedure NameThreadForDebuggingTest(aThreadName: UnicodeString; aThreadID: TThreadID = TThreadID(-1));  end;  procedure TMyThread.Execute;begin  writeln('hello from thread '+ThreadID.ToString);  NameThreadForDebugging1(IntToStr(i));  sleep(30000);end; procedure TMyThread.SetNumber(const aNum: Integer);begin  i := aNum;end; procedure TMyThread.NameThreadForDebugging1(aThreadName: AnsiString; aThreadID: TThreadID);begin  writeln('ansi '+ThreadID.ToString);  NameThreadForDebugging1(UnicodeString(aThreadName), aThreadID);end; procedure TMyThread.NameThreadForDebugging1(aThreadName: UnicodeString; aThreadID: TThreadID);begin  writeln('uni threadid '+ThreadID.ToString);  writeln('uni threadhandle '+Handle.ToString);   NameThreadForDebuggingTest(aThreadName, aThreadID);end; class procedure TMyThread.NameThreadForDebuggingTest(aThreadName: UnicodeString; aThreadID: TThreadID);const  MS_VC_EXCEPTION: DWord = $406D1388;type  THREADNAME_INFO = record    dwType: DWord; // Must be 0x1000.    szName: PAnsiChar; // Pointer to name (in user addr space).    dwThreadID: DWord; // Thread ID (-1=caller thread).    dwFlags: DWord; // Reserved for future use, must be zero.  end;var  ThreadHandle: THandle;  thrdinfo: THREADNAME_INFO;begin  writeln('setting threadname to '+aThreadName);   if Assigned(SetThreadDescription) then  begin    writeln('this Windows has SetThreadDescription');    // at least Windows 10 version 1607 or Windows Server 2016    if aThreadID = TThreadID(-1) then    begin    ThreadHandle := GetCurrentThread();    end    else    begin      ThreadHandle := OpenThread($0400, False, aThreadID);    end;     SetThreadDescription(ThreadHandle, @aThreadName[1]);  end  else  begin    writeln('fallback for older Windows');    // older Windows versions    if IsDebuggerPresent then    begin      writeln('changing name');       thrdinfo.dwType := $1000;      thrdinfo.szName := @AnsiString(aThreadName)[1];      thrdinfo.dwThreadID := aThreadID;    thrdinfo.dwFlags := 0;       RaiseException(MS_VC_EXCEPTION, 0, SizeOf(thrdinfo) div SizeOf(DWord), @thrdinfo);    end;  end;end; var  threads: TArray;  i: Integer;  thread: TMyThread; begin  writeln('start');    KernelHandle := GetModuleHandle(KernelDLL);  if KernelHandle <> 0 then    SetThreadDescription := TSetThreadDescription(GetProcAddress(KernelHandle, 'SetThreadDescription'));    SetLength(threads, 5);  for i := Low(threads) to High(threads) do  begin    threads[i] := TMyThread.Create(True);    threads[i].SetNumber(i);  end;   for thread in threads do  begin    thread.Start;  end;    sleep(10000);   TMyThread.NameThreadForDebuggingTest('test'); // main thread  TMyThread.NameThreadForDebuggingTest('bronze', threads[2].ThreadID); // third thread   for thread in threads do  begin    thread.WaitFor;  end;   writeln('done');  readln;end. @ASerge Would be good if you could test it inside a debugger on Windows 7 (e.g. x64dbg) ---------------------------------------------------------------------- ASerge · 2020-04-09 20:14 Quote from: Bi0T1N on April 09, 2020, 10:06:57 pm@ASerge Would be good if you could test it inside a debugger on Windows 7 (e.g. x64dbg) After adding the Windows unit (otherwise it is not compiled) and running, it returned an error in line 101, because the DWORD parameter was attempted to be assigned a 64-bit value. I think it's better to test it first on Windows 10. ---------------------------------------------------------------------- Bi0T1N · 2020-04-09 20:45 Quote from: ASerge on April 09, 2020, 10:14:25 pmQuote from: Bi0T1N on April 09, 2020, 10:06:57 pm@ASerge Would be good if you could test it inside a debugger on Windows 7 (e.g. x64dbg) After adding the Windows unit (otherwise it is not compiled) and running, it returned an error in line 101, because the DWORD parameter was attempted to be assigned a 64-bit value. I think it's better to test it first on Windows 10. As I said, compiles and works fine with Free Pascal Compiler version 3.3.1-r44644 [2020/04/08] for x86_64. Also no need to add Windows unit. So I guess your compiling 32-bit? ---------------------------------------------------------------------- PascalDragon · 2020-04-10 08:45 Quote from: ASerge on April 09, 2020, 10:14:25 pmQuote from: Bi0T1N on April 09, 2020, 10:06:57 pm@ASerge Would be good if you could test it inside a debugger on Windows 7 (e.g. x64dbg) After adding the Windows unit (otherwise it is not compiled) and running, it returned an error in line 101, because the DWORD parameter was attempted to be assigned a 64-bit value. I think it's better to test it first on Windows 10. Just add a typecast to DWord. TThreadID is supposed to be a 32-bit type which is fixed with 3.2 (thus only the lower 32-bit will be set anyway). ---------------------------------------------------------------------- 440bx · 2020-04-10 09:00 Just thinking out loud here... Wouldn't just having a threadvar set to a string name do the trick ?  it seems that would be preferable since it could be used on versions other than Win10. ---------------------------------------------------------------------- PascalDragon · 2020-04-10 10:00 We are talking about the NameThreadForDebugging method. The idea for this is to utilise the functionality of the operating system to set the thread's name so that it can easily be identifier in the debugger (thus using ThreadSetDescription in current Windows 10, raising a specific exception in older Windows systems (if a debugger is attached) and using pthread_setname_np on POSIX systems). While a thread variable would be useful for having the thread's name accessible in the Pascal side this isn't as easy on the debugger side then (where identifying a thread is more often than not more important; I've been there...), cause thread variables aren't that easily accessed from the debugger (I don't even know if FPC generates correct debug information for them). ---------------------------------------------------------------------- ASerge · 2020-04-10 13:45 Quote from: Bi0T1N on April 09, 2020, 10:45:46 pmAs I said, compiles and works fine with Free Pascal Compiler version 3.3.1-r44644 [2020/04/08] for x86_64. Also no need to add Windows unit. So I guess your compiling 32-bit? Sorry, I missed about 3.3.1. Of course 64 bit Test with fpc 3.3.1 64bit, rev.44675. In Lazarus 2.1.0 rev 62926 and outside it. 1. Run app from console: Code: [Select]start hello from thread 9312 ansi 9312 uni threadid 9312 uni threadhandle 132 hello from thread 9736 setting threadname to 3 ansi 9736 fallback for older Windows uni threadid 9736 uni threadhandle 124 hello from thread 5936 setting threadname to 1 ansi 5936 fallback for older Windows  uni threadid 5936 uni threadhandle 120 hello from thread 2992 setting threadname to 0 ansi 2992 fallback for older Windows uni threadid 2992 uni threadhandle 128 hello from thread 9084 setting threadname to 2 ansi 9084 fallback for older Windows uni threadid 9084 uni threadhandle 136 setting threadname to 4  fallback for older Windows  <-- timeout setting threadname to test fallback for older Windows  <-- timeout setting threadname to bronze fallback for older Windows  <-- timeout done  <-- Press return this Heap dump by heaptrc unit of "%RealPath%\project1.exe" 168 memory blocks allocated : 7335/7808 163 memory blocks freed     : 6935/7408 5 unfreed memory blocks : 400 True heap size : 229376 (160 used in System startup) True free heap : 227776 Should be : 227856 Call trace for block $0000000001277C80 size 80   $000000010000B302   $0000000100008FEA   $000000010002459E   $0000000100002021  main,  line 124 of project1.lpr   $00000001000022A6   $0000000100010A80   $0000000100001770   $00000000776F556D   $000000007795372D Call trace for block $0000000001277B60 size 80   $000000010000B302   $0000000100008FEA   $000000010002459E   $0000000100002021  main,  line 124 of project1.lpr   $00000001000022A6   $0000000100010A80   $0000000100001770   $00000000776F556D   $000000007795372D Call trace for block $0000000001277A40 size 80   $000000010000B302   $0000000100008FEA   $000000010002459E   $0000000100002021  main,  line 124 of project1.lpr   $00000001000022A6   $0000000100010A80   $0000000100001770   $00000000776F556D   $000000007795372D Call trace for block $0000000001277920 size 80   $000000010000B302   $0000000100008FEA   $000000010002459E   $0000000100002021  main,  line 124 of project1.lpr   $00000001000022A6   $0000000100010A80   $0000000100001770   $00000000776F556D   $000000007795372D Call trace for block $0000000001277800 size 80   $000000010000B302   $0000000100008FEA   $000000010002459E   $0000000100002021  main,  line 124 of project1.lpr   $00000001000022A6   $0000000100010A80   $0000000100001770   $00000000776F556D   $000000007795372D 2. Run from IDE. Gdb 7.3.50. Code: [Select]start hello from thread 7288 hello from thread 4716 ansi 7288 ansi 4716 uni threadid 7288 uni threadid 4716 uni threadhandle 128 hello from thread 3620 uni threadhandle 132 setting threadname to 2 ansi 3620 setting threadname to 3 fallback for older Windows uni threadid 3620 fallback for older Windows changing name uni threadhandle 136 changing name setting threadname to 4 fallback for older Windows hello from thread 8720 changing name ansi 8720 hello from thread 8256 ansi 8256 uni threadid 8720 uni threadid 8256 uni threadhandle 124 uni threadhandle 120 setting threadname to 1 setting threadname to 0 fallback for older Windows fallback for older Windows changing name changing nameI wait about 15 seconds, the threads window shows only one thread without a name. After that, the program terminates itself with a similar heap trace. ---------------------------------------------------------------------- 440bx · 2020-04-10 15:09 Quote from: PascalDragon on April 10, 2020, 12:00:10 pmWe are talking about the NameThreadForDebugging method.  ... cause thread variables aren't that easily accessed from the debugger (I don't even know if FPC generates correct debug information for them). Thank you.  I appreciate the clarification. ---------------------------------------------------------------------- Bi0T1N · 2020-04-10 18:09 Quote from: ASerge on April 10, 2020, 03:45:28 pmQuote from: Bi0T1N on April 09, 2020, 10:45:46 pmAs I said, compiles and works fine with Free Pascal Compiler version 3.3.1-r44644 [2020/04/08] for x86_64. Also no need to add Windows unit. So I guess your compiling 32-bit? Sorry, I missed about 3.3.1. Of course 64 bit Test with fpc 3.3.1 64bit, rev.44675. In Lazarus 2.1.0 rev 62926 and outside it. 1. Run app from console: 2. Run from IDE. Gdb 7.3.50. I wait about 15 seconds, the threads window shows only one thread without a name. After that, the program terminates itself with a similar heap trace. 1. It should also work with older FPC versions (read 3.0.4) if you do the typecast PascalDragon mentioned. 2. The console output just gives some extra infos but the threadnames can only be seen in a debugger which supports threadnames by MS_VC_EXCEPTION. I've used x64dbg as debugger to verify the method for older Windows on my Win10 system. 3. You need at least gdb 8.0 or newer as mentioned before. Older versions doesn't support the exception type from point 2) 4. Not sure what Lazarus shows in its thread window but normally it shouldn't support it (at least not through gdb with your version) 5. It's a PoC so I didn't really paid attention if everything is freed correctly. ---------------------------------------------------------------------- ASerge · 2020-04-10 21:17 Quote from: Bi0T1N on April 10, 2020, 08:09:37 pm1. It should also work with older FPC versions (read 3.0.4) if you do the typecast PascalDragon mentioned. 2. The console output just gives some extra infos but the threadnames can only be seen in a debugger which supports threadnames by MS_VC_EXCEPTION. I've used x64dbg as debugger to verify the method for older Windows on my Win10 system. 3. You need at least gdb 8.0 or newer as mentioned before. Older versions doesn't support the exception type from point 2) 4. Not sure what Lazarus shows in its thread window but normally it shouldn't support it (at least not through gdb with your version) 5. It's a PoC so I didn't really paid attention if everything is freed correctly. 1. Oddly enough, I check and you don't. Not compiled in 3.0.4. without Windows. 2. Cast will not help, THandle is 64 bit, but the parameter is DWORD. I checked - the value is really more than 32 bits. 3. I only use the debugger that comes with the latest official version of Lazarus. And of course, in the context of the Lazarus forum, it is unusual to discuss that this is not appropriate for it. 4. The heap trace shows that the release is incorrect. 5. Under the debugger, the program does not work at all. ---------------------------------------------------------------------- PascalDragon · 2020-04-11 12:36 Quote from: ASerge on April 10, 2020, 11:17:51 pm1. Oddly enough, I check and you don't. Not compiled in 3.0.4. without Windows. 2. Cast will not help, THandle is 64 bit, but the parameter is DWORD. I checked - the value is really more than 32 bits. The parameter is a TThreadID, not a THandle. And a thread ID is a DWORD, even on 64-bit Windows (see the result of GetThreadID. That's why it was changed in FPC 3.2. ---------------------------------------------------------------------- ASerge · 2020-04-11 13:02 Quote from: PascalDragon on April 11, 2020, 02:36:50 pmThe parameter is a TThreadID, not a THandle. And a thread ID is a DWORD, even on 64-bit Windows (see the result of GetThreadID. That's why it was changed in FPC 3.2. I was talking about an example. It is explicitly defined in lines 64-69: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  THREADNAME_INFO = record    dwType: DWord; // Must be 0x1000.    szName: PAnsiChar; // Pointer to name (in user addr space).    dwThreadID: DWord; // Thread ID (-1=caller thread).   <--------------- this DWORD    dwFlags: DWord; // Reserved for future use, must be zero.  end; The NameThreadForDebuggingTest does not test AThreadId = -1 (the default value for the 64 bit parameter) for old Windows, which is what causes RunError 201 to be called when the application starts. ---------------------------------------------------------------------- PascalDragon · 2020-04-11 16:57 I just tested the example from here with 3.0.4 on Win64. The only change I did was to disable the loading of SetThreadDescription (because I'm on Windows 10). Otherwise it compiled without error (even the assignment of aThreadID to dwThreadID) and when run in WinDBG I correctly saw the names even for the one that used the default parameter for the main thread. ---------------------------------------------------------------------- Bi0T1N · 2020-04-11 17:14 I've noticed that the threads disappear after the exception is raised so following change seems appropriate Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       RaiseException(MS_VC_EXCEPTION, 0, SizeOf(thrdinfo) div SizeOf(DWord), @thrdinfo);to Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i           try            RaiseException(MS_VC_EXCEPTION, 0, SizeOf(thrdinfo) div SizeOf(DWord), @thrdinfo);          except          end;and the threads remain running. Attached a screenshot of x64dbg which show that it is working fine. Trying to bring it into the compiler now ---------------------------------------------------------------------- ASerge · 2020-04-11 17:49 Quote from: PascalDragon on April 11, 2020, 06:57:04 pmI just tested the example from here with 3.0.4 on Win64. The only change I did was to disable the loading of SetThreadDescription (because I'm on Windows 10). Otherwise it compiled without error (even the assignment of aThreadID to dwThreadID) and when run in WinDBG I correctly saw the names even for the one that used the default parameter for the main thread. I get example from last Bi0T1N message with code. 1. Get code as is Code: [Select]fpc project1.lpr Free Pascal Compiler version 3.0.4 [2019/10/27] for x86_64 Copyright (c) 1993-2017 by Florian Klaempfl and others Target OS: Win64 for x64 Compiling project1.lpr project1.lpr(119,51) Error: Identifier not found "GetProcAddress" project1.lpr(145,4) Fatal: There were 1 errors compiling module, stopping Fatal: Compilation aborted Error: ....\lazarus64\fpc\3.0.4\bin\x86_64-win64\ppcx64.exe returned an err or exitcode 2. Add windows to uses: Code: [Select]d:\Users\Serge\Documents\FPC Projects\Test\DebugName>fpc project1.lpr -g -Cirot Free Pascal Compiler version 3.0.4 [2019/10/27] for x86_64 Copyright (c) 1993-2017 by Florian Klaempfl and others Target OS: Win64 for x64 Compiling project1.lpr Linking project1.exe 144 lines compiled, 0.2 sec, 161328 bytes code, 6596 bytes data 3. An application running in the console does not show a RunError(201) because it overflows silently. But in Lazarus (2.0.6) it is shown under the debugger (gdb). This can be considered debugger behavior, but I agree with this-the variable is explicitly truncated, although this is not critical in a specific case. ---------------------------------------------------------------------- PascalDragon · 2020-04-11 20:03 Quote from: ASerge on April 11, 2020, 07:49:37 pm2. Add windows to uses: Code: [Select]d:\Users\Serge\Documents\FPC Projects\Test\DebugName>fpc project1.lpr -g -Cirot Ah, you enabled range checking... As Bi0T1N wrote his code is a proof of concept as such enabling checks can be unwise. That said changing the assignment to dwThread to from aThreadID to DWord(aThread) solves the range check error (and with 3.2 that won't be necessary anyway).