I am creating a service, which is waiting for a Logon event. When this event occurs, the service should start a thread, which will do the rest of the work.
In the service:
while ( WaitForSingleObject( ghSvcStopEvent, 0 ) != WAIT_OBJECT_0 )
{
DWORD dwEventFlags;
BOOL bRes;
// WTSWaitSystemEvent waits until a logon event ocurs
bRes = WTSWaitSystemEvent( WTS_CURRENT_SERVER_HANDLE, WTS_EVENT_LOGON, &dwEventFlags );
if ( dwEventFlags == WTS_EVENT_NONE )
{
ShowErrorText( "Cancelling waiting for logon event. Service shutting down.", 0, true );
}
if ( bRes )
{
// Someone has logged on
HANDLE hThread = CreateThread( NULL, 0, ServiceWorkerThread, NULL, 0, &dwThreadID );
}
else
{
ShowErrorText( "WTSWaitSystemEvent failed.", GetLastError(), true );
}
}//while
My question is, how can I close correctly the handles returned by CreateThread? If I want to use WaitForMultipleObjects then the first parameter is the size of the threads. The maximum number of object handles is MAXIMUM_WAIT_OBJECTS, which is 36. So this means that I can only start 36 threads. But if the service is running a very long time, then it won't be enough, if a user is logging on more than 36 times, without restarting the system.
Use a thread pool to avoid needing to destroy threads. When your application exits you do not need to close the handles - Windows will do that for you.
Related
I am trying to code a simple firewall application which can allow or block network connection attempts made from userlevel processes.
To do so, following the WFPStarterKit tutorial, I created a WFP Driver which is set to intercept data at FWPM_LAYER_OUTBOUND_TRANSPORT_V4 layer.
The ClassifyFn callback function is responsible for intercepting the connection attempt, and either allow or deny it.
Once the ClassifyFn callback gets hit, the ProcessID of the packet is sent, along with a few other info, to a userlevel process through the FltSendMessage function.
The userlevel process receives the message, checks the ProcessID, and replies a boolean allow/deny command to the driver.
While this approach works when blocking a first packet, in some cases (expecially when allowing multiple packets) the code generates a BSOD with the INVALID_PROCESS_ATTACH_ATTEMPT error code.
The error is triggered at the call to FltSendMessage.
While I am still unable to pinpoint the exact problem,
it seems that making the callout thread wait (through FltSendMessage) for a reply from userlevel can generate this BSOD error on some conditions.
I would be very grateful if you can point me to the right direction.
Here is the function where I register the callout:
NTSTATUS register_example_callout(DEVICE_OBJECT * wdm_device)
{
NTSTATUS status = STATUS_SUCCESS;
FWPS_CALLOUT s_callout = { 0 };
FWPM_CALLOUT m_callout = { 0 };
FWPM_DISPLAY_DATA display_data = { 0 };
if (filter_engine_handle == NULL)
return STATUS_INVALID_HANDLE;
display_data.name = EXAMPLE_CALLOUT_NAME;
display_data.description = EXAMPLE_CALLOUT_DESCRIPTION;
// Register a new Callout with the Filter Engine using the provided callout functions
s_callout.calloutKey = EXAMPLE_CALLOUT_GUID;
s_callout.classifyFn = example_classify;
s_callout.notifyFn = example_notify;
s_callout.flowDeleteFn = example_flow_delete;
status = FwpsCalloutRegister((void *)wdm_device, &s_callout, &example_callout_id);
if (!NT_SUCCESS(status)) {
DbgPrint("Failed to register callout functions for example callout, status 0x%08x", status);
goto Exit;
}
// Setup a FWPM_CALLOUT structure to store/track the state associated with the FWPS_CALLOUT
m_callout.calloutKey = EXAMPLE_CALLOUT_GUID;
m_callout.displayData = display_data;
m_callout.applicableLayer = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
m_callout.flags = 0;
status = FwpmCalloutAdd(filter_engine_handle, &m_callout, NULL, NULL);
if (!NT_SUCCESS(status)) {
DbgPrint("Failed to register example callout, status 0x%08x", status);
}
else {
DbgPrint("Example Callout Registered");
}
Exit:
return status;
}
Here is the callout function:
/*************************
ClassifyFn Function
**************************/
void example_classify(
const FWPS_INCOMING_VALUES * inFixedValues,
const FWPS_INCOMING_METADATA_VALUES * inMetaValues,
void * layerData,
const void * classifyContext,
const FWPS_FILTER * filter,
UINT64 flowContext,
FWPS_CLASSIFY_OUT * classifyOut)
{
UNREFERENCED_PARAMETER(layerData);
UNREFERENCED_PARAMETER(classifyContext);
UNREFERENCED_PARAMETER(flowContext);
UNREFERENCED_PARAMETER(filter);
UNREFERENCED_PARAMETER(inMetaValues);
NETWORK_ACCESS_QUERY AccessQuery;
BOOLEAN SafeToOpen = TRUE;
classifyOut->actionType = FWP_ACTION_PERMIT;
AccessQuery.remote_address = inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value.uint32;
AccessQuery.remote_port = inFixedValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_PORT].value.uint16;
// Get Process ID
AccessQuery.ProcessId = (UINT64)PsGetCurrentProcessId();
if (!AccessQuery.ProcessId)
{
return;
}
// Here we connect to our userlevel application using FltSendMessage.
// Some checks are done and the SafeToOpen variable is populated with a BOOLEAN which indicates if to allow or block the packet.
// However, sometimes, a BSOD is generated with an INVALID_PROCESS_ATTACH_ATTEMPT error on the FltSendMessage call
QueryUserLevel(QUERY_NETWORK, &AccessQuery, sizeof(NETWORK_ACCESS_QUERY), &SafeToOpen, NULL, 0);
if (!SafeToOpen) {
classifyOut->actionType = FWP_ACTION_BLOCK;
}
return;
}
WFP drivers communicate to user-mode applications using the inverted call model. In this method, you keep an IRP from the user-mode pending at your kernel-mode driver instance and whenever you want to send data back to the user-mode you complete the IRP along with the data you want to send back.
The problem was that sometimes the ClassifyFn callback function can be called at IRQL DISPATCH_LEVEL.
FltSendMessage does not support DISPATCH_LEVEL, as it can only be run at IRQL <= APC_LEVEL.
Running at DISPATCH_LEVEL can cause this function to generate a BSOD.
I solved the problem by invoking FltSendMessage from a worker thread which runs at IRQL PASSIVE_LEVEL.
The worker thread can be created using IoQueueWorkItem.
I have an interactive Win32 application and at some point I need to launch another application and wait until that other application has finished. During the time the other application runs, the interactive application shouldn't be responsive expect for resizing and moving the window (this implies of course that the interactive application still should continue redrawing).
My current approach is this:
Create a Thread T
Disable the main window (with EnableWindow(handle, FALSE))
Continue message loop
Special WM_APP message sent from thread T will enable the main window again (EnableWindow(handle, TRUE));
Thread T:
launch the application with CreateProcess
wait until application has terminated using WaitForSingleObject
post a special WM_APP message to the main window using PostMessage.
thread terminates here
This works fine, but I'm not sure if this is the correct approach.
Is there a better approach or should I continue like this?
Your approach is fine, just make sure to use PostMessage() for
send a special WM_APP message to the main window
to avoid deadlock if the main thread happens to wait for thread T.
As commenter noted, an alternative for creating a thread is to use a message loop with MsgWaitForMultipleObjectsEx.
The advantages I see:
There is no additional thread required, so there will be no worries about thread-related problems such as race conditions and deadlocks. These problems are often hard to find and debug (typically they only happen on customer machines), so I try to avoid threads as much as possible.
The program flow for creating the process and waiting for it is simpler. It can be sequential instead of event-based like the thread solution.
You have to judge for yourself if this is a better approach for your scenario.
A function that can be used to wait for a process (or any other waitable handle) while processing messages could be as follows. It's implementation is pretty involved (for background info see the links at the end of my answer) but usage is quite easy (see example afterwards).
// Function to process messages until the state of any of the given objects is signaled,
// the timeout is reached or a WM_QUIT message is received.
// Parameter hDialog can be nullptr, if there is no dialog.
//
// Returns ERROR_SUCCESS if any of the given handles is signaled.
// Returns ERROR_TIMEOUT in case of timeout.
// Returns ERROR_CANCELLED if the WM_QUIT message has been received.
// Returns the value of GetLastError() if MsgWaitForMultipleObjectsEx() fails.
DWORD WaitForObjectsWithMsgLoop(
HWND hDialog, const std::vector<HANDLE>& handles, DWORD timeOutMillis = INFINITE )
{
if( handles.empty() )
return ERROR_INVALID_PARAMETER;
DWORD handleCount = static_cast<DWORD>( handles.size() );
DWORD startTime = GetTickCount();
DWORD duration = 0;
do
{
DWORD status = MsgWaitForMultipleObjectsEx(
handleCount, handles.data(),
timeOutMillis - duration,
QS_ALLINPUT,
MWMO_INPUTAVAILABLE );
if( status == WAIT_FAILED )
{
// MsgWaitForMultipleObjectsEx() has failed.
return GetLastError();
}
else if( status >= WAIT_OBJECT_0 && status < WAIT_OBJECT_0 + handleCount )
{
// Any of the handles is signaled.
return ERROR_SUCCESS;
}
else if( status == WAIT_OBJECT_0 + handleCount )
{
// New input is available, process it.
MSG msg;
while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
if( msg.message == WM_QUIT )
{
// End the message loop because of quit message.
PostQuitMessage( static_cast<int>( msg.wParam ) );
return ERROR_CANCELLED;
}
// Enable message filter hooks (that's what the system does in it's message loops).
// You may use a custom code >= MSGF_USER.
// https://blogs.msdn.microsoft.com/oldnewthing/20050428-00/?p=35753
if( ! CallMsgFilter( &msg, MSGF_USER ) )
{
// Optionally process dialog messages.
if( ! hDialog || ! IsDialogMessage( hDialog, &msg ) )
{
// Standard message processing.
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
}
}
duration = GetTickCount() - startTime;
}
while( duration < timeOutMillis );
// Timeout reached.
return ERROR_TIMEOUT;
}
The function could be used in a dialog box procedure as follows. Error handling omitted for brevity.
INT_PTR CALLBACK YourDialogProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
static bool s_childProcessRunning = false;
switch( message )
{
case YourMessageToLaunchProcess:
{
// prevent reentrancy in case the process is already running
if( s_childProcessRunning )
{
MessageBoxW( hDlg, L"Process already running", L"Error", MB_ICONERROR );
return TRUE;
}
// Prepare CreateProcess() arguments
STARTUPINFO si{ sizeof(si) };
PROCESS_INFORMATION pi{};
wchar_t command[] = L"notepad.exe"; // string must be writable!
// Launch the process
if( CreateProcessW( NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) )
{
// Set flag to prevent reentrancy.
s_childProcessRunning = true;
// Wait until the child process exits while processing messages
// to keep the window responsive.
DWORD waitRes = WaitForObjectsWithMsgLoop( hDlg, { pi.hProcess } );
// TODO: Check waitRes for error
s_childProcessRunning = false;
// Cleanup
CloseHandle( pi.hThread );
CloseHandle( pi.hProcess );
}
return TRUE;
}
// more message handlers...
}
return FALSE;
}
Obligatory Old New Thing links:
Pumping messages while waiting for a period of time
The dialog manager, part 4: The dialog loop
Rescuing thread messages from modal loops via message filters
I've tried several ways to synch the following scenario. The last way I tried is to use an event to signal after FifoQueueData();. Thread 2 would then release from the event and send the data over the network. The problem is that thread 1 loops around too fast and signals the event before Thread 2 waits on that same event. This causes the event to wait until it times out.
thread1 {
for(1 .. 10) {
GenerateData();
FifoQueueData();
}
signal();
}
thread2 {
while(not signalled) {
WaitForQueuedData();
FifoDequeueData();
SendDataOverNetwork();
}
}
Because you are on Windows, you could indeed use the thread message queue for synchronization (this particular queue is threadsafe, maybe message queues are what Olaf means by "normally"). To do that, use GetMessage and PostThreadMessage.
Be aware, however, that message queues are accessible from other code -- for type safety, you might put objects in your own queue, and use the thread message queue only for waking the other thread. (To see why this is important, read about "window shatter attacks"). In that case, you might as well just be using auto-reset events.
The last way I tried is to use an event to signal after FifoQueueData();. Thread 2 would then release from the event and send the data over the network. The problem is that thread 1 loops around too fast and signals the event before Thread 2 waits on that same event. This causes the event to wait until it times out.
If you are going to use events, use 2 manual-reset events and a thread-safe lock, like a critical section. One event signals when the queue has space available, and one event signals when the queue has data. Thread1 would wait for the Available event to be signaled, lock the queue, put data into it, set the Data event, and reset the Available event if the queue is full, before finally unlocking the queue. Thread2 would wait for the Data event, lock the queue, dequeue an item, set the Available event, and reset the Data event if the queue is empty, before finally unlocking the queue and processing the item.
CRITICAL_SECTION csQueue;
HANDLE hAvailableEvent, hDataEvent;
BOOL bFinished;
...
InitializeCriticalSection(&csQueue);
hAvailableEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
hDataEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
bFinished = FALSE;
...
void FifoQueueData(BOOL IsFinished)
{
WaitForSingleObject(hAvailableEvent, INFINITE);
EnterCriticalSection(&csQueuelock);
if (IsFinished)
bFinished = TRUE;
else
{
// put data in the queue
if (queue is full)
ResetEvent(hAvailableEvent);
}
SetEvent(hDataEvent);
LeaveCriticalSection(&csQueuelock);
}
BOOL FifoDequeueData()
{
WaitForSingleObject(hDataEvent, INFINITE);
EnterCriticalSection(&csQueuelock);
if ((queue is empty) && (bFinished))
{
LeaveCriticalSection(&csQueuelock);
return FALSE;
}
// remove data from the queue
SetEvent(hAvailableEvent);
if (queue is empty)
ResetEvent(hDataEvent);
LeaveCriticalSection(&csQueuelock);
}
thread1
{
bFinished = FALSE;
for(1 .. 10)
{
GenerateData();
FifoQueueData(FALSE);
}
FifoQueueData(TRUE);
}
thread2
{
while (FifoDequeueData())
SendDataOverNetwork();
}
Another option is to use an I/O Completion Port. Call CreateIoCompletionPort() before starting the threads, then have Thread1 queue data using PostQueuedCompletionStatus() and Thread2 dequeue data using GetQueuedCompletionStatus(). IOCP uses a FIFO queue and is self-syncing.
HANDLE hQueue;
...
hQueue = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
...
void FifoQueueData(BOOL IsFinished)
{
if (IsFinished)
PostQueuedCompletionStatus(hQueue, 0, 1, NULL);
else
{
LPBYTE buffer = LocalAlloc(LMEM_FIXED, size of data);
if (buffer)
{
// copy data into buffer...
if (!PostQueuedCompletionStatus(hQueue, size of data, 0, (LPOVERLAPPED)buffer))
LocalFree(buffer);
}
}
}
BOOL FifoDequeueData()
{
DWORD dwSize = 0;
ULONG_PTR ulCompletionKey = 0;
LPBYTE buffer;
if (!GetQueuedCompletionStatus(hQueue, &dwSize, &ulCompletionKey, (LPOVERLAPPED*)&buffer, INFINITE))
return FALSE;
if (ulCompletionKey == 1)
return FALSE;
// copy data from buffer up to dwSize bytes as needed...
LocalFree(buffer);
return TRUE;
}
thread1
{
for(1 .. 10)
{
GenerateData();
FifoQueueData(FALSE);
}
FifoQueueData(TRUE);
}
thread2
{
while (FifoDequeueData())
SendDataOverNetwork();
}
Could someone please help me? I'm trying to create a jobobject with JOB_OBJECT_SECURITY_ONLY_TOKEN but SetInformationJobObject always fails with error code 6 ( Invalid handle ).
Here is my code:
HANDLE Job( CreateJobObject( NULL, NULL ) );
if( !Job )
{
wprintf( L"Could not create job object, error %d\n", GetLastError() );
return 1;
}
JOBOBJECT_SECURITY_LIMIT_INFORMATION SecLimit = { 0 };
SecLimit.SecurityLimitFlags = JOB_OBJECT_SECURITY_ONLY_TOKEN;
if ( !SetInformationJobObject( Job, JobObjectSecurityLimitInformation, &SecLimit, sizeof( SecLimit ) ) )
{
wprintf( L"Could not associate job with IO completion port, error %d\n", GetLastError() );
return 1;
}
I am trying to run this app on XP!
Thanks!
You haven't set SecLimit.JobToken to a valid handle to a restricted token. That's why you're getting an invalid handle error. If you use the JOB_OBJECT_SECURITY_ONLY_TOKEN flag, SecLimit.JobToken is mandatory.
Unless there is some particular reason why you need to force processes in the job to use a restricted token, don't use the JOB_OBJECT_SECURITY_ONLY_TOKEN flag.
Note that the article you reference does not in fact say that this flag is required. It only says that if you use this flag the process must be created suspended in order to assign it to the job object.
I have an issue in my use of critical sections. My app has a large number of threads, say 60, which all need access to a global resource. I therefore protect that resource with a critical section. This works perfectly during operation, however when my application shuts down, I trigger the threads to quit, and then destroy the critical section.
The problem comes if some of those threads are waiting on the critical section at exit time, and thus are blocked from quitting themselves.
I've written a wrapper around the windows CriticalSection calls that has an 'Initialised' flag, which I set to true when the crit is created, and set to false when I'm about to leave the crit (both cases are set when inside the crit). This flag is checked before the 'enter crit' wrapper function tries entering the crit, bypassing the request if the flag is false. The flag is also checked the moment any thread successfully enters the crit, making it immediately leave the crit if it's false.
What I to do prior to deleting the crit is set the flag to false, then wait for any waiting threads to: be allowed into the crit; see the Initialised flag is false; then leave the crit (which should be a pretty quick operating for each thread).
I check the number of threads waiting for access to the crit by checking the LockCount inside the CRITICAL_SECTION struct, and waiting until it hits 0 (in XP, that's LockCount - (RecursionCount-1); in 2003 server and above, the lock count is ((-1) - (LockCount)) >> 2), before I then destroy the critical section.
This should be sufficient, however I'm finding that the LockCount reaches 0 when there's still one thread (always just one thread, never more) waiting to enter the crit, meaning if I delete the crit at that point, the other thread subsequently wakes up from waiting on the crit, and causes a crash as the CRITICAL_SECTION object has by that time been destroyed.
If I keep my own internal lock count of threads waiting for access, I have the correct count; however this isn't ideal as I have to increment this count outside of the crit, meaning that value isn't protected and therefore can't be entirely relied upon at any one time.
Does anyone know why the LockCount in the CRITICAL_SECTION struct would be out by 1? If I use my own lock count, then check the CRITICAL_SECTION's lock count after that last thread has exited (and before I destroy the crit), it's still 0...
Or, is there a better way for me to protect the global resource in my app with that many threads, other than a critical section?
This is my wrapper struct:
typedef struct MY_CRIT {
BOOL Initialised;
CRITICAL_SECTION Crit;
int MyLockCount;
}
Here's my Crit init function:
BOOL InitCrit( MY_CRIT *pCrit )
{
if (pCrit)
{
InitializeCriticalSection( &pCrit->Crit );
pCrit->Initialised = TRUE;
pCrit->MyLockCount = 0;
return TRUE;
}
// else invalid pointer
else
return FALSE;
}
This is my enter crit wrapper function:
BOOL EnterCrit( MY_CRIT *pCrit )
{
// if pointer valid, and the crit is initialised
if (pCrit && pCrit->Initialised)
{
pCrit->MyLockCount++;
EnterCriticalSection( &pCrit->Crit );
pCrit->MyLockCount--;
// if still initialised
if (pCrit->Initialised)
{
return TRUE;
}
// else someone's trying to close this crit - jump out now!
else
{
LeaveCriticalSection( &pCrit->Crit );
return FALSE;
}
}
else // crit pointer is null
return FALSE;
}
And here's my FreeCrit wrapper function:
void FreeCrit( MY_CRIT *pCrit )
{
LONG WaitingCount = 0;
if (pCrit && (pCrit->Initialised))
{
// set Initialised to FALSE to stop any more threads trying to get in from now on:
EnterCriticalSection( &pCrit->Crit );
pCrit->Initialised = FALSE;
LeaveCriticalSection( &pCrit->Crit );
// loop until all waiting threads have gained access and finished:
do {
EnterCriticalSection( &pCrit->Crit );
// check if any threads are still waiting to enter:
// Windows XP and below:
if (IsWindowsXPOrBelow())
{
if ((pCrit->Crit.LockCount > 0) && ((pCrit->Crit.RecursionCount - 1) >= 0))
WaitingCount = pCrit->Crit.LockCount - (pCrit->Crit.RecursionCount - 1);
else
WaitingCount = 0;
}
// Windows 2003 Server and above:
else
{
WaitingCount = ((-1) - (pCrit->Crit.LockCount)) >> 2;
}
// hack: if our own lock count is higher, use that:
WaitingCount = max( WaitingCount, pCrit->MyLockCount );
// if some threads are still waiting, leave the crit and sleep a bit, to give them a chance to enter & exit:
if (WaitingCount > 0)
{
LeaveCriticalSection( &pCrit->Crit );
// don't hog the processor:
Sleep( 1 );
}
// when no other threads are waiting to enter, we can safely delete the crit (and leave the loop):
else
{
DeleteCriticalSection( &pCrit->Crit );
}
} while (WaitingCount > 0);
}
}
You are responsible to make sure that CS is not in use any longer before destroying it. Let us say that no other thread is currently trying to enter, but there is a chance that it is going to attempt very soon. Now you destroy the CS, what this concurrent thread is going to do? At its full pace it hits deleted critical section causing memory access violation?
The actual solution depends on your current app design, but if you are destroying threads, then you will perhaps want to flag your request to stop those threads, and then wait on theit handles to wait for their destruction. And then complete with deleting critical section when you are sure that threads are done.
Note that it is unsafe to rely on CS member values such as .LockCount, and having done things right way you will prehaps not even need thing like IsWindowsXPOrBelow. Critical section API suggest that you use CRITICAL_SECTION structure as "black box" leaving the internals to be implementation specific.