views:

1174

answers:

4

I've accidentally removed Win2K compatibility from an application by using GetProcessID.

I use it like this, to get the main HWND for the launched application.

ShellExecuteEx(&info); // Launch application
HANDLE han = info.hProcess; // Get process

cbinfo.han = han;

//Call EnumWindows to enumerate windows....
//with this as the callback

static BOOL CALLBACK enumproc(HWND hwnd, LPARAM lParam)
{
  DWORD id;
  GetWIndowThreadProcessID(hwnd, &id);
  if (id == GetProcessID(cbinfo.han))
    setResult(hwnd)
 ...
}

Any ideas how the same function could be acheived on Win2K?

+4  A: 

There is an 'sort-of-unsupported' function: ZwQueryInformationProcess(): see

http://msdn.microsoft.com/en-us/library/ms687420.aspx

This will give you the process id (amongst other things), given the handle. This isn't guaranteed to work with future Windows versions, so I'd suggest having a helper function that tests the OS version and then uses GetProcAddress() to call either GetProcessId() for XP and above, and ZwQueryInformationProcess() for Win2K only.

DavidK
A: 

No, it's not ZwQueryInformationProcess() It's NtQIP and of course it works for all versions since NT 3.5 and you don't need to test OS at all

I assume you meant NtQueryInformationProcess() - What's the difference between that and ZwQueryInformationProcess()?
Roddy
there seems to be no difference between ZwQueryInformationProcess() and NtQueryInformationProcess(). On Vista they both have the same address. Both are marked potentially unavailable, so testing OS *is* necessary for future compatibility.
Roddy
In kernel mode they are different.
ChristianWimmer
+4  A: 

DavidK's right. Please see the comment in the ZwQueryInformationProcess documentation:

[ZwQueryInformationProcess may be altered or unavailable in future versions of Windows. Applications should use the alternate functions listed in this topic.]

That means that Microsoft can choose to remove this at any time in the future, thus breaking your application. I strongly consider you follow DavidK's advice and use ZwQueryInformationProcess on OS's that don't support GetProcessID and use GetProcessID on OS's that support it (XP SP1 and above).

Larry Osterman
+1  A: 

Thanks to DavidK and Larry - Here's my final solution. Full error handling is left as an exercise for the reader.

Note that rather than specifically checking the OS version, I attempt to dynamically link to the functions. Static linking would mean that the application would simply fail to load if the procedure wasn't available.

This has been tried successfully on Win2K and Vista:

#include "Winternl.h"

typedef DWORD (WINAPI* pfnGetProcID)(HANDLE h);

typedef NTSTATUS (WINAPI* pfnQueryInformationProcess)(
    HANDLE ProcessHandle,
    PROCESSINFOCLASS ProcessInformationClass,
    PVOID ProcessInformation,
    ULONG ProcessInformationLength,
    PULONG ReturnLength);

DWORD MyGetProcessId(HANDLE h)
{
    static pfnQueryInformationProcess ntQIP = (pfnQueryInformationProcess) GetProcAddress(GetModuleHandle("NTDLL.DLL"),"NtQueryInformationProcess");
    static pfnGetProcID getPId = (pfnGetProcID) GetProcAddress(GetModuleHandle("KERNEL32.DLL"),"GetProcessId");

    if ((ntQIP == NULL) && (getPId == NULL))
     throw Exception("Can't retrieve process ID : GetProcessID not supported");

    if (getPId != NULL)
     return getPId(h);
    else
    {
     PROCESS_BASIC_INFORMATION info;
     ULONG returnSize;
     ntQIP(h, ProcessBasicInformation, &info, sizeof(info), &returnSize);  // Get basic information.
     return info.UniqueProcessId;
    }
}
Roddy