Killing "System Error" dialog box under csrss.exe - windows

I'm running an automated test of an application that has insufficient dependencies (missing dll), and got this error message:
When the test runner kills the application, the above message box isn't disappearing. Apparently it's owned by csrss.exe, which can't be killed.
My problem is that the only way I can get this message box to close is by manually logging in to to the machine and clicking the X on that message window. Is there another way?
Note that I can easily fix the dependency error, but I would like a robust way of terminating the application under test with all its messages when such errors are encountered.

Try avoiding the dialog in the first place: Call SetErrorMode(SEM_FAILCRITICALERRORS) in your test runner, this will cause calls to CreateProcess to fail silently if the loader cannot resolve every import.
If you want to create a query function so you can tell if the process can load, add the CREATE_SUSPENDED flag when calling CreateProcess and call TerminateProcess if it succeeded.

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)

Related

How to write an application to control a driver which is pnp and in kmdf?

so I will detail so that we can easily understand
I have to make a driver for a pcie card, I already have the driver that I wrote in kmdf, now I am using this driver, unfortunately I find myself stuck, I have to write an application (which for example would call the METHOD_IN_DIRECT function that I defined in a switch case in my IoDeviceControl)
I therefore tried to start from an example on github and modified it so that it works ... but obviously as this example is for a NONpnp driver it is not usable for my driver which is pnp.
So I looked for examples of applications that worked with a pnp driver to see the model / shape, but I can't find a tutorial / sites / example on the realization of this famous application, one of the only sites that spoke about it was saying:
"Set an interface guide so the application can find the device and talk to it."
now my question is:
"how to write an aplication to control a PNP driver"
the main in "test.c":
int __cdecl
main(
_In_ ULONG argc,
_In_reads_(argc) PCHAR argv[]
)
{
HANDLE hDevice;
DWORD errNum = 0;
CHAR driverLocation[MAX_PATH];
BOOL ok;
LONG error;
// ULONG bytesReturned;
printf("main start. \n");
//
//open the device
printf("createFile. \n");
hDevice = CreateFileA(DRIVER_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hDevice == INVALID_HANDLE_VALUE){...}
printf("press enter \n");
int c = getchar();
printf("reception d'un charactere . \n");
if (c) {
printf("ioctl go \n");
DoIoctls(hDevice);
printf("ioctl end \n");
//
// Close the handle to the device before unloading the driver.
//
CloseHandle(hDevice);
//
// Unload the driver. Ignore any errors.
//
ManageDriver(DRIVER_NAME, driverLocation, DRIVER_FUNC_REMOVE);
}
c = getchar();
return;
}
here is the main of "test.c" which is at the base for nonpnp but that I modified that said I do not know how to embed the use of the GUID in my application (I imagine that it is because of that that it does not work).
the function DoIoctl :
VOID
DoIoctls(
HANDLE hDevice
)
{
char OutputBuffer[100];
char InputBuffer[200];
BOOL bRc;
ULONG bytesReturned;
//
// Printing Input & Output buffer pointers and size
//
printf("\nInputBuffer Pointer = %p, BufLength = %Id\n", InputBuffer,sizeof(InputBuffer));
printf("OutputBuffer Pointer = %p BufLength = %Id\n", OutputBuffer,sizeof(OutputBuffer));
//
// Performing METHOD_IN_DIRECT
//
printf("\nCalling DeviceIoControl METHOD_IN_DIRECT\n");
if (FAILED(StringCchCopy(InputBuffer, sizeof(InputBuffer),"this String is from User Application; using METHOD_IN_DIRECT")))
{
return;
}
if (FAILED(StringCchCopy(OutputBuffer, sizeof(OutputBuffer),"This String is from User Application in OutBuffer; using METHOD_IN_DIRECT")))
{
return;
}
bRc = DeviceIoControl(hDevice,
(DWORD)Spw_PCIe_IOCTL_IN_BUFFERED,
InputBuffer,
(DWORD)strlen(InputBuffer) + 1,
OutputBuffer,
sizeof(OutputBuffer),
&bytesReturned,
NULL
);
if (!bRc)
{
printf("Error in DeviceIoControl : %d \n", GetLastError());
return;
}
printf(" Number of bytes transfered from OutBuffer: %d\n",bytesReturned);
//
// Performing METHOD_OUT_DIRECT
//
printf("\nCalling DeviceIoControl METHOD_OUT_DIRECT\n");
if (FAILED(StringCchCopy(InputBuffer, sizeof(InputBuffer), "this String is from User Application; using METHOD_OUT_DIRECT"))) {
return;
}
memset(OutputBuffer, 0, sizeof(OutputBuffer));
bRc = DeviceIoControl(hDevice,
(DWORD)Spw_PCIe_IOCTL_OUT_BUFFERED,
InputBuffer,
(DWORD)strlen(InputBuffer) + 1,
OutputBuffer,
sizeof(OutputBuffer),
&bytesReturned,
NULL
);
if (!bRc)
{
printf("Error in DeviceIoControl : : %d", GetLastError());
return;
}
printf(" OutBuffer (%d): %s\n", bytesReturned, OutputBuffer);
return;
}
function ManageDriver :
BOOLEAN
ManageDriver( // <- ManageDriver
IN LPCTSTR DriverName,
IN LPCTSTR ServiceName,
IN USHORT Function
)
{
SC_HANDLE schSCManager;
BOOLEAN rCode = TRUE;
schSCManager = OpenSCManager(NULL, // local machine
NULL, // local database
SC_MANAGER_ALL_ACCESS // access required
)
// Do the requested function.
switch (Function) {;
case DRIVER_FUNC_REMOVE: // REMOVE
printf("remove case. \n");
// Stop the driver.
StopDriver(schSCManager,DriverName);
// Remove the driver service.
RemoveDriver(schSCManager,DriverName);
// Ignore all errors.
rCode = TRUE;
break;
default:
printf("Unknown ManageDriver() function. \n");
rCode = FALSE;
break;
}
// Close handle to service control manager.
if (schSCManager) {
CloseServiceHandle(schSCManager);
}
return rCode;
} // ManageDriver fin
function remove :
BOOLEAN
RemoveDriver( // <- RemoveDriver
_In_ SC_HANDLE SchSCManager,
_In_ LPCTSTR DriverName
)
{
SC_HANDLE schService;
BOOLEAN rCode;
// Open the handle to the existing service.
schService = OpenService(SchSCManager,DriverName,SERVICE_ALL_ACCESS);
// Mark the service for deletion from the service control manager database.
DeleteService(schService)
if (schService) {
CloseServiceHandle(schService);
}
return rCode;
} // RemoveDriver fin
function StartDriver :
BOOLEAN
StartDriver(
_In_ SC_HANDLE SchSCManager,
_In_ LPCTSTR DriverName
)
{
SC_HANDLE schService;
DWORD err;
// Open the handle to the existing service.
schService = OpenService(SchSCManager, DriverName,SERVICE_ALL_ACCESS );
// Start the execution of the service (i.e. start the driver).
StartService(schService, // service identifier
0, // number of arguments
NULL // pointer to arguments
)
// Close the service object.
if (schService) {
CloseServiceHandle(schService);
}
return TRUE;
} // StartDriver fin
function StopDriver :
BOOLEAN
StopDriver(
_In_ SC_HANDLE SchSCManager,
_In_ LPCTSTR DriverName
)
{
BOOLEAN rCode = TRUE;
SC_HANDLE schService;
SERVICE_STATUS serviceStatus;
//
// Open the handle to the existing service.
//
schService = OpenService(SchSCManager,
DriverName,
SERVICE_ALL_ACCESS
);
//
// Request that the service stop.
//
ControlService(schService,
SERVICE_CONTROL_STOP,
&serviceStatus
)
//
// Close the service object.
//
if (schService) {
CloseServiceHandle(schService);
}
return rCode;
} // StopDriver fin
I deleted everything that is debugger otherwise there is sure that it would not be clear
if you had any indication maybe I'm wrong about the nature of applications maybe the solution is very dumb but if you know anything about writing application for pnp driver I'm a taker
to shorten it :
i would need an application skeleton, but not just any, i need one that works for a pnp driver.
(it doesn't matter which driver as long as it's a pnp)
this is to be able to compare with my application and see what is missing from my aplication to support plug and play
cordially thank you all
You need to obtain the device path using the SetupDi functions as shown in this answer.

Create a mutex that is only visible within the same session

I have a windows app that (for company policy reasons) I would like to be limited to a single user at a time, but allowing multiple instances to run simultaneously within a single session.
I am using a mutex to do this much like as already asked and answered in
How to Avoid Multiple Instances of Different Users but Allow Multiple Instances on Single User Session
VB6: Single-instance application across all user sessions
Making a singleton application across all users
one instance of app per Computer, how?
The first one on the list is promising, but the proposed solution is not clear if it is just for "the same user with the same session id" as asked, or only for "the same user in any session" as is implied in the second answer.
My question is, will the first approach really work to restrict access rights to the mutex to the same user in the same session, or is it just to the same user?
More precisely, does the default security descriptor contain any restriction information for the session id? I suspect that is doesn't, meaning that the same user in a different session would have the same default security descriptor and would still be able to get access rights the mutex.
Am I correct about this?
If so, how would I clone the default security descriptor and add a restriction on being in the same session as the creator?
if you like to be limited to a single user at a time, most logical solution - create some named object in common global namespace (so visible for all) and allow only concrete user Sid access this object. as result all instance which run by the same user can open this object, when another users, will be have another Sid and can fail open it, if it already exist. we not mandatory need mutex. the best use named event - this is most simply and small object. also need use not CreateEvent api but CreateEventExW because it let specify dwDesiredAccess - this is important in case some code will be run as low/untrusted integrity. he fail open existing object with all access (if we not add untrusted label to object), but can open object with generic read/generic execute access. we can say request only SYNCHRONIZE access for event. the poc code:
ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
volatile UCHAR guz = 0;
ULONG CanRun(BOOL& bCan)
{
bCan = false;
HANDLE hToken;
ULONG dwError = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken));
if (dwError == NOERROR)
{
ULONG cb = 0, rcb = sizeof(TOKEN_USER) + GetSidLengthRequired(5);
PVOID stack = alloca(guz);
union {
PVOID buf;
PTOKEN_USER User;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
dwError = BOOL_TO_ERROR(GetTokenInformation(hToken, TokenUser, buf, cb, &rcb));
} while (dwError == ERROR_INSUFFICIENT_BUFFER);
CloseHandle(hToken);
if (dwError == NOERROR)
{
PSID UserSid = User->User.Sid;
SECURITY_DESCRIPTOR sd;
SECURITY_ATTRIBUTES sa = { sizeof(sa), &sd, FALSE };
PACL Dacl = (PACL)alloca(cb = sizeof(ACL) + FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + GetLengthSid(UserSid));
if (
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) &&
InitializeAcl(Dacl, cb, ACL_REVISION) &&
AddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, UserSid) &&
SetSecurityDescriptorDacl(&sd, TRUE, Dacl, FALSE) &&
CreateEventExW(&sa, L"Global\\<Your Unique Name>", CREATE_EVENT_MANUAL_RESET, SYNCHRONIZE)
)
{
bCan = true;
}
else
{
switch (dwError = GetLastError())
{
case ERROR_ACCESS_DENIED:
dwError = NOERROR;
break;
}
}
}
}
return dwError;
}
so we query TOKEN_USER from current process token and create (or open) named event "Global\\<Your Unique Name>". if even yet not exist - we create it and assign SD which allow only to the same user sid open it. any other users( even localsystem) fail open this event (without change it dacl first) with ERROR_ACCESS_DENIED.
demo usage:
BOOL bCan;
ULONG dwError = CanRun(bCan);
if (dwError == NOERROR)
{
MessageBoxW(0, 0, bCan ? L"Yes" : L"No", bCan ? MB_ICONINFORMATION : MB_ICONWARNING);
}
else
{
PWSTR sz;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, 0, dwError, 0, (PWSTR)&sz, 0, 0))
{
MessageBoxW(0, sz, 0, MB_ICONHAND);
LocalFree(sz);
}
}

ZwQuerySystemInformation is Not working Properly

I tried FindProcessidByName with Kernel Mode Driver, sometimes ImageName.Buffer goes to NULL , because of this , when ImageName.Buffer goes to NULL I can not find process ids. Do you have any idea why ImageName.Buffer goes to NULL sometimes, sir ?
typedef struct _SYSTEM_PROCESS_INFO_L
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER Reserved[3];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
ULONG BasePriority;
HANDLE ProcessId;
HANDLE InheritedFromProcessId;
}_SYSTEM_PROCESS_INFO_L, *P_SYSTEM_PROCESS_INFO_L;
HANDLE LSFindProcess(LPSTR name)
{
NTSTATUS durum;
ULONG retsize;
HANDLE hProcid = -1;
P_SYSTEM_PROCESS_INFO_L pi;
durum = ZwQuerySystemInformation(SystemProcessInformation, NULL, NULL, &retsize); // request how much memory size we need.
if (!NT_SUCCESS(durum) && durum !=STATUS_INFO_LENGTH_MISMATCH)
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "LS : ZwQuerySystemInformation Failed 1 durum : %p \n",durum);
return -1;
}
PVOID memPtr;
memPtr = ExAllocatePool(PagedPool, retsize);
if (!memPtr)
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "LS : ExAllocatePool Failed \n");
return -1;
}
memset(memPtr, 0, retsize);// zero mem
durum = ZwQuerySystemInformation(SystemProcessInformation, memPtr, retsize, NULL);
pi = (P_SYSTEM_PROCESS_INFO_L)memPtr; // parselliyorz
if (!NT_SUCCESS(durum))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "LS : ZwQuerySystemInformation Failed 2 durum : %p \n", durum);
return -1;
}
while (pi->NextEntryOffset)
{
if (pi->ImageName.Buffer) //some process null if I dont use this I am getting BSOD.
{
if (!_stricmp(pi->ImageName.Buffer, name))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "LS : name %ws , pid : %d \n", pi->ImageName.Buffer, pi->ProcessId);
hProcid = pi->ProcessId; // pid
break; // foundedd
}
}
pi = (P_SYSTEM_PROCESS_INFO_L)((unsigned char*)pi + pi->NextEntryOffset);
}
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "LS : LSFindProcess bitti \n");
return hProcid;
}
ZwQuerySystemInformation is Not working Properly
this is of course false. api working properly. if you got wrong results, this is only due errors in code. in concrete code spinet exist several errors.
in kernel mode exist difference between Zw and Nt api - need understand which version need call, if both (like in this concrete case) is exported. if you know that previous mode is kernel mode - need use Nt version. if you don't know previous mode in this point(at compile time) or previous mode user mode - need use Zw version. however this is general note and will be not error always call Zw
the first serious error - this is how ZwQuerySystemInformation called - first time for query requested buffer size retsize (in code) and second time already with this buffer size. but between this calls new threads and processes can be spawn in system. as result returned buffer size can be already not large enough. correct solution - call this api in loop, while it return STATUS_INFO_LENGTH_MISMATCH
the second - memory always must be free. especially in kernel mode. say that code incomplete - no excuse. code can be incomplete and intermediate, but free memory after allocation always must be immediately inserted
else one critical error - while (pi->NextEntryOffset) loop - with this loop we always lost (not process) the last entry (last created process). need change this.
if (pi->ImageName.Buffer) //some process null if I dont use this I am
getting BSOD.
the ImageName is UNICODE_STRING and need work with it respectively. in case ImageName.Buffer the ImageName.Length is also 0. the UNICODE_STRING ImageName; is correct. incorrect only how you use it.
!_stricmp(pi->ImageName.Buffer, name); // ??
the pi->ImageName.Buffer is PWSTR so it can not be used with _stricmp as a matter of principle. think you use c compiler - c++ simply give you error here. but even use _wcsicmp is incorrect here - again pi->ImageName is UNICODE_STRING and need use RtlEqualUnicodeString(&pi->ImageName, name, TRUE) where name must be of course PCUNICODE_STRING but not PCSTR or PSTR or even PCWSTR. if you have PCWSTR name as input - you need wrap it to UNICODE_STRING before call this api. example of code:
NTSTATUS LSFindProcess(PCUNICODE_STRING ImageName, PHANDLE UniqueProcessId)
{
ULONG cb = 0x20000;
PVOID buf;
NTSTATUS status;
do
{
status = STATUS_INSUFFICIENT_RESOURCES;
if (buf = ExAllocatePool(PagedPool, cb))
{
if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
{
union {
PVOID pv;
PBYTE pb;
PSYSTEM_PROCESS_INFORMATION pspi;
};
pv = buf;
ULONG NextEntryOffset;
goto __0;
do
{
pb += NextEntryOffset;
__0:
if (RtlEqualUnicodeString(&pspi->ImageName, ImageName, TRUE))
{
*UniqueProcessId = pspi->UniqueProcessId;
break;
}
} while (NextEntryOffset = pspi->NextEntryOffset);
}
ExFreePool(buf);
}
} while (status == STATUS_INFO_LENGTH_MISMATCH);
return status;
}
NTSTATUS LSFindProcess(PCWSTR ImageName, PHANDLE UniqueProcessId)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, ImageName);
return LSFindProcess(&us, UniqueProcessId);
}
thanks for everyone especially #RbMm thanks for informations.
Finished code I hope this post help someone..
/// <summary>
/// Struct SystemProcessInformation
/// </summary>
typedef struct _SYSTEM_PROCESS_INFO_L
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER Reserved[3];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
ULONG BasePriority;
HANDLE ProcessId;
HANDLE InheritedFromProcessId;
}_SYSTEM_PROCESS_INFO_L, *P_SYSTEM_PROCESS_INFO_L;
/// <summary>
/// Find Process ID By Name , thanks #RbMm
/// </summary>
/// <param name="imagename">Process name </param>
/// <param name="pid">Output Process id</param>
/// <returns>NTSTATUS.</returns>
NTSTATUS LSFindProcessIdByName(IN PCWSTR imagename, OUT PHANDLE pid)
{
NTSTATUS durum = STATUS_UNSUCCESSFUL;
ULONG qmemsize = 0x1024;
PVOID qmemptr = 0;
P_SYSTEM_PROCESS_INFO_L spi;
UNICODE_STRING uimagename;
RtlInitUnicodeString(&uimagename, imagename); // #RbMm
*pid = -1;
do
{
qmemptr = ExAllocatePool(PagedPool, qmemsize); // alloc memory for spi
if (qmemptr == NULL) // check memory is allocated or not.
{
return STATUS_UNSUCCESSFUL;
}
durum = ZwQuerySystemInformation(SystemProcessInformation,qmemptr, qmemsize, NULL);
if (durum == STATUS_INFO_LENGTH_MISMATCH)
{
qmemsize = qmemsize * 2; // increase qmemsize for next memory alloc
ExFreePool(qmemptr); // free memory
}
} while (durum == STATUS_INFO_LENGTH_MISMATCH); // resize memory
spi = (P_SYSTEM_PROCESS_INFO_L)qmemptr;
while(1)
{
if (RtlEqualUnicodeString(&uimagename, &spi->ImageName, TRUE)) // #RbMm
{
*pid = spi->ProcessId;
break;
}
if (spi->NextEntryOffset == 0)
break;
spi = (P_SYSTEM_PROCESS_INFO_L)((unsigned char*)spi + spi->NextEntryOffset); // next info
}
if (!NT_SUCCESS(durum))
{
ExFreePool(qmemptr); // free memory
return STATUS_UNSUCCESSFUL;
}
ExFreePool(qmemptr); // free memory
return STATUS_SUCCESS;
}
You should not be calling ZwQuerySystemInformation, it's not easily portable across Windows OS's and may not even be available on some of them. The MSDN documentation for this function recommends alternate functions for your particular usage.

How can I get an HMONITOR handle from a display device name?

I want to obtain a monitor handle (HMONITOR) that can be used with the Windows multi-monitor APIs for a specific monitor attached to the system by index. For example, say I have three monitors attached to my system and forming part of my desktop; I want to get a handle to monitor 3.
I already know how to get the device name for a specific monitor by index by calling the EnumDisplayDevices function. For example:
HMONITOR MonitorFromIndex(int index /* (zero-indexed) */)
{
DISPLAY_DEVICE dd;
dd.cb = sizeof(dd);
if (EnumDisplayDevices(NULL, index, &dd, 0) != FALSE)
{
// We found a match; make sure that it's part of the desktop.
if ((dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
{
// Yup. Now we've got the name of the device:
std::cout << dd.DeviceName << std::endl;
// But how do I obtain an HMONITOR for this device?
// ...
}
}
return NULL; // indicate failure
}
In the code above, we've found the name of the desired device (dd.DeviceName). I can use this name to create a DC for that monitor by calling CreateDC:
HDC hDC = CreateDC(dd.DeviceName, dd.DeviceName, NULL, NULL);
And I can obtain information about that monitor by calling EnumDisplaySettings:
DEVMODE dm;
dm.dmSize = sizeof(dm);
dm.dmDriverExtra = 0;
if (EnumDisplaySettings(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm) != FALSE)
{
std::cout << "The monitor supports " << dm.dmBitsPerPel << " bits per pixel." << std::endl;
}
Which is all great, but I want a handle to that monitor. How can I get it?
I tried to call EnumDisplayMonitors, passing a handle to the device context that I created using CreateDC, hoping to get a handle to the monitor passed to the callback function, but no such luck. The callback function was never called, and EnumDisplayMonitors returned FALSE (without setting an error code):
struct FoundMatch
{
BOOL found;
HMONITOR hMonitor;
};
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
{
FoundMatch* pfm = reinterpret_cast<FoundMatch*>(dwData);
pfm->found = TRUE;
pfm->hMonitor = hMonitor;
return FALSE; // stop enumerating
}
// elsewhere, after getting the device name and using it to create a DC
FoundMatch fm;
fm.found = FALSE;
fm.hMonitor = NULL;
BOOL result = EnumDisplayMonitors(hDC, NULL, MonitorEnumProc, reinterpret_cast<LPARAM>(&fm));
Sorry for such a late reply but maybe someone can find this useful.
The multi-monitor API is really minimalist to say the least. Once you've got your dd.DeviceName, it appears you have to go through EnumDisplayMonitors() enumeration until you find a match of dd.DeviceName against MONITORINFOEX.szDevice.
The MONITORINFOEX structure can be obtained by calling GetMonitorInfo().
Here is a non-compilable C++11 pseudo code:
struct DataBag
{
HMONITOR hmon;
TCHAR* devname;
} bag;
bag.hmon = NULL;
bag.devname = &dd.DeviceName;
BOOL bRes = EnumDisplayMonitors(
NULL, NULL,
[](HMONITOR hMonitor, HDC hDC, LPRECT rc, LPARAM data) -> BOOL {
auto& bag = *reinterpret_cast<DataBag*>(data);
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
if (/* match bag.devname against mi.szDevice */ && GetMonitorInfo(hMonitor, &mi))
{
bag.hmon = hMonitor;
return FALSE;
}
return TRUE;
},
reinterpret_cast<LPARAM>(&bag));
if (bRes && bag.hmon)
{
// Monitor found!
}

Does marshaling a marshaled interface give me a marshaller to the proxy or the original interface?

Here is a concrete example:
I create a IWeBrowser2 interface by calling wb.CoCreateInstance(CLSID_InternetExplorer, 0, CLSCTX_SERVER);. This gives me a marshaled interface from my process into whichever of the running iexplore.exe processes happens to contain this browser tab in my thread A.
Now I use the IGlobalInterfaceTable to get a cookie for this interface, pass it to my thread B and request the marshaled interface from there.
Question: Do I get a proxy to the proxy in my thread A or directly to the instance in the IE process?
It seems sensible to me that I will get a direct proxy to the instance with its own reference to it, however:
If I end my thread A, the cookie I created there becomes invalid and I can't retrieve (and close) the interface pointers to the web browsers I created any more. This does not make sense unless there is a thunk in that thread that is destroyed when the thread quits.
Edit: Oh, both threads are STA.
I finally had some time to figure out what is happening, so I wrote a short test to see what is going on.
// MarshalTest.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
enum { WM_THEREYOUGO = WM_USER+1, WM_THANKYOU, WM_YOURWELCOME };
DWORD WINAPI TheOtherThread(DWORD * main_thread_id)
{
MSG msg = { 0 };
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
assert(SUCCEEDED(hr));
{
// create web browser
CComPtr<IWebBrowser2> wb;
hr = wb.CoCreateInstance(CLSID_InternetExplorer, 0, CLSCTX_SERVER);
assert(SUCCEEDED(hr) && wb);
// navigate
hr = wb->Navigate2(&CComVariant(_T("stackoverflow.com")), &CComVariant(0), &CComVariant(_T("")), &CComVariant(), &CComVariant());
assert(SUCCEEDED(hr));
hr = wb->put_Visible(VARIANT_TRUE);
assert(SUCCEEDED(hr));
// Marshal
DWORD the_cookie = 0;
{
CComPtr<IGlobalInterfaceTable> com_broker;
hr = com_broker.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
assert(SUCCEEDED(hr));
hr = com_broker->RegisterInterfaceInGlobal(wb, __uuidof(IWebBrowser2), &the_cookie);
}
// notify main thread
PostThreadMessage(*main_thread_id, WM_THEREYOUGO, the_cookie, NULL);
// message loop
while(GetMessage(&msg, 0, 0, 0)) {
if(msg.hwnd == NULL) {
// thread message
switch(msg.message) {
case WM_THANKYOU:
PostQuitMessage(0);
break;
}
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
CoUninitialize();
PostThreadMessage(*main_thread_id, WM_YOURWELCOME, 0, NULL);
return msg.wParam;
}
int _tmain(int argc, _TCHAR* argv[])
{
MSG msg = {0};
DWORD main_thread_id = GetCurrentThreadId();
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
assert(SUCCEEDED(hr));
{
DWORD ThreadId = 0;
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)TheOtherThread, &main_thread_id, 0, &ThreadId);
DWORD the_cookie = 0;
CComPtr<IWebBrowser2> wb, wb2;
while(GetMessage(&msg, 0, 0, 0)) {
if(msg.hwnd == NULL) {
// thread message
switch(msg.message) {
case WM_THEREYOUGO:
// we got the cookie.
the_cookie = msg.wParam;
// get the browser. This should work.
{
CComPtr<IGlobalInterfaceTable> com_broker;
hr = com_broker.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
assert(SUCCEEDED(hr));
hr = com_broker->GetInterfaceFromGlobal(the_cookie, __uuidof(IWebBrowser2), (void**)&wb);
assert(SUCCEEDED(hr) && wb);
}
// do something with it.
hr = wb->put_FullScreen(VARIANT_TRUE);
assert(SUCCEEDED(hr));
// signal the other thread.
PostThreadMessage(ThreadId, WM_THANKYOU, 0, NULL);
break;
case WM_YOURWELCOME:
// the other thread has ended.
PostQuitMessage(0);
break;
}
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// the other thread has ended. Try getting the interface again.
{
CComPtr<IGlobalInterfaceTable> com_broker;
hr = com_broker.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
assert(SUCCEEDED(hr));
hr = com_broker->GetInterfaceFromGlobal(the_cookie, __uuidof(IWebBrowser2), (void**)&wb2);
//assert(SUCCEEDED(hr) && wb2); // this fails, hr == E_INVALIDARG.
// clean up, will not be executed.
if(SUCCEEDED(hr)) {
hr = com_broker->RevokeInterfaceFromGlobal(the_cookie);
}
}
// try using it
if(wb2) {
hr = wb2->put_FullScreen(VARIANT_FALSE);
assert(SUCCEEDED(hr));
} else if(wb) {
// this succeeds
hr = wb->put_FullScreen(VARIANT_FALSE);
assert(SUCCEEDED(hr));
}
CloseHandle(hThread);
}
CoUninitialize();
return msg.wParam;
}
The bottom line is this:
Ending the thread that registered the interface invalidates the cookie.
The already marshaled interface stays valid. (In this case, that is.)
This means that I get a proxy to the IE process instead of to the other thread's object.
You already got a proxy on thread A since you asked for an out-of-process server. What happens next depends on the kind of apartment that thread A lives in, the argument to CoInitializeEx(). If it is MTA you will definitely get the same proxy in thread B, assuming it is MTA as well. The added reference count should keep it alive if Thread A exits. If it is STA then I'm not 100% sure but think you ought to get a new one. Easy to test btw, just use the one from thread A and you'll get RPC_E_WRONGTHREAD if a new one would have to be created.
I don't have a great explanation for why the thread A exit kills the proxy for thread B. Unless you call IGlobalInterfaceTable::RevokeInterfaceFromGlobal(). Which you'd normally do.

Resources