in reply to Re^6: how to change process & thread priority on Win32 ?
in thread how to change process & thread priority on Win32 ?

Many thanks for this excellent explanation !

Thanks, but apparently not so good as you seem to have misunderstood it. Don't feel bad about that, it probably reflects more on my explanation than anything else. I'll try to correct that here.

PID == Process IDentifier. These are the values you see in the Task Manager. They are not the same as TID (Thread identifiers), nor are they either process or thread handles. You cannot use SetThreadPriority() to affect a processes priority.

Each process consists of one or more threads. Each process has a PID. Each thread within a process has a TID . Each PID is (at any given time) unique within the system. Ie. No two running processes will have the same PID. TIDs are unique within their process. That is, each threaded process will have a TID==0 (zero), and any process that has more than one thread will have a TID==1 (unless it has already terminated); and so on.

Update: The above stuck sentence is true only for TIDs returned by Perl's threads module! Which are not the same as Win32 native TIDS, which are unique within the system at any given time.

Process and thread handles are required to use the SetPriorityClass() and SetThreadPriority() APIs respectively. And you must have the correct type for each call, and handles are different from IDs.

IDs identify a particular instance of a process(within the system) or thread (within its process).

Handles are created as required for a particular use. They 'point', (in a loose sense of the term), to a given process or thread, but they also carry with them a bunch of other information, like security privileges, access rights and inheritability flags. A handle of either type can only be used within the process for which it is created, and for the purpose (thread or process) for which it is created. If you obtain a handle within one process, you cannot 'pass' it to another process and use it--except if you use DuplicateHandle() to duplicate an existing handle within one process specifically for another process.

Indeed, having duplicated a handle in one process for use in another, the duplicate is unusable within the process that created it. It must be passed to another process, and only the process for which it was duplicated, before it can be used.

All of which may be more than you wished to know, but it is necessary background information for the answer to your real question. Which, going by the code snippet you've posted above, is nothing at all to do with "threads", despite the title of your original post.

What you appear to be wanting to do, is to change the priority of a process, via it's PID (as listed in the Task Manager), from a separate process, namely a Perl script.

The good news is that, if all you want to do is change the process priority of an existing process, then Win32::Process provides access to the calls required without your needing to use Win32::API. Which means that martos posts do indeed tell you how to do this, albeit that it is completely at odds with the title of your OP.

In a nutshell, to do this you need to a) obtain a handle to the process who's priority you wish to change; b) call SetPriorityClass(). Win32::Process gives you direct access to the calls required to do both. See the snippet below.

However, I think I see from whence your confusion stems. There are two sets of 'priority' related constants exported by Win32::Process. The *PRIORITY_CLASS set and the THREAD_PRIORITY_* set.

But this does not appear to be the case according to my tests. Maybe it was once true and isn't on my version of the OS. There are now two new PRIORITY_CLASS constants that the module does not export (ABOVE/BELOW_NORMAL), as these were not available when the module was written.

Basically, there appears to be no good reason that I can divine for why the THREAD_PRIORITY_* constants are exported by Win32::Process. As far as I can tell, there are no calls within that module capable of making use of them. It would probably be better if they were removed.

Here's a snippet that allows you to set the priority class of an existing process to any of the 4 (6, depending on the age of your version) priority classes:

realtime|high(|above)|normal(|below)|idle.

It could really use the addition of code to look up the process by name and retrieve its PID, but there are several snippets around the site for doing that.

#! perl -slw use strict; use Win32::Process; my %priorityClasses = ( realtime => REALTIME_PRIORITY_CLASS, high => HIGH_PRIORITY_CLASS, above => 0x00008000, ## ABOVE_NORMAL_PRIORITY_C +LASS (Not NT/95/96/ME). normal => NORMAL_PRIORITY_CLASS, below => 0x00004000, ## BELOW_NORMAL_PRIORITY_C +LASS (Not NT/95/96/ME). idle => IDLE_PRIORITY_CLASS, ); my %priority2class = reverse %priorityClasses; my( $pid, $priorityClass ) = @ARGV; $priorityClass = lc $priorityClass; die 'Priority class must be specified as [realtime|above|normal|below| +idle]' unless exists $priorityClasses{ $priorityClass }; my( $OS, $major, $minor, $build, $id ) = Win32::GetOSVersion(); die "Priority class $priorityClass not available on $OS" if $priorityClass eq 'above' or $priorityClass eq 'below' and $major < 5; my( $hProcess, $class ); Win32::Process::Open( $hProcess, $pid, 0 ) or die $^E; $hProcess->GetPriorityClass( $class ); print "Process $pid had priority class: ", $priority2class{ $class }; $hProcess->SetPriorityClass( $priorityClasses{ $priorityClass } ) or d +ie $^E; $hProcess->GetPriorityClass( $class ); print "Process $pid now has priority class: ", $priority2class{ $class + }; __END__ P:\test>SetPriority.pl 1892 high Process 1892 had priority class: normal Process 1892 now has priority class: high P:\test>SetPriority.pl 1892 normal Process 1892 had priority class: high Process 1892 now has priority class: normal

Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

Replies are listed 'Best First'.
Re^8: how to change process & thread priority on Win32 ?
by rbutcher (Beadle) on Dec 10, 2005 at 06:22 UTC
    I need to change both Process Priority and Thread Priority - I've already managed to change Process Priority using SetPriorityClass. So I'm working on changing the Thread Priority. I think my problem up to now has been my vagueness / misunderstandingson terminology - this was not helped by my using Win32::Process::Info to dump all the system process details - it gives Handle and ProcessId with same value, and I assumed Handle meant Thread Handle, and that it was the same as Process Id if the process only had a single thread (i.e. I thought Handle == PID == TID).
    But with your detailed explanation of terminology I am now a little closer to Enlightenment. As I see it now, to change a thread priority :-
    I need to generate a Duplicate Handle from within the running process.
    Use this duplicate handle from within Perl to change the Thread Priority of the original thread.
    I'll go away and use the info I now have to make it work. Many thanks for your patience.
      this was not helped by my using Win32::Process::Info to dump all the system process details - it gives Handle and ProcessId with same value, and I assumed Handle meant Thread Handle, and that it was the same as Process Id if the process only had a single thread (i.e. I thought Handle == PID == TID).

      I'm not sure why the 'handle' key returned from Win32::Process::Info is so labelled. From a quick inspection, it appears to the the same value as the 'ProcessId' in every case. It just appears to be a duplicate.

      As I see it now, to change a thread priority :-
    • I need to generate a Duplicate Handle from within the running process.
    • Use this duplicate handle from within Perl to change the Thread Priority of the original thread.
      1. As far as I am aware, there is no mechanism for obtaining a handle to a thread within another process.

        Update: It is possible to obtain a handle to a thread within another process, namely OpenThread(), but you must have a Win32 native TID (not a threads module TID).

      2. Unless you have the source code for all the processes you are trying to affect, you will not be able to use DuplicateHandle() within those processes(*).

        Even if you do have the sources, you wouldn't be able to generate a duplicate thread handle for your Perl script's use without having a process handle to the Perl process. Catch22.

        And even if you arrange for all the processes you wish to modify to look for your Perl script and generate a duplicate for it, you would still need to communicate those to your Perl script.

      3. Adjusting the priority of another process' threads is a really "Bad Idea".

        Actually, adjusting another process' priority class is not a great idea either, unless it is just a temporary measure and you set it back to it former value fairly soon afterwards.

        Even setting the priority (class) of your own process or threads to non-default values is not recommended unless you have very well thought through reasons for doing so, and those threads or processes are written to do tasks that require it, and are aware of their status.

        Eg. If you have a process that needs to respond very quickly to an event when it occurs, then running the thread waiting (on a semaphore or similar sleep-till-it happens mechanism--not polling!), for that event, then setting it's priority higher than normal can work. But once it becomes unblocked, it should either do what it has to do very quickly and go back to blocking, by handing the work off to a normal priority thread if it will take any time, or by dropping it's priority before doing the work.

        Multi-threaded processes rely upon the relative priority of their threads being well balanced commensurate with the types of work each thread is charged with doing. If you go around adjusting those priorities without understanding the balance required, you will probably render the processes, and possibly your system, defective or non-functional.

      So the questions are: Why do you want to do this? What are you trying to achieve?

      (*)Actually, there is a mechanism that involves injecting a thread into the other process using CreateRemoteThread(). This requires exceptional privileges, and is normally reserved for Debugger programs.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        I need to control the priorities of various longrunning jobs - some of which kick off threads with nonstandard priority settings which cause some to hog the cpu, others to get none - this Perl utility is intended to keep them running at uniform settings.
        I have been given the source of a C program that adjusts thread priority. The relevant code :-
        HANDLE hProcessSnap = NULL; BOOL bRet = FALSE; THREADENTRY32 te32 = {0}; // Take a snapshot of all processes in the system. hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) return (FALSE); // Fill in the size of the structure before using it. te32.dwSize = sizeof(THREADENTRY32); // Get first process information Thread32First(hProcessSnap, &te32) // get the thread priority HANDLE hThread; hThread = OpenThread(THREAD_SET_INFORMATION|THREAD_QUERY_INFORMATI +ON, FALSE, te32.th32ThreadID); // set new priority class if (SetThreadPriority(hThread, threadPriority) != TRUE) { cerr << "SetThreadPriority() "; printError(); } CloseHandle(hThread);
        I can't see why the last parameter to CreateToolhelp32Snapshot is 0, as that means current process Id, in this case Perl - I would have thought this should be PID of the process whose thread I want to change. ??
        I converted the above to Perl calls to Win32::API as follows :-
        $pid = 952; # 952 is a running job pid use threads; use threads::shared; use Win32::API; Win32::API->Import( 'Kernel32', q[ DWORD GetLastError( ) ] ); Win32::API->Import( 'Kernel32', q[ HANDLE CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32Pr +ocessID ) ] ); $hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, $pid); + print "hProcessSnap = $hProcessSnap \n";
        580 output, presumably an open handle to the snapshot
        Win32::API->Import( 'Kernel32', q[ BOOL Thread32First( HANDLE hSnapshot, THREADENTRY32 lpte)] ); $retcode = GetLastError(); print "import threadfirst error was $retcode \n";
        WARNING Unknown parameter type THREADENTRY32 ??
        0 output, import OK ??
        The following is my attempt to define the C THREADENTRY32 structure - not to sure about this.
        # typedef for THREADENTRY32 typedef Win32::API::Struct AA => qw{ DWORD dwSize; DWORD cntUsage; DWORD th32ThreadID; DWORD th32OwnerProcessID; LONG tpBasePri; LONG tpDeltaPri; DWORD dwFlags; }; my $Point = Win32::API::Struct->new( AA ); $Point->{dwSize} = 24; ##$size=Win32::API::Struct->sizeof(AA); ##always returns 0. ?? Thread32First($hProcessSnap, LPAA); $retcode = GetLastError(); print "threadfirst error was $retcode \n";
        error code 24 is output, indicating incorrect command length.
        Acording to the Win32::API::Struct Pod, LPAA is a pointer to AA generated automatically by Win32::API::Struct, but I don't think it's getting a correct address. Any Perl way to get the address of AA to pass to Thread32First ?
        The rest of the Perl code will be :-
        Win32::API->Import( 'Kernel32', q[HANDLE OpenThread( DWORD dwDesiredAc +cess, BOOL bInheritHandle, DWORD dwThreadId) ] ); $hThread = OpenThread(THREAD_SET_INFORMATION|THREAD_QUERY_INFORMATION, FALSE, $point->{th32ThreadID}); Win32::API->Import( 'Kernel32', q[ BOOL SetThreadPriority( HANDLE hThr +ead, int nPriority ) ] ); SetThreadPriority( hThread, $threadpriority );
        But I'm stuck at Thread32First