you can do next:
1) get csrss.exe process id in your session
2) enumerate windows via EnumWindows, for every window query it process id (GetWindowThreadProcessId), compare it with csrss.exe process id. if equal - get window class name (GetClassName ) and if it is L"#32770" - post WM_CLOSE message to this window
3) if you want be more precise - after you found L"#32770" window class, query the window caption text via GetWindowText and ensure that it begin with "module.exe - " or how your exe is named ?
struct FIND_WND_CONTEXT
{
PCWSTR szCaptionBegin;
ULONG lenCaptionBegin;
ULONG dwProcessId;
};
BOOL CALLBACK EnumWndProc(HWND hwnd, FIND_WND_CONTEXT& fwc)
{
ULONG dwProcessId;
if (GetWindowThreadProcessId(hwnd, &dwProcessId) && dwProcessId == fwc.dwProcessId)
{
PWSTR Name = (PWSTR)alloca( max(fwc.lenCaptionBegin * sizeof(WCHAR), sizeof(L"#32770") + sizeof(WCHAR)) );
if (GetClassNameW(hwnd, Name, sizeof(L"#32770") + sizeof(WCHAR)) && !wcscmp(Name, L"#32770"))
{
if (GetWindowText(hwnd, Name, fwc.lenCaptionBegin))
{
_wcslwr(Name);
if (!wcscmp(Name, fwc.szCaptionBegin))
{
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
}
}
}
return TRUE;
}
void CloseTest()
{
const WCHAR module_exe[] = L"module.exe - ";
FIND_WND_CONTEXT fwc = { module_exe, RTL_NUMBER_OF(module_exe) };
if (fwc.dwProcessId = GetMySessionCsrssId())
{
EnumWindows((WNDENUMPROC)EnumWndProc, (LPARAM)&fwc);
}
}
ULONG GetMySessionCsrssId()
{
ULONG cb = 0, rcb = 0x10000;
static volatile UCHAR guz;
PVOID stack = alloca(guz);
union {
PVOID buf;
PBYTE pb;
PSYSTEM_PROCESS_INFORMATION pspi;
};
ULONG SessionId;
ProcessIdToSessionId(GetCurrentProcessId(), &SessionId);
NTSTATUS status;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (0 <= (status = ZwQuerySystemInformation(SystemProcessInformation, buf, cb, &rcb)))
{
ULONG NextEntryOffset = 0;
do
{
pb += NextEntryOffset;
STATIC_UNICODE_STRING(csrss, "csrss.exe");
if (pspi->SessionId == SessionId && RtlEqualUnicodeString(&pspi->ImageName, &csrss, TRUE))
{
return PtrToUlong(pspi->UniqueProcessId);
}
} while (NextEntryOffset = pspi->NextEntryOffset);
return 0;
}
} while (status == STATUS_INFO_LENGTH_MISMATCH);
return 0;
}
somebody of course ask - why use "undocumented", ("no longer available" (this is direct lie in msdn - available from win200 to latest win10 1703), "unsupported", etc) ZwQuerySystemInformation with SystemProcessInformation instead CreateToolhelp32Snapshot + Process32First + Process32Next ? because we need got SessionId information for process. the SYSTEM_PROCESS_INFORMATION containing ULONG SessionId member, while psapi shell over this function by unknown reason drop this member - PROCESSENTRY32 - no here SessionId - it dropped. of course we can retrieve SessionId by addition call to ProcessIdToSessionId, but this function internally open process by Id, for query it SessionId. but for open csrss.exe you need SeDebugPriviledge enabled in your toke (+ 3 extra call to kernel - open process, query information, close handle)