2013-10-17 163 views
3

當我啓動我的應用程序時,我試圖弄清楚是否有另一個應用程序進程。我也試圖弄清楚它是否在不同的用戶會話中運行。在C++中獲取另一個進程的會話ID

到目前爲止好,這就是它看起來像在C#:

private static bool isThereAnotherInstance() { 
     string name = Path.GetFileNameWithoutExtension(Application.ExecutablePath); 
     Process[] pAll = Process.GetProcessesByName(name); 
     Process pCurrent = Process.GetCurrentProcess(); 
     foreach (Process p in pAll) { 
      if (p.Id == pCurrent.Id) continue; 
      if (p.SessionId != pCurrent.SessionId) continue; 
      return true; 
     } 
     return false; 
    } 

但要求發生了變化,我需要這段代碼在C++中使用純WinAPI的。

到現在爲止,我能找到,通過使用CreateToolhelp32SnapshotOpenProcess具有相同的可執行文件路徑等處理

缺少的部分是如何得到一個進程的會話ID(電流和其他進程,AND當前和其他會話)
如何做到這一點?

回答

1

正如ARX提到,ProcessIdToSessionId應該做的工作。
但不幸的是,在我的情況告訴我ACCESS_DENIED對我有興趣的過程。
它的工作對當前進程。

因此,這裏是我的解決方案,使用NtQuerySystemInformation
.NETs Process類在內部使用相同的函數。

typedef struct _SYSTEM_PROCESS_INFORMATION_BUG { 
    //... 
} 

typedef NTSTATUS (WINAPI *PNtQuerySystemInformation) (
    IN SYSTEM_INFORMATION_CLASS SystemInformationClass, 
    OUT PVOID SystemInformation, 
    IN ULONG SystemInformationLength, 
    OUT PULONG ReturnLength OPTIONAL 
    ); 

#ifndef NT_ERROR 
#define NT_ERROR(Status) ((ULONG)(Status) >> 30 == 3) 
#endif 

#define PROCESSINFO_BUFFERSIZE (256*1024) 

DLL_EXPORT int GetProcessIdFromPath2(char *exePath, int flags) { 
    char exe[MAX_PATH], *exeName, file[MAX_PATH], *fileName; 
    DWORD pidCurrent, sessionIdCurrent; 
    int ret=-1; 

    strcpy(exe, exePath); 
    strupr(exe); 
    exeName=getFileName(exe); 

    pidCurrent = GetCurrentProcessId(); 
    if (!ProcessIdToSessionId(pidCurrent, &sessionIdCurrent)) sessionIdCurrent=0; 
    HMODULE hNT = LoadLibrary("Ntdll.dll"); 
    if (hNT) { 
     PNtQuerySystemInformation pNtQuerySystemInformation = (PNtQuerySystemInformation)GetProcAddress(hNT, "NtQuerySystemInformation"); 
     if (pNtQuerySystemInformation) { 
      SYSTEM_PROCESS_INFORMATION_BUG* processInfo; 
      char *buffer = (char*)malloc(PROCESSINFO_BUFFERSIZE); 
      if (!buffer) { 
       ret=-3; 
      } 
      else { 
       char *current=buffer; 
       DWORD len; 
       int count=0; 
       NTSTATUS s = pNtQuerySystemInformation(SystemProcessInformation, buffer, PROCESSINFO_BUFFERSIZE, &len); 
       if (NT_ERROR(s)) { 
        ret=-2; 
       } 
       else { 
        ret=0; 
        while(1) { 
         processInfo = (SYSTEM_PROCESS_INFORMATION_BUG*)current; 
         if (processInfo->ImageName.Buffer!=NULL){ 
          wcstombs(file, processInfo->ImageName.Buffer, MAX_PATH-1); 
          strupr(file); 
          fileName=getFileName(file); 
          if (strcmp(fileName, exeName)==0) { 
           if (processInfo->UniqueProcessId!=pidCurrent) { 
            if (processInfo->SessionId==sessionIdCurrent) { 
             ret = processInfo->UniqueProcessId; 
            } 
           } 
          } 
         } 
         if (processInfo->NextEntryOffset==0) break; 
         current+=processInfo->NextEntryOffset; 
         count++; 
        } 
       } 
       free(buffer); 
       buffer=NULL; 
      } 
     } 
     FreeLibrary(hNT); 
    } 
    return ret; 
} 
+0

我也用這種方法更新了我的答案... – arx

+0

@arx:仍然提高了您的答案。關於'SYSTEM_PROCESS_INFORMATION'結構的提示非常有用! – joe

3

ProcessIdToSessionId函數映射進程ID的會話ID。

您注意到這似乎需要.Net不需要的過多權限。

淨確實得到一些從HKEY_PERFORMANCE_DATA註冊表的過程數據,但這並不包括會話ID。會話ID是使用NtQuerySystemInformation獲取以返回SYSTEM_PROCESS_INFORMATION結構的數組。這種結構沒有很好的記錄,但會話ID緊跟在句柄計數之後(即,它是當前聲明爲BYTE Reserved4[4];的字段)。微軟並不保證在未來的Windows版本中這種情況會繼續存在。

+0

的SPI結構的文檔,對於不同的會話的過程中,我得到只是0,但應該是2.什麼是錯的? – joe

+0

「如果函數失敗,則返回值爲零。要獲得擴展錯誤信息,請調用GetLastError。」 – arx

+0

@joe我想回答你的問題,並使用EnumProcesses和ProcessIdToSessionID創建一個小程序,將其作爲Admnistrator運行(並讓另一個用戶登錄)。它悲慘地失敗了。有些PID(大多數)不能通過ACCESS_DENIED(例如:calc.exe,!?!)映射到SessionId,對於其他人(很少)我有「2」(例如:winlogon.exe ...)。 – manuell

1

代碼列出所有PID,SID,EXE( 「ALA」 任務管理器之類的) 對我的作品(Windows 7的64B)VS2012快遞

#include <stdio.h> 
#include <tchar.h> 

#include <Windows.h> 
#include <Winternl.h> 

#pragma comment(lib, "ntdll.lib") 

typedef LONG KPRIORITY; // Thread priority 
typedef struct _SYSTEM_PROCESS_INFORMATION_DETAILD { 
    ULONG NextEntryOffset; 
    ULONG NumberOfThreads; 
    LARGE_INTEGER SpareLi1; 
    LARGE_INTEGER SpareLi2; 
    LARGE_INTEGER SpareLi3; 
    LARGE_INTEGER CreateTime; 
    LARGE_INTEGER UserTime; 
    LARGE_INTEGER KernelTime; 
    UNICODE_STRING ImageName; 
    KPRIORITY BasePriority; 
    HANDLE UniqueProcessId; 
    ULONG InheritedFromUniqueProcessId; 
    ULONG HandleCount; 
    BYTE Reserved4[4]; 
    PVOID Reserved5[11]; 
    SIZE_T PeakPagefileUsage; 
    SIZE_T PrivatePageCount; 
    LARGE_INTEGER Reserved6[6]; 
} SYSTEM_PROCESS_INFORMATION_DETAILD, *PSYSTEM_PROCESS_INFORMATION_DETAILD; 

int _tmain(int argc, _TCHAR* argv[]) { 

    SYSTEM_PROCESS_INFORMATION aSPI[ 1024 ]; 
    // could ask for actual needed size size and malloc (with few extra new processes bonus...) 
    NTSTATUS nts = NtQuerySystemInformation(SystemProcessInformation, aSPI, sizeof(aSPI), NULL); 
    if (NT_ERROR(nts)) return -1; 

    char * pSPI = reinterpret_cast<char*>(&aSPI[ 0 ]); 
    while (true) { 
     SYSTEM_PROCESS_INFORMATION_DETAILD * pOneSPI = reinterpret_cast<SYSTEM_PROCESS_INFORMATION_DETAILD*>(pSPI); 
     WCHAR * pwch = pOneSPI->ImageName.Buffer; 
     if (pwch == 0 || pOneSPI->ImageName.Length == 0) pwch = TEXT("Unknown"); 
     _tprintf(TEXT("PID %d - SID %d EXE %s\n"), pOneSPI->UniqueProcessId, *reinterpret_cast<LONG*>(&pOneSPI->Reserved4), pwch); 
     if (pOneSPI->NextEntryOffset) pSPI += pOneSPI->NextEntryOffset; 
     else break; 
    } 

    return 0; 
} 

非常感謝@Oleg爲在SO here