Thread: Telling other applications to stop

  1. #1
    Banned nickname_changed's Avatar
    Join Date
    Feb 2003
    Location
    Australia
    Posts
    986

    Telling other applications to stop

    I have an NT Service whos job it is to start an application. All it does it watches the application running, and catches its return code. When the service is told to stop, it has to tell the application to stop.

    This is a snapshot of the WinMain() of the application

    Code:
    int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprev, PSTR cmdline, int ishow)
    {
    	//-----------------------------------------------------------------------------------------
    	// Step 1: Do stuff we must do as a windows application
    	//-----------------------------------------------------------------------------------------
    	WSADATA wsaData;
        WSAStartup(MAKEWORD(1,1), &wsaData);
    
        HWND Hwnd;                                                                      // Handle for this window
        MSG Message;                                                                    // Current message
        WNDCLASSEX WndClassEx = {0};													// Window Class
    	WndClassEx.cbSize = sizeof(WndClassEx);											// Size of itself
    	WndClassEx.lpfnWndProc = WinProc;												// Message Handler
    	WndClassEx.hInstance = hinstance;												// Instance of this window class
        WndClassEx.lpszClassName = "SWEBSWindowClass";									// Class Name
    Then it goes into a loop that continues "while (SERVER_STOP == false)"

    This is the WinProc function of the application:
    Code:
    LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) 
    { 
    	switch(message) 
    	{ 
            case WM_DESTROY:
            case WM_CLOSE:
    		    SERVER_STOP = true;
                PostQuitMessage(0);
                break;
    	    default:
                break;
    	}
        // Report current status
        return DefWindowProc(hwnd, message, wparam, lparam);
    }
    Here is the ControlHandler function of the server, which just handles the stop/shutdown service messages:
    Code:
    	case SERVICE_CONTROL_STOP: 
    		SERVER_STOP = true;
        
            SendMessage((HWND)pi.hProcess , WM_CLOSE, NULL, NULL);
            CloseHandle( pi.hProcess );
            CloseHandle( pi.hThread );
    
            ServiceStatus.dwWin32ExitCode = 0; 
            ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
            SetServiceStatus (hStatus, &ServiceStatus);
            return; 
     
    	case SERVICE_CONTROL_SHUTDOWN: 
            SERVER_STOP = true;
    
            SendMessage((HWND)pi.hProcess, WM_CLOSE, NULL, NULL);
            CloseHandle( pi.hProcess );
            CloseHandle( pi.hThread );
    
            ServiceStatus.dwWin32ExitCode = 0; 
            ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
            SetServiceStatus (hStatus, &ServiceStatus);
            return; 
            
    	default:
            break;
    	}
    Where 'pi' is PROCESS_INFORMATION structure that was passed to the CreateProcess call when the application was started. Its supposed to hold things like handles to the started process (supposedly).

    When the service is told to stop, it does, but the application doesn't. What should I do to make sure the application is shut down properly?

    This is the call to CreateProcess:
    Code:
    if( !CreateProcess( EnginePath.c_str(),                                         // No module name (use command line). 
            NULL,                                                                       // Command line. 
            NULL,                                                                       // Process handle not inheritable. 
            NULL,                                                                       // Thread handle not inheritable. 
            FALSE,                                                                      // Set handle inheritance to FALSE. 
            0,                                                                          // No creation flags. 
            NULL,                                                                       // Use parent's environment block. 
            AppPath.c_str(),                                                            // Starting directory. 
            &si,                                                                        // Pointer to STARTUPINFO structure.
            &pi )                                                                       // Pointer to PROCESS_INFORMATION structure.
        ) 
        {
            TestLog("Could not create process for SWEBSEngine.exe");
        }
    
    SERVER_STOP = false;
    while (SERVER_STOP == false)
    {
            Sleep(3000);
    }
    GetExitCodeProcess(pi.hProcess, &ExitCode);
    
    // Close process and thread handles. 
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );
    How can I tell the application to close properly? It doesn't seem to be working my way

  2. #2
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Firstly, you can't post a message to the process handle but you can post a message to the thread id.

    PostThreadMessage(pi.dwThreadId, WM_CLOSE, 0,0);

    Also Try WM_QUIT if that doesn't work.
    There are some issues here if the service and the application do not share the same window station.

    Suggestion:
    Code:
    while (SERVER_STOP == false)
    {
            Sleep(3000);
    }
    Change to:
    Code:
    while (SERVER_STOP == false)
    {
            if (WaitForSingleObject(pi.hProcess, 1000) != WAIT_TIMEOUT) break;
    }
    This will break from the loop if the application finishes. Also three seconds is too long to wait between checking the stop variable.

    Can I also suggest that your control handler looks like this and you put the other code below the loop:
    Code:
    	case SERVICE_CONTROL_STOP: 
    	case SERVICE_CONTROL_SHUTDOWN: 
    		SERVER_STOP = true;
    Note that you do not need a window in the application to shut it down. There are important security issues here if the service is going to run in a priviliged account.

    I just implemented a similar service that starts an application, so if you need more help/code please post back.

    P.S Using CreateProcess without both an application path and command line can lead to serious security holes.

    P.P.S Is the the open source web server you are working on? Looks intersting.

  3. #3
    Banned nickname_changed's Avatar
    Join Date
    Feb 2003
    Location
    Australia
    Posts
    986
    Thanks anontymouse, but it still doesn't seem to be working. I added a handle for the WM_QUIT message in the application, and I change the serviceControl function to look like this:

    Code:
    //---------------------------------------------------------------------------------------------
    //			Control Handler
    //---------------------------------------------------------------------------------------------
    void ControlHandler(DWORD request) 
    { 
    	switch(request) 
    	{ 
    	case SERVICE_CONTROL_STOP: 
    		SERVER_STOP = true;
        
            PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0);
            CloseHandle( pi.hProcess );
            CloseHandle( pi.hThread );
    
            ServiceStatus.dwWin32ExitCode = 0; 
            ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
            SetServiceStatus (hStatus, &ServiceStatus);
            return; 
     
    	case SERVICE_CONTROL_SHUTDOWN: 
            SERVER_STOP = true;
    
            PostThreadMessage(pi.dwThreadId, WM_QUIT, 0, 0);
            CloseHandle( pi.hProcess );
            CloseHandle( pi.hThread );
    
            ServiceStatus.dwWin32ExitCode = 0; 
            ServiceStatus.dwCurrentState = SERVICE_STOPPED; 
            SetServiceStatus (hStatus, &ServiceStatus);
            return; 
            
    	default:
            break;
    	} 
     
        // Report current status
        SetServiceStatus (hStatus, &ServiceStatus);
        return; 
    }
    And made the loop:
    Code:
    SERVER_STOP = false;
        while (SERVER_STOP == false)
        {
            if (WaitForSingleObject(pi.hProcess, 1000))
                break;
        }
        GetExitCodeProcess(pi.hProcess, &ExitCode);
    
        // Close process and thread handles. 
        CloseHandle( pi.hProcess );
        CloseHandle( pi.hThread );
    But the application still does not close. What else can I try?

    Thanks for all your help so far anontymouse, its greatly appreciated!

    In regards to your first PS, I don't have them both NULL, the first is the application I wish to open. And for your second, yes this is the SWEBS Web Server. We are moving all the server code to an external application, so that it can use DLL's and Pipes easier (services have problems with pipes).

  4. #4
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    I could be barking up the wrong tree, but do you actually have a message loop in the launched application? Without a message loop your window procedure will never be called. If you don't need a window, get rid of it and the window procedure and use this:

    Code:
    MSG msg;
    
    // This will create a message queue for the thread...
    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
    
    // Check for a WM_QUIT in the message queue...
    while(!PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE)) {
    
            // do server stuff
    
    }
    // cleanup code
    With this method no window is required and the loop will break when there is a WM_QUIT message present in the queue. Call the first PeekMessage very early in the program.

    An alternative approach would be to use named events. These can be waited on which may be an advantage depending on the program.

    In regards to CreateProcess, both arguments should be non-null. Simply use an empty string("") as lpCommandLine if the program takes no arguments.

  5. #5
    Banned nickname_changed's Avatar
    Join Date
    Feb 2003
    Location
    Australia
    Posts
    986
    Hehe, that could be my problem. I create a window class, and tell it what function to use as the message loop, but since I dont have an actual window maybe the messages never get sent to it?

    Heres the bottom of my WinMain():
    Code:
        while (!SERVER_STOP)
    	{
            // Get message(s) if there is one
    		if(PeekMessage(&Message,Hwnd,0,0,PM_REMOVE))
    		{
    			if(Message.message == WM_QUIT)
    				break;	
    			TranslateMessage(&Message);
    			DispatchMessage(&Message);
    		}
    		else
    		{
    			// Handle some requests
    		    SFD_New = accept(SFD_Listen, (struct sockaddr *) &ClientAddress, &Size);
    		
    		    DWORD dwThreadId;														// Info for the thead 
    		    HANDLE hThread; 
    
    		    // Create a structure of type ARGUMENT to be passed to the new thread
    		    ARGUMENT Argument;
    		    Argument.CLA = ClientAddress;
    		    Argument.SFD = SFD_New;
    
    		    // CreateThread and process the request
    		    hThread = CreateThread( 
                    NULL,																// default security attributes 
                    0,                           										// use default stack size  
                    ProcessRequest,                 									// thread function 
                    &Argument,                											// argument to thread function 
                    0,                           										// use default creation flags 
                    &dwThreadId
                );                												        // returns the thread identifier 
    		
    		    if (hThread != NULL)												    // If the thread was created, destroy it
    		    {
    			    CloseHandle( hThread );
    		    }
            }
    
    	}
    If I move my WinProc functions into the PeekMessage part of that loop, should that work?

  6. #6
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    >If I move my WinProc functions into the PeekMessage part of that loop, should that work?

    Most likely. A thread message will not be dispatched to a window procedure anyway, even if that window happens to exist. :-)

    I assumed you were using a GetMessage loop which will 'automatically' exit(return 0) if the thread receives a WM_QUIT.

    EDIT: The second argument of PeekMessage must be null to retrieve thread messages, not a (non-existant) window handle.
    Last edited by anonytmouse; 09-24-2003 at 05:27 AM.

  7. #7
    Banned nickname_changed's Avatar
    Join Date
    Feb 2003
    Location
    Australia
    Posts
    986
    Well, heres my WinProc():
    Code:
    LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) 
    { 
    	switch(message) 
    	{ 
            case WM_DESTROY:
            case WM_QUIT:
            case WM_CLOSE:
    		    SERVER_STOP = true;
                PostQuitMessage(0);
                break;
    	    default:
                break;
    	}
        // Report current status
        return DefWindowProc(hwnd, message, wparam, lparam);
    }
    Maybe its having problems because I use Hwnd, when I have no window to be handled by Hwnd? I'll try it using the code you gave and I'll see what I can come up with.

  8. #8
    Banned nickname_changed's Avatar
    Join Date
    Feb 2003
    Location
    Australia
    Posts
    986
    Ok, thanks, heres what that loop looks like now:

    Code:
        while (!SERVER_STOP)
    	{
            PeekMessage(&Message, NULL, WM_USER, WM_USER, PM_NOREMOVE);
            // Get message(s) if there is one
    		if(PeekMessage(&Message, NULL, 0, 0, PM_REMOVE))
    		{
    			if(Message.message == WM_QUIT)
    				break;	
    			TranslateMessage(&Message);
    			DispatchMessage(&Message);
    		}
    		else
    		{
    			// Handle some requests
    		    SFD_New = accept(SFD_Listen, (struct sockaddr *) &ClientAddress, &Size);
    		
    		    DWORD dwThreadId;														// Info for the thead 
    		    HANDLE hThread; 
    
    		    // Create a structure of type ARGUMENT to be passed to the new thread
    		    ARGUMENT Argument;
    		    Argument.CLA = ClientAddress;
    		    Argument.SFD = SFD_New;
    
    		    // CreateThread and process the request
    		    hThread = CreateThread( 
                    NULL,																// default security attributes 
                    0,                           										// use default stack size  
                    ProcessRequest,                 									// thread function 
                    &Argument,                											// argument to thread function 
                    0,                           										// use default creation flags 
                    &dwThreadId
                );                												        // returns the thread identifier 
    		
    		    if (hThread != NULL)												    // If the thread was created, destroy it
    		    {
    			    CloseHandle( hThread );
    		    }
            }
    
    	}
    It still didn't shut the application down though...

  9. #9
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    It looks like that is a blocking accept() call in the loop so the program will not get the WM_QUIT message and exit until the accept() function returns (which is probably never if no one is connecting to the web server when you are trying to shut it down).

    Comment out the accept and see if it works.

    The first PeekMessage call should not be in the loop. It should be at the start of the program to set up the message queue. There is no need for Translate and DispatchMessage.

  10. #10
    Banned nickname_changed's Avatar
    Join Date
    Feb 2003
    Location
    Australia
    Posts
    986
    Hehe, yep you were right, thats exactly what it was... looks like im going to have to look up some non-blocking sockets now If your interested heres the code so far:
    Code:
    // This will create a message queue for the thread...
        PeekMessage(&Message, NULL, WM_USER, WM_USER, PM_NOREMOVE);
    
        // Check for a WM_QUIT in the message queue...
        while(!PeekMessage(&Message, NULL, WM_QUIT, WM_QUIT, PM_REMOVE))
        {
    		// Handle some requests
    	    //SFD_New = accept(SFD_Listen, (struct sockaddr *) &ClientAddress, &Size);
    		
    	    DWORD dwThreadId;														// Info for the thead 
    	    HANDLE hThread; 
    
    	    // Create a structure of type ARGUMENT to be passed to the new thread
    	    ARGUMENT Argument;
    	    Argument.CLA = ClientAddress;
    	    Argument.SFD = SFD_New;
    
    		    // CreateThread and process the request
    	    hThread = CreateThread( 
                   NULL,																// default security attributes 
                 0,                           										// use default stack size  
                    ProcessRequest,                 									// thread function 
                    &Argument,                											// argument to thread function 
                    0,                           										// use default creation flags 
                    &dwThreadId
            );                												        // returns the thread identifier 
    		
    	    if (hThread != NULL)												    // If the thread was created, destroy it
    	    {			    
                CloseHandle( hThread );
    	    }
    	}
    Thanks for all the help anontymouse, your an absolute legend!!

  11. #11
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    >looks like im going to have to look up some non-blocking sockets now

    In the meantime, you could simple connect() to the web server after you have posted the WM_QUIT. This would release the accept() call.

  12. #12
    Banned nickname_changed's Avatar
    Join Date
    Feb 2003
    Location
    Australia
    Posts
    986
    Ok... I have another idea. Is it possible before going into the loop to tell all thread messages to go to the winproc? Or, to another thread running at the same time? That way I can catch the message and do ExitProcess or another function to close the program. Non-blocking sockets don't look to be what I want right now... I need to use another thread or something.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Get Installed applications list and applications activity
    By arunarora in forum C++ Programming
    Replies: 5
    Last Post: 05-25-2009, 09:41 AM
  2. fscanf %s or %d integer input space char stop question...
    By transgalactic2 in forum C Programming
    Replies: 5
    Last Post: 04-14-2009, 10:44 AM
  3. Error stop Http Listener
    By George2 in forum C# Programming
    Replies: 1
    Last Post: 06-04-2008, 02:14 AM
  4. A question about windows programming
    By Hussain Hani in forum Windows Programming
    Replies: 16
    Last Post: 05-23-2007, 07:38 AM
  5. when a while loop will stop ?
    By blue_gene in forum C Programming
    Replies: 13
    Last Post: 04-20-2004, 03:45 PM