How to Fire Windows Service stop event on System restart? - visual-c++

I have written Windows Service in VC++ to mount Drives on System restart.
Now when i restart the system, on system shutdown i want to fire my service stop event which is not getting fired.
I have set Windows service properties as automatic but it does not work.
When i manually click on stop button stop event get fired.
Any help is apprecaited.
My code looks like :
void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
switch(Opcode)
{
case SERVICE_CONTROL_PAUSE:
m_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
m_ServiceStatus.dwWin32ExitCode = 0;
m_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
Disconnect() ;// This method i want to get called on system shot down automatically.
SetServiceStatus (m_ServiceStatusHandle,&m_ServiceStatus);
bRunning=false;
break;
case SERVICE_CONTROL_INTERROGATE:
break;
}
return;
}

This is relatively straight forward.
Either handle the SERVICE_CONTROL_SHUTDOWN in your current callback handler function by adding another case to the switch statement. Probably something like:
void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
switch(Opcode)
{
case SERVICE_CONTROL_PAUSE:
m_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
m_ServiceStatus.dwWin32ExitCode = 0;
m_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
Disconnect() ;// This method i want to get called on system shot down automatically.
SetServiceStatus (m_ServiceStatusHandle,&m_ServiceStatus);
bRunning=false;
break;
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
Disconnect();
break;
}
return;
}
OR:
Instead of registering the callback function with RegisterServiceCtrlHandler use RegisterServiceCtrlHandlerEx. This new callback method is preferred. Your callback function's signature needs to match HandlerEx, see MSDN for more info.
DWORD WINAPI HandlerEx(
__in DWORD dwControl,
__in DWORD dwEventType,
__in LPVOID lpEventData,
__in LPVOID lpContext
);
Add another case to your switch statement, either SERVICE_CONTROL_PRESHUTDOWN (not available on XP/Server 2003) or SERVICE_CONTROL_SHUTDOWN, read the warnings about handling these notifications in the HandlerEx documentation referenced above.

Related

CreateFile on Mailslot fails with Error 53 ERROR_BAD_NETPATH after 2018-05 Windows 10 Feature Update 1803

commands such as CreateFile("\\mycomputer\mailslot\this_fails",...) fail with last error = 53 ERROR_BAD_NETPATH
That fails if used with any valid or non-existing computer name including the same computer on which the test is running. On computers where this works, it succeeds and returns a mailslot handle even if the referenced computer does not exist or does not have a mailslot created with that name. Note that if an non-existing computer name or mailslot is used, subsequent WriteFiles on the handle will fail, but the CreateFile does succeed.
However, the CreateFile above will succeed if the Mailslot reference is explicitly local: "\\.\mailslot\always_works"
This worked on all versions of Windows previously until the 2018-05 cumulative updates were installed. Specifically KB4103721 (Windows 10 home) seemed to be the culprit. [Edit: as noted in answers below, it is actually Feature Update Build 1803 that causes this issue.]
Test Client: (works with no parameter or "." but fails with any computername).
Based on msdn sample
Syntax: testclient [server computername]
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
LPTSTR SlotName = TEXT("\\\\%hs\\mailslot\\sample_mailslot");
BOOL WriteSlot(HANDLE hSlot, LPTSTR lpszMessage)
{
BOOL fResult;
DWORD cbWritten;
fResult = WriteFile(hSlot,
lpszMessage,
(DWORD) (lstrlen(lpszMessage)+1)*sizeof(TCHAR),
&cbWritten,
(LPOVERLAPPED) NULL);
if (!fResult)
{
// this failure is valid if computername is not valid
printf("WriteFile failed with %d.\n", GetLastError());
return FALSE;
}
printf("Slot written to successfully.\n");
return TRUE;
}
int main(int nArgs,char * arg[])
{
HANDLE hFile;
TCHAR szSlot[256];
_stprintf (szSlot,SlotName,nArgs > 1 ? arg[1] : ".");
_tprintf(TEXT("Writing to slot %s\n"),szSlot);
hFile = CreateFile(szSlot,
GENERIC_WRITE,
FILE_SHARE_READ,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
// this is the failure I'm trying to debug
printf("CreateFile failed with %d.\n", GetLastError());
return FALSE;
}
WriteSlot(hFile, TEXT("Message one for mailslot."));
WriteSlot(hFile, TEXT("Message two for mailslot."));
Sleep(5000);
WriteSlot(hFile, TEXT("Message three for mailslot."));
CloseHandle(hFile);
return TRUE;
}
Test Server: (reads a displays sent messages)
Note that duplicate messages may be received because Mailslot messages are transmitted over all possible protocols. Based on msdn sample.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
HANDLE hSlot;
LPTSTR SlotName = TEXT("\\\\.\\mailslot\\sample_mailslot");
BOOL ReadSlot()
{
DWORD cbMessage, cMessage, cbRead;
BOOL fResult;
LPTSTR lpszBuffer;
TCHAR achID[80];
DWORD cAllMessages;
HANDLE hEvent;
OVERLAPPED ov;
cbMessage = cMessage = cbRead = 0;
hEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("ExampleSlot"));
if( NULL == hEvent )
return FALSE;
ov.Offset = 0;
ov.OffsetHigh = 0;
ov.hEvent = hEvent;
fResult = GetMailslotInfo( hSlot, // mailslot handle
(LPDWORD) NULL, // no maximum message size
&cbMessage, // size of next message
&cMessage, // number of messages
(LPDWORD) NULL); // no read time-out
if (!fResult)
{
printf("GetMailslotInfo failed with %d.\n", GetLastError());
return FALSE;
}
if (cbMessage == MAILSLOT_NO_MESSAGE)
{
printf("Waiting for a message...\n");
return TRUE;
}
cAllMessages = cMessage;
while (cMessage != 0) // retrieve all messages
{
// Create a message-number string.
StringCchPrintf((LPTSTR) achID,
80,
TEXT("\nMessage #%d of %d\n"),
cAllMessages - cMessage + 1,
cAllMessages);
// Allocate memory for the message.
lpszBuffer = (LPTSTR) GlobalAlloc(GPTR,
lstrlen((LPTSTR) achID)*sizeof(TCHAR) + cbMessage);
if( NULL == lpszBuffer )
return FALSE;
lpszBuffer[0] = '\0';
fResult = ReadFile(hSlot,
lpszBuffer,
cbMessage,
&cbRead,
&ov);
if (!fResult)
{
printf("ReadFile failed with %d.\n", GetLastError());
GlobalFree((HGLOBAL) lpszBuffer);
return FALSE;
}
// Concatenate the message and the message-number string.
StringCbCat(lpszBuffer,
lstrlen((LPTSTR) achID)*sizeof(TCHAR)+cbMessage,
(LPTSTR) achID);
// Display the message.
_tprintf(TEXT("Contents of the mailslot: %s\n"), lpszBuffer);
GlobalFree((HGLOBAL) lpszBuffer);
fResult = GetMailslotInfo(hSlot, // mailslot handle
(LPDWORD) NULL, // no maximum message size
&cbMessage, // size of next message
&cMessage, // number of messages
(LPDWORD) NULL); // no read time-out
if (!fResult)
{
printf("GetMailslotInfo failed (%d)\n", GetLastError());
return FALSE;
}
}
CloseHandle(hEvent);
return TRUE;
}
BOOL WINAPI MakeSlot(LPTSTR lpszSlotName)
{
hSlot = CreateMailslot(lpszSlotName,
0, // no maximum message size
MAILSLOT_WAIT_FOREVER, // no time-out for operations
(LPSECURITY_ATTRIBUTES) NULL); // default security
if (hSlot == INVALID_HANDLE_VALUE)
{
printf("CreateMailslot failed with %d\n", GetLastError());
return FALSE;
}
return TRUE;
}
void main()
{
MakeSlot(SlotName);
while(TRUE)
{
ReadSlot();
Sleep(3000);
}
}
The test server to read messages, and the test client to send messages can be run in different cmd shells on the same computer, or run on different computers. When it fails, it fails immediately and seems to be a problem trying to resolve the network path name. On the same computer, file shares such as \\ThisComputer\share work properly from the same computer or a different one.
NetBIOS is enabled over TCP/IP for the network adapters in use. The network adapters are designated as Private. Firewall was disabled for testing. File and Printer sharing are enabled. Computers are in same workgroup. Computer name resolution works, and this fails even if IP addresses are used (even 127.0.0.1).
The issue is already fixed since last Year
September 26, 2018—KB4458469 (OS Build 17134.320)
Addresses an issue that causes NTLTEST, DCLOCATOR, or joining an
Active Directory and SAMBA domain to fail when using the NetBIOS
domain name. The error is “An Active Directory domain Controller (AD
DC) for the domain %domain% could not be contacted”. This also
addresses connection issues for applications that use mailslots to
communicate.
This seems to be a problem with the latest Feature Update from Windows 10 (1803), not a patch via Windows Update.
Please check if you are using build 17134.48 (Also known as 1803)
Try a downgrade to 1709.
01/09/2019:
With the latests 1809 Build Mailslots are working again
I didn`t find any information that mailslot communication is not longer supported in that way you do this.
I think it is a bug.
But the only way to find out is to open a support ticket via support.microsoft.com.
Or you could post here https://social.technet.microsoft.com/Forums
Until we get any new information from Microsoft everybody who needs mailslots should block the feature upgrade 1803.

Using SendMessageToDescendants between threads

In my MFC application I have a worker thread that listens on a network connection and as soon as some information has arrived I call SendMessageToDescendants to send that information via the lparam parameter. So every frame window will get the message and possibly process it via a message handler for the WM_SYNCHRONOTIFICATION message (WM_SYNCHRONOTIFICATION is a WM_APP+x message).
Code in the worker thread: (simplified for brevity)
while (TRUE)
{
CNotificationMessage notificationmessage;
Listen(&notificationmessage); // blocking until some information arrives
m_pMainWnd->SendMessageToDescendants(WM_SYNCHRONOTIFICATION, NULL, (LPARAM)(newnotif));
// have all OnSynchroNotification handlers been called here ?
}
Message handler in the main thread:
LRESULT CMyFrame::OnSynchroNotification(WPARAM p1, LPARAM p2)
{
CNotificationMessage *pNotification = (CNotificationMessage*)p2;
// process message (ommited for brevity)
}
The code works fine, but I'm not sure if upon return from SendMessageToDescendants all OnSynchroNotification have been called.
The simplest solution to use is a counter. Before calling SendMessage, initialize a shared counter to the number of child windows you would like to have process the message. Each message handler is then responsible for decrementing the counter when it completes its work, and your event loop can check that the counter is 0 before generating more events. In pseudo C++:
unsigned int sharedCount; // global/static shared value
while (TRUE)
{
CNotificationMessage notificationmessage;
Listen(&notificationmessage); // blocking until some information arrives
unsigned int current = InterlockedCompareExchange(&sharedCount, activeWindowCount, 0);
if (current == 0)
{
m_pMainWnd->SendMessageToDescendants(WM_SYNCHRONOTIFICATION, NULL, (LPARAM)(newnotif));
}
else
{
// Still processing the last message.
}
while (InterlockedCompareExchange(&sharedCount, 0, 0) != 0)
{
Sleep(100);
}
}
LRESULT CMyFrame::OnSynchroNotification(WPARAM p1, LPARAM p2)
{
// Processing
InterlockedDecrement(&sharedCount);
}
The slightly more complex solution, but the one I personally prefer, since you don't have to burn CPU waiting on completion, is to create an event for each message processing window, and then use WaitForMultipleObjects (or the Ex version) to halt the event loop until completion. Again, in pseudo C++:
while (TRUE)
{
CNotificationMessage notificationmessage;
Listen(&notificationmessage); // blocking until some information arrives
m_pMainWnd->SendMessageToDescendants(WM_SYNCHRONOTIFICATION, NULL, (LPARAM)(newnotif));
DWORD waitResult = WaitForMultipleObjects(activeWindowCount, FrameEvents, TRUE, INFINITE);
if (waitResult == WAIT_OBJECT_0)
{
// Success
}
else if (waitResult == WAIT_FAILED)
{
// Failure: Use GetLastError() to figure out why the function call failed.
}
// Reset the events
}
LRESULT CMyFrame::OnSynchroNotification(WPARAM p1, LPARAM p2)
{
// Processing
SetEvent(thisFramesEvent);
}
This example uses an infinite timeout, but you can always set a reasonable timeout, and check for the return value WAIT_TIMEOUT to see if the time elapsed.
(Required Disclaimer: Error checking and variable initialization have been removed from both of these for the sake of brevity and readability. See the documentation for how to check for errors.)

Why won't my registry read work vc++?

I am using the following code:
bool DllGuard()
{
HKEY keyHandle;
bool rgValue = bool();
DWORD Size;
DWORD Type;
try
{
if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\MSB", 0, KEY_QUERY_VALUE, &keyHandle) == ERROR_SUCCESS)
{
Type = REG_DWORD;
Size = sizeof(DWORD);
RegQueryValueEx(keyHandle, "DllGuard", NULL, &Type, (LPBYTE)rgValue,&Size);
}
RegCloseKey(keyHandle);
if (rgValue == false)
{
return true;
}
else
{
return false;
}
}
catch (...)
{
return false;
}
}
BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpReserved)
{
if(fdwReason == DLL_PROCESS_ATTACH)
{
if (DllGuard())
{
DisableThreadLibraryCalls(hInstance);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&Main, NULL, 0, NULL);
}
else
{
exit(0);
}
}
return TRUE;
}
First time around it works fine, thus creating the thread in DllMain.
The second time around, it does not work as it still creates the thread in DllMain even if the registry key is set to true.
Help!
Thanks!
(LPBYTE)rgValue shoud be (LPBYTE)&rgValue, as you want to pass a pointer (so rgValue is modified by RegQueryValueEx. And it should be DWORD rgValue instead of bool (maybe you know that they happen to be of the same size, but it's harder to read).
Also it would be great to check the return value of RegQueryValueEx, so we have a chance to know what's wrong next time when it fails. (If you have no idea how to handle a failure, maybe write something with OutputDebugString, to be seen in sysinternals' dbgview.exe).
I can't comment but I think is important to know that you should consider not using DLLMain at all unless you know what you are doing... Give a look at the MSDN pages for DllMain entry point:
Warning There are serious limits on what you can do in a DLL entry point. To provide more complex initialization, create an initialization routine for the DLL. You can require applications to call the initialization routine before calling any other routines in the DLL.
Or you can Be afraid. Be very afraid

Starting a thread in a Windows CE 6.0 service

I am developing a Windows Service under Windows CE 6.0. The project produces a DLL, which gets integrated in OS Image. The service gets started, when the WinCE boots thanks to registry settings.
The problem is that I am not able to start the "Thread1" thread. I should see the MessageBox, but there is nothing on the screen. Why? Putting the MessageBox into SFC_Init works fine.
Another thing - when I type "services list" in the console (in the WinCE system), the state of my service is unknown... Why is that so?
Please help!
// SrvForCom.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
HINSTANCE hInst;
// main entry point of the DLL
BOOL WINAPI DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
hInst = (HINSTANCE)hModule;
switch(ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls((HMODULE)hModule);
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
// function called during initialization process
DWORD SFC_Init(DWORD dwContext) {
PSRVCONTEXT pSrv;
HANDLE hThrd;
DWORD err = ERROR_INVALID_PARAMETER;
// fill the info structure
pSrv->servState = SERVICE_STATE_UNKNOWN;
switch (dwContext) {
case SERVICE_INIT_STARTED:
pSrv->servState = SERVICE_STATE_ON;
break;
case SERVICE_INIT_STOPPED:
pSrv->servState = SERVICE_STATE_OFF;
break;
}
// start new thread
hThrd = CreateThread (NULL, 0, Thread1, NULL, 0, NULL);
if (hThrd) {
CloseHandle (hThrd);
err = 0;
} else {
err = GetLastError();
}
return hThrd;
}
BOOL SFC_Deinit(DWORD dwData) {
return TRUE;
}
BOOL SFC_Open(DWORD dwData, DWORD dwAccess, DWORD dwShareMode) {
PSRVCONTEXT pSrv = (PSRVCONTEXT)dwData;
return (DWORD)pSrv;
}
BOOL SFC_Close(DWORD dwData) {
return 1;
}
BOOL SFC_IOControl(DWORD dwData, DWORD dwCode, PBYTE pBufIn,
DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
PDWORD pdwActualOut) {
PSRVCONTEXT pSrv = (PSRVCONTEXT)dwData;
switch (dwCode) {
case IOCTL_SERVICE_STATUS:
*pBufOut = pSrv->servState;
break;
}
return 1;
}
DWORD WINAPI Thread1(LPVOID lpv) {
MessageBox (NULL, TEXT ("The thread has been successfully started!"), TEXT ("Info"), MB_OK);
return 0;
}
I have found answer to this question. The code above is correct apart from one detail - uninitialized structure. "pSrv" is a pointer (type PSRVCONTEXT) to a struct SRVCONTEXT with "servState" field. When executing "pSrv->servState = SERVICE_STATE_UNKNOWN;" some part of the memmory was overwritten, causing errors in the application.
The solution is to first allocate the memmory to this structure before using it:
pSrv = (PSRVCONTEXT)LocalAlloc (LPTR, sizeof (SRVCONTEXT));
After adding above line to my application, everything started working fine. The thread properly started and the service's state changed to "Running".
P.S. The structure looks like this:
typedef struct {
DWORD servState;
} SRVCONTEXT, *PSRVCONTEXT;

MFC - Posting data via PostMessage to the GUI

I Read a few threads on how passing data from Workerthread to Main window(Dialog), but I still don't understand and still need help.
My workerthread should process some calculations and display the result during each loop to an edit on the GUI. I know, I should use PostMessage but since the calculation I'm doing implies a control element, I don't know how to solve this problem ...
//CWorkerThreadMgr.h manages the second thread
HRESULT Start (HWND hWnd);// from where the workerthread will be started
HWND m_hWnd ; //Window handle to the UI ;
HANDLE m_hTread; //handle of the worker thread
static UINT WINAPI ThreadProc( LPVOID lptest );
static UINT WINAPI ThreadProc( LPVOID lptest )
{
CWorkerThreadMgr* pCalculateMgr = reinterpret_cast< CWorkerThreadMgr*(lptest);
//The following operation:rand() *m_Slider.GetPos() should
//should be calculated and the result displayed each time in the edit box in the gui
for( UINT uCount = 0; uCount < 40; uCount++ ){
pCalculateMgr->rand() *m_Slider.GetPos();//?don't allowed to touch the gui!!
PostMessage(pCalculateMgr-> m_hWnd, WM_SENDCALCULATED_VALUE,wparam(rand() *m_Slider.GetPos(),0);
}
}
LRESULT CStartCalculationDlg::OnSendCalculatedValue( WPARAM Result, LPARAM )
{
// The resut should be displayed in the edit box
m_Calculation.Format(_T("%d"),???);
SetDlgItemText(IDC_CALCULATION, m_Calculation);
return 1;
}
void CStartCalculationDlg::OnHScroll(UINT nSBCode, UINT nPos,CScrollBar* pScrollBar)
{
m_SliderValue.Format(_T("%d"),m_Slider.GetPos());
SetDlgItemText(IDC_SLIDER_VALUE,m_SliderValue);
}
// Implementation in the CStartCalculationDlg.h
CWorkerThreadMgr m_WorkerThreadMgr //instance of the WorkerThreadMgr
CSliderCtrl m_Slider //member variable of the slider control
CString m_SliderValue // member variable of the edit box, where the current value of the
//slider will be displayed
CString m_Calculation // member variable of the edit box where the calculated
//result from the workerthread will be displayed via PostMessage
afx_msg LRESULT OnSendCalculatedValue( WPARAM, LPARAM );
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
The next probem is that, when my slider control is moved and gets a new value, the thread procedure should know it and update the value of the slider. How can I manage to do it?
Instead of reading the slider position from the worker thread, read it in the UI thread and make it available to the worker thread.
I'm really no expert in this field, but I've done some worker threads as explained here.
What I would do in your case is create a static variable, like the "running" flag used in the article linked above, to hold the current position of the slider. Then, in the OnHScroll handler set it to the appropriate value.
As long as you only write to that variable from one thread, you should not have synchronization issues.
From worker thread:
m_data = foo();
PostMessage(hWndMain, UWM_SOME_CUSTOM_MESSAGE, 0, 0);
From UI thread:
LRESULT CMainFrame::OnSomeCustomMessage(WPARAM wParam, LPARAM lParam)
{
CMyData data = m_pWorker->GetData();
// Do stuff...
return 0;
}
GetData must be guarded by a critical section:
CMyData CMyWorker::GetData()
{
// This critical section is used in the worker thread too, whenever m_data is accessed.
m_lock.Lock();
CMyData data = m_data;
m_lock.Unlock();
return data;
}
See CCriticalSection on MSDN.

Resources