2013-10-17 179 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