///////////////////////////////////////////////////////////////////////////////
//
//  File Name 
//      SERVICE.CPP
//
//  Description
//      This is the file where the entry point is (WinMain).
//      Here we also implement the registration and unregistration of
//      the RPC server.
//      The server register its services with NAMED PIPES as the exposed
//      protocol sequence for RPC calls. This server can only be run on
//      Windows NT platforms. Windows 95 does not support the creation of
//      named pipes.
//      This is where we register the NT service with the Service control
//      manager and where the service control handler is implemented
//
//      The WinMain, function can take certain paramenters from the command
//      line to control the action of the executable
//
//      Argument        Action
//      ========        ======
//      <None>          If normally started by the Service control manager,
//                      the WINDS NT Service starts and it is ready to accept
//                      request from WINDS MAPI service providers and from
//                      the WINDS administrator.
//                      If started as an application, a message dialog will
//                      come up to allow interactive install and removal of
//                      the service.
//
//      APP             Start the service as an APPLICATION EXECUTABLE, as
//                      opposed to an NT SERVICE. This is useful for
//                      debugging the WINDS server.
//
//      INSTALL         Adds the WINDS NT Service to the Service Control
//                      Manager list, modify the registry entries and starts
//                      the NT services normally.
//
//      REMOVE          Remove the WINDS NT service from the Service
//                      control list, and stops the WINDS service, halting
//                      services WINDS MAPI providers and WINDS administrators
//
//  Author
//      Irving De la Cruz
//
//  Revision: 1.7
//
// Written for Microsoft Windows Developer Support
// Copyright (c) 1995-1996 Microsoft Corporation. All rights reserved.
//
#include "_WINDS.H"
#include <WINDOWSX.H>
#include "WRAP3D.H"
#include <RPC.H>
#include "WINDS.H"     // Header file generated by the MIDL compiler
#include "WDSADM.H"    // Header file generated by the MIDL compiler
#include "COMMON.H"

extern "C"
{
    BOOL WINAPI InitRPCServer
                        ();
    BOOL WINAPI TerminateAllRPCServer
                        ();
    void WINAPI RegisterWindsPrivateClass
                        ();
    void WindsServiceMain
                        (DWORD                      dwArgc,
                         LPTSTR *                   pszArgv);
    void WindsServiceCtrlHandler
                        (DWORD                      dwControlCode);
    void WINAPI TerminateService
                        (HRESULT                    hErrror);
    HRESULT WINAPI SendStatusToSCM
                        (DWORD                      dwServiceState,
                         HRESULT                    hError,
                         DWORD                      dwCheckPoint,
                         DWORD                      dwWait);
    HRESULT WINAPI InstallWindsService
                        (DWORD                      dwStartType,
                         LPTSTR                     szFullPathToServiceExe,
                         BOOL                       fStartImmediately);
    HRESULT WINAPI RemoveWindsService
                        ();
    HRESULT WINAPI InitWindsService
                        ();
    void WINAPI PauseWindsService
                        ();
    void WINAPI ResumeWindsService
                        ();
    void WINAPI StopWindsService
                        ();
    BOOL CALLBACK GenericDlgProc
                        (HWND                       hDlg, 
                         UINT                       message, 
                         WPARAM                     wParam, 
                         LPARAM                     lParam);
    BOOL CALLBACK InstallDlgProc
                        (HWND                       hDlg, 
                         UINT                       message, 
                         WPARAM                     wParam, 
                         LPARAM                     lParam);
    LRESULT CALLBACK PrivateDlgClassProc
                        (HWND                       hDlg, 
                         UINT                       message, 
                         WPARAM                     wParam, 
                         LPARAM                     lParam);

    TCHAR g_szAppTitle[] = TEXT("WINDS Messaging Host");
    HINSTANCE ghInstance = NULL;
    HANDLE ghHeap = NULL;
    IOTHREADINFO     g_IOInfo = { 0 };
    TCHAR            g_szDataFile[MAX_PATH] = { 0 };
    HRESULT          g_hServerState = HRESULT_FROM_WIN32(ERROR_BUSY);
    CRITICAL_SECTION g_csIOInfo;
    CRITICAL_SECTION g_csNotifLinks;
    CRITICAL_SECTION g_csServiceState;
    CRITICAL_SECTION g_csPipeID;
    DWORD g_dwNextPipeID = MIN_PIPE_NUMBER;

    SERVICE_STATUS_HANDLE   g_ssHandle = NULL;
    HANDLE                  g_hTerminateEvent = NULL;
    LPVOID                  g_pctl3d = NULL;
};

CObjectList GlobalObjectMap;

#define WINDS_SERVICE_NAME          TEXT("WINDS Messaging Host")
#define WINDS_SERVICE_NAME_SHORT    TEXT("WindsMessagingHost")
#define WINDS_SERVICE_EXECUTABLE    TEXT("\\WINDSSRV.EXE")

///////////////////////////////////////////////////////////////////////////////
//    WindsServiceMain()
//
//    Parameters
//      { Refer to Win32 API documentation on ServiceMain }
//
//    Purpose
//      
//    Return Value
//      None
//      
void WindsServiceMain (DWORD dwArgc, LPTSTR * pszArgv)
{
    g_ssHandle = RegisterServiceCtrlHandler (WINDS_SERVICE_NAME,
                                             (LPHANDLER_FUNCTION)WindsServiceCtrlHandler);
    if (NULL == g_ssHandle)
    {
        TraceResult ("WindsServiceMain: Failed to register the control handle", HRESULT_FROM_WIN32(GetLastError()));
        return;
    }
    HRESULT hResult = SendStatusToSCM (SERVICE_START_PENDING, S_OK, 1, 5);
    if (hResult)
    {
        TerminateService (hResult);
        return;
    }
    g_hTerminateEvent = CreateEvent (0, TRUE, FALSE, 0);
    if (NULL == g_hTerminateEvent)
    {
        hResult = HRESULT_FROM_WIN32(GetLastError());
        TraceResult ("WindsServiceMain: Failed to create terminate event", hResult);
        TerminateService (hResult);
        return;
    }

    hResult = SendStatusToSCM (SERVICE_START_PENDING, S_OK, 2, 1);
    if (!hResult)
    {
        hResult = InitWindsService();
        if (!hResult)
        {
            hResult = SendStatusToSCM (SERVICE_RUNNING, S_OK, 0, 0);
            // If an error occured, terminate the service
            if (hResult)
            {
                StopWindsService();
            }
        }
    }
    if (S_OK == hResult)
    {
        WaitForSingleObject (g_hTerminateEvent, INFINITE);
    }
    TerminateService (hResult);

}

///////////////////////////////////////////////////////////////////////////////
//    TerminateService()
//
//    Parameters
//
//    Purpose
//      
//    Return Value
//      None
//      
void WINAPI TerminateService (HRESULT hError)
{
    if (g_hTerminateEvent)
    {
        CloseHandle (g_hTerminateEvent);
    }
    if (g_ssHandle)
    {
        SendStatusToSCM (SERVICE_STOPPED, hError, 0, 0);
    }
}

///////////////////////////////////////////////////////////////////////////////
//    SendStatusToSCM()
//
//    Parameters
//
//    Purpose
//      
//    Return Value
//      None
//      
HRESULT WINAPI SendStatusToSCM (DWORD   dwServiceState,
                                HRESULT hError,
                                DWORD   dwCheckPoint,
                                DWORD   dwWait)
{
    SERVICE_STATUS ssStatus = { 0 };
    ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ssStatus.dwCurrentState = dwServiceState;
    if (SERVICE_START_PENDING == dwServiceState)
    {
        ssStatus.dwControlsAccepted = 0;
    }
    else
    {
        ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
                                      SERVICE_ACCEPT_PAUSE_CONTINUE |
                                      SERVICE_ACCEPT_SHUTDOWN;
    }
    if (hError)
    {
        ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
        ssStatus.dwServiceSpecificExitCode = (DWORD)hError;
    }
    ssStatus.dwCheckPoint = dwCheckPoint;
    ssStatus.dwWaitHint = (dwWait * 1000);
    
    HRESULT hResult = S_OK;
    if (!SetServiceStatus (g_ssHandle, &ssStatus))
    {
        hResult = HRESULT_FROM_WIN32(GetLastError());
        TraceResult ("SendStatusToSCM: Failed to set service status", hResult);
    }
    return hResult;
}

///////////////////////////////////////////////////////////////////////////////
//    WindsServiceCtrlHandler()
//
//    Parameters
//
//    Purpose
//      
//    Return Value
//      None
//      
void WindsServiceCtrlHandler (DWORD dwControlCode)
{
    switch (dwControlCode)
    {
        case SERVICE_CONTROL_SHUTDOWN :
        case SERVICE_CONTROL_STOP :
            SendStatusToSCM (SERVICE_STOP_PENDING, S_OK, 1, 10);
            StopWindsService();
            SendStatusToSCM (SERVICE_STOPPED, S_OK, 0, 0);
            break;

        case SERVICE_CONTROL_PAUSE :
            SendStatusToSCM (SERVICE_PAUSE_PENDING, S_OK, 1, 1);
            PauseWindsService();
            SendStatusToSCM (SERVICE_PAUSED, S_OK, 0, 0);
            break;

        case SERVICE_CONTROL_CONTINUE :
            SendStatusToSCM (SERVICE_CONTINUE_PENDING, S_OK, 1, 1);
            ResumeWindsService();
            SendStatusToSCM (SERVICE_RUNNING, S_OK, 0, 0);
            break;

        case SERVICE_CONTROL_INTERROGATE :
            SendStatusToSCM (0, S_OK, 0, 0);
            break;
    }
}

///////////////////////////////////////////////////////////////////////////////
//    InitWindsService()
//
//    Parameters
//
//    Purpose
//      
//    Return Value
//      
HRESULT WINAPI InitWindsService()
{
    HRESULT hResult = S_OK;
    LoadSettingsFromRegistry();
    SaveSettingsToRegistry();

    InitializeCriticalSection (&g_csIOInfo);
    InitializeCriticalSection (&g_csPipeID);
    InitializeCriticalSection (&g_csNotifLinks);
    InitializeCriticalSection (&g_csServiceState);

    g_IOInfo.hResumeEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (!g_IOInfo.hResumeEvent)
    {
        hResult = HRESULT_FROM_WIN32(GetLastError());
        TraceResult ("InitWindsService: Failed to create sync object (EVENT)", hResult);
        return FALSE;
    }

    DWORD dwThreadID;
    HANDLE hThread = CreateThread (NULL,
                                   0,
                                   (LPTHREAD_START_ROUTINE)WorkingIOProc,
                                   NULL,
                                   0,
                                   &dwThreadID);
    if (!hThread)
    {
        hResult = HRESULT_FROM_WIN32(GetLastError());
        TraceResult ("InitWindsService: Failed to create I/O thread", hResult);
        ReleaseSyncObj();
        return hResult;
    }
    TraceDebugger ("InitWindsService: IO thread spawned. ID: %X", dwThreadID);
    SetThreadPriority (hThread, THREAD_PRIORITY_HIGHEST);
    CloseHandle (hThread);
    
    HANDLE hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);

    if (InitRPCServer())
    {
        EnterCriticalSection (&g_csIOInfo);
        SetEvent (g_IOInfo.hResumeEvent);
        g_IOInfo.Action           = IO_OPEN_STORAGE_FILE;
        g_IOInfo.hActionCompleted = hWaitEvent;
        g_IOInfo.phLastError      = &hResult;
        LeaveCriticalSection (&g_csIOInfo);
        WaitForSingleObject (hWaitEvent, INFINITE);
    }
    else
    {
        TraceMessage ("InitWindsService: Failed to initialize the RPC server");
        hResult = E_FAIL;
    }
    CloseHandle (hWaitEvent);

    EnterCriticalSection (&g_csIOInfo);
    SetEvent (g_IOInfo.hResumeEvent);
    g_IOInfo.Action            = IO_LOAD_MAILBOX_NAMES;
    g_IOInfo.hActionCompleted  = NULL;
    g_IOInfo.phLastError       = NULL;
    LeaveCriticalSection (&g_csIOInfo);

    return hResult;
}

///////////////////////////////////////////////////////////////////////////////
//    PauseWindsService()
//
//    Parameters
//
//    Purpose
//      
//    Return Value
//      
void WINAPI PauseWindsService()
{
    EnterCriticalSection (&g_csServiceState);
    g_hServerState = HRESULT_FROM_WIN32(ERROR_SERVICE_CANNOT_ACCEPT_CTRL);
    LeaveCriticalSection (&g_csServiceState);
}

///////////////////////////////////////////////////////////////////////////////
//    ResumeWindsService()
//
//    Parameters
//
//    Purpose
//      
//    Return Value
//      
void WINAPI ResumeWindsService()
{
    EnterCriticalSection (&g_csServiceState);
    g_hServerState = S_OK;
    LeaveCriticalSection (&g_csServiceState);
}

///////////////////////////////////////////////////////////////////////////////
//    StopWindsService()
//
//    Parameters
//
//    Purpose
//      
//    Return Value
//      
void WINAPI StopWindsService()
{
    HANDLE hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);

    PWINDS_NOTIFICATION pNotif = (PWINDS_NOTIFICATION)HeapAlloc (ghHeap,
                                                                 HEAP_ZERO_MEMORY,
                                                                 sizeof(WINDS_NOTIFICATION));
    if (pNotif)
    {
        pNotif->Event = SERVER_IS_SHUTTING_DOWN;
        EnterCriticalSection (&g_csIOInfo);
        SetEvent (g_IOInfo.hResumeEvent);
        g_IOInfo.Action           = IO_SENT_SHUTDOWN_NOTIFICATION;
        g_IOInfo.hActionCompleted = hWaitEvent;
        g_IOInfo.pNotif           = pNotif;
        g_IOInfo.phLastError      = NULL;
        LeaveCriticalSection (&g_csIOInfo);
        WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
    }
    else
    {
        TraceMessage ("StopWindsService: Failed to create structure for shutdown notification");
    }
    TerminateAllRPCServer();
    RemoveAllNotifLinks();
    
    EnterCriticalSection (&g_csIOInfo);
    // Resume the thread so that it terminate ifself properly
    SetEvent (g_IOInfo.hResumeEvent);
    g_IOInfo.fAppIsTerminating = TRUE;
    g_IOInfo.hActionCompleted  = hWaitEvent;
    LeaveCriticalSection (&g_csIOInfo);
    WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
    
    CloseHandle (hWaitEvent);
    
    UnInitTraces ();
    
    ReleaseSyncObj();
}

///////////////////////////////////////////////////////////////////////////////
//    InstallWindsService()
//
//    Parameters
//
//    Purpose
//      
//    Return Value
//      
HRESULT WINAPI InstallWindsService (DWORD   dwStartType,
                                    LPTSTR  szFullPathToServiceExe,
                                    BOOL    fStartImmediately)
{
    HRESULT hResult = S_OK;
    SC_HANDLE schSCM, schWINDS;
    schSCM = OpenSCManager (0, 0, SC_MANAGER_CREATE_SERVICE);
    if (NULL == schSCM)
    {
        hResult = HRESULT_FROM_WIN32 (GetLastError());
    }
    else
    {
RestartInstall:
        schWINDS = CreateService (schSCM,
                                  WINDS_SERVICE_NAME_SHORT,
                                  WINDS_SERVICE_NAME,
                                  SERVICE_ALL_ACCESS,
                                  SERVICE_WIN32_OWN_PROCESS,
                                  dwStartType,
                                  SERVICE_ERROR_NORMAL,
                                  szFullPathToServiceExe,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL);
        if (NULL == schWINDS)
        {
            hResult = HRESULT_FROM_WIN32 (GetLastError());
        }
        else
        {
            if (fStartImmediately)
            {
                StartService (schWINDS, 0, NULL);
            }
            CloseServiceHandle (schWINDS);
        }
        if (HRESULT_FROM_WIN32 (ERROR_SERVICE_EXISTS) == hResult)
        {
            RemoveWindsService();
            goto RestartInstall;
        }
        CloseServiceHandle (schSCM);
    }
    TraceResult ("InstallWindsService", hResult);
    return hResult;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoveWindsService()
//
//    Parameters
//
//    Purpose
//      
//    Return Value
//      
HRESULT WINAPI RemoveWindsService()
{
    HRESULT hResult = S_OK;
    SC_HANDLE schSCM, schWINDS;
    schSCM = OpenSCManager (0, 0, SC_MANAGER_CREATE_SERVICE);
    if (NULL == schSCM)
    {
        hResult = HRESULT_FROM_WIN32 (GetLastError());
    }
    else
    {
        schWINDS = OpenService (schSCM,
                                WINDS_SERVICE_NAME_SHORT,
                                SERVICE_ALL_ACCESS);
        if (NULL == schWINDS)
        {
            hResult = HRESULT_FROM_WIN32 (GetLastError());
        }
        else
        {
            SERVICE_STATUS ssState;
            ControlService (schWINDS, SERVICE_CONTROL_STOP, &ssState);
            DeleteService (schWINDS);
            CloseServiceHandle (schWINDS);
        }
        CloseServiceHandle (schSCM);
    }
    TraceResult ("RemoveWindsService", hResult);
    return hResult;
}

///////////////////////////////////////////////////////////////////////////////
//    WinMain()
//
//    Parameters
//      { Refer to Win32 SDK documentation }
//      
//    Purpose
//      This is the entry function. The application will start executing here.
//      
//    Return Value
//      Return code of the application to the system.
//      
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pszCmdLine, int nCmdShow)
{
    InitTraces (TRACES_NO_ASSERT_DIALOG | TRACES_NO_COM_OUTPUT);
    //InitTraces (TRACES_NO_ASSERT_DIALOG);
    ghInstance = hInstance;
    ghHeap = GetProcessHeap();

    if (0 == lstrcmpi ("APP", pszCmdLine))
    {
        // Check for other instances of this application running. If one is found,
        // bring it to the foreground
        HWND hOtherInstance = FindWindow (MAINDLGCLASS, NULL);
        if (NULL != hOtherInstance)
        {
            HWND hChildWnd = GetLastActivePopup (hOtherInstance);
            if (hChildWnd != hOtherInstance)
            {
                BringWindowToTop (hOtherInstance);
            }
            else
            {
                SetForegroundWindow (hOtherInstance);
            }
            return 1;
        }

        RegisterWindsPrivateClass();

        if (S_OK != InitWindsService())
        {
            return -1;
        }
    
        DialogBoxParam (ghInstance, MAKEINTRESOURCE(IDD_WINDS_APP), NULL, GenericDlgProc, (LPARAM)TRUE);
    
        StopWindsService();

        return 0;
    }

    if (0 == lstrcmpi ("INSTALL", pszCmdLine))
    {
        TCHAR szFullPath[_MAX_PATH] = { 0 };
        GetCurrentDirectory (_MAX_PATH, szFullPath);
        lstrcat (szFullPath, WINDS_SERVICE_EXECUTABLE);
        if (S_OK != InstallWindsService (SERVICE_AUTO_START, szFullPath, TRUE))
        {
            return -1;
        }
        return 0;
    }

    if (0 == lstrcmpi ("REMOVE", pszCmdLine))
    {
        if (S_OK != RemoveWindsService())
        {
            return -1;
        }
        return 0;
    }

    SERVICE_TABLE_ENTRY ServiceTable[] =
    {
        { WINDS_SERVICE_NAME,
          (LPSERVICE_MAIN_FUNCTION)WindsServiceMain,
        },
        { NULL, NULL }
    };

    if (FALSE == StartServiceCtrlDispatcher (ServiceTable))
    {
        if (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT == GetLastError())
        {
            RegisterWindsPrivateClass();
            DialogBoxParam (ghInstance, MAKEINTRESOURCE(IDD_WINDS_DIALOG), NULL, GenericDlgProc, (LPARAM)FALSE);
        }
    }
    return 0;
}

///////////////////////////////////////////////////////////////////////////////
//    InitRPCServer()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
BOOL WINAPI InitRPCServer()
{
    #define MAX_NUM_OF_CALLS    65536
    RPC_STATUS status = RpcServerUseProtseqEp ((WINDS_RPC_STRING)WINDS_RPC_PROTOCOL,
                                               MAX_NUM_OF_CALLS,
                                               (WINDS_RPC_STRING)WINDS_PROVIDERS_RPC_ENDPOINT,
                                               NULL);  // Security descriptor
    if (status)
    {
        TraceMessage ("InitRPCServer: RpcServerUseProtseqEp failed (PROVIDER)");
        goto ErrorExit;
    }

    status = RpcServerRegisterIf (WINDSREM_v1_6_s_ifspec,   // Interface to register
                                  NULL,                     // MgrTypeUuid
                                  NULL);                    // MgrEpv; null means use default
    if (status)
    {
        TraceMessage ("InitRPCServer: RpcServerRegisterIf failed (PROVIDER)");
        goto ErrorExit;
    }

    status = RpcServerUseProtseqEp ((WINDS_RPC_STRING)WINDS_RPC_PROTOCOL,
                                    MAX_NUM_OF_CALLS,
                                    (WINDS_RPC_STRING)WINDS_ADMIN_RPC_ENDPOINT,
                                    NULL);  // Security descriptor
    if (status)
    {
        TraceMessage ("InitRPCServer: RpcServerUseProtseqEp failed (ADMIN)");
        goto ErrorExit;
    }
    
    status = RpcServerRegisterIf (WINDSADM_v1_0_s_ifspec,   // Interface to register
                                  NULL,                     // MgrTypeUuid
                                  NULL);                    // MgrEpv; null means use default
    if (status)
    {
        TraceMessage ("InitRPCServer: RpcServerRegisterIf failed (ADMIN)");
        goto ErrorExit;
    }

    status = RpcServerListen (1, MAX_NUM_OF_CALLS, TRUE);
    if (status)
    {
        TraceMessage ("InitRPCServer: RpcServerListen failed");
        goto ErrorExit;
    }
ErrorExit:
    if (status)
    {
        TraceResult ("InitRPCServer", MAKE_HRESULT(1, FACILITY_RPC, status));
        return FALSE;
    }
    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
//    TerminateAllRPCServer()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
BOOL WINAPI TerminateAllRPCServer()
{
    RPC_STATUS status = RpcMgmtStopServerListening (NULL);
    if (status)
    {
        TraceMessage ("TerminateRPCServer: RpcMgmtStopServerListening failed");
    }
    else
    {
        status = RpcServerUnregisterIf (NULL, NULL, FALSE);
        if (status)
        {
            TraceMessage ("TerminateRPCServer: RpcServerUnregisterIf failed");
        }
    }
    if (status)
    {
        TraceResult ("TerminateRPCServer", MAKE_HRESULT(1, FACILITY_RPC, status));
        return FALSE;
    }
    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
//    GetServiceState()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
HRESULT WINAPI GetServiceState()
{
    HRESULT hServiceState;
    EnterCriticalSection (&g_csServiceState);
    hServiceState = g_hServerState;
    LeaveCriticalSection (&g_csServiceState);
    return hServiceState;
}

///////////////////////////////////////////////////////////////////////////////
//    PrivateDlgClassProc()
//
//    Parameters
//      { Refer to Win32 API documentation on dialog procedures }
//
//    Purpose
//      
//    Return Value
//      
LRESULT CALLBACK PrivateDlgClassProc (HWND    hDlg, 
                                      UINT    message, 
                                      WPARAM  wParam, 
                                      LPARAM  lParam)
{
    return DefDlgProc (hDlg, message, wParam, lParam);
}

///////////////////////////////////////////////////////////////////////////////
//    RegisterWindsPrivateClass()
//
//    Parameters
//
//    Purpose
//      
//    Return Value
//      
void WINAPI RegisterWindsPrivateClass()
{
    WNDCLASS wc = { 0 };
    // Register the class for this application
    wc.hInstance        = ghInstance;
    wc.hCursor          = LoadCursor (NULL, IDC_ARROW);
    wc.lpfnWndProc      = PrivateDlgClassProc;
    wc.cbWndExtra       = DLGWINDOWEXTRA;
    wc.hIcon            = LoadIcon (ghInstance, MAKEINTRESOURCE (IDI_WINDSSERVICE));
    wc.lpszClassName    = MAINDLGCLASS;
    if (!RegisterClass (&wc))
    {
        TraceResult ("RegisterWindsPrivateClass: Failed to register the main class", HRESULT_FROM_WIN32(GetLastError()));
    }
}

///////////////////////////////////////////////////////////////////////////////
//    GenericDlgProc()
//
//    Parameters
//      { Refer to Win32 API documentation on dialog procedures }
//
//    Purpose
//      
//    Return Value
//      TRUE if message was handled, FALSE if we don't handle the message
//      
BOOL CALLBACK GenericDlgProc (HWND    hDlg, 
                              UINT    message, 
                              WPARAM  wParam, 
                              LPARAM  lParam)
{
    HRESULT hResult;
    switch (message)
    {
        case WM_INITDIALOG :
            if (TRUE == lParam)
            {
                ShowWindow (hDlg, SW_MINIMIZE);
            }
            else
            {
                if (!g_pctl3d)
                {
                    BOOL f;
                    g_pctl3d = CTL3D_Initialize(ghInstance);
                    CTL3D_AutoSubclass(g_pctl3d, ghInstance, &f);
                }
                CenterDialogBox (hDlg);
            }
            return TRUE;

        case WM_COMMAND :
            switch (LOWORD(wParam))
            {
                case IDOK :
                case IDCANCEL :
                    PostMessage (hDlg, WM_SYSCOMMAND, SC_CLOSE, 0);
                    return TRUE;

                case IDC_INSTALL_SERVICE :
                    DialogBox (ghInstance, MAKEINTRESOURCE(IDD_INSTALL_OPTIONS), hDlg, InstallDlgProc);
                    break;

                case IDC_REMOVE_SERVICE :
                    hResult = RemoveWindsService();
                    if (S_OK == hResult)
                    {
                        MessageBox (hDlg,
                                    TEXT("Service successfully removed"),
                                    TEXT("WINDS Services - Remove"),
                                    MB_OK | MB_ICONEXCLAMATION);
                    }
                    else
                    {
                        MessageBox (hDlg,
                                    TEXT("Failed to remove the service"),
                                    TEXT("WINDS Services - Remove"),
                                    MB_OK | MB_ICONSTOP);
                    }
                    break;
            }
            break;

        case WM_SYSCOMMAND:
            if (wParam == SC_CLOSE)
            {
                CTL3D_Uninitialize(g_pctl3d);
                g_pctl3d = NULL;
                EndDialog (hDlg, TRUE);
                return TRUE;
            }
            break;
    }
    return FALSE;
}

///////////////////////////////////////////////////////////////////////////////
//    InstallDlgProc()
//
//    Parameters
//      { Refer to Win32 API documentation on dialog procedures }
//
//    Purpose
//      
//    Return Value
//      TRUE if message was handled, FALSE if we don't handle the message
//      
BOOL CALLBACK InstallDlgProc (HWND    hDlg, 
                              UINT    message, 
                              WPARAM  wParam, 
                              LPARAM  lParam)
{
    TCHAR szFullPath[_MAX_PATH];
    DWORD dwStart;
    BOOL fStartNow;
    switch (message)
    {
        case WM_INITDIALOG :
            CenterDialogBox (hDlg);
            Edit_LimitText (GetDlgItem (hDlg, IDC_SERVICE_FILEPATH), _MAX_PATH - 1);
            GetCurrentDirectory (_MAX_PATH, szFullPath);
            lstrcat (szFullPath, WINDS_SERVICE_EXECUTABLE);
            SetWindowText (GetDlgItem (hDlg, IDC_SERVICE_FILEPATH), szFullPath);
            Button_SetCheck (GetDlgItem (hDlg, IDC_AUTO_START), BST_CHECKED);
            Button_SetCheck (GetDlgItem (hDlg, IDC_START_NOW), BST_CHECKED);
            return TRUE;

        case WM_COMMAND :
            switch (LOWORD(wParam))
            {
                case IDOK :
                    GetWindowText (GetDlgItem (hDlg, IDC_SERVICE_FILEPATH), szFullPath, _MAX_PATH);
                    if (BST_CHECKED == Button_GetCheck (GetDlgItem (hDlg, IDC_AUTO_START)))
                    {
                        dwStart = SERVICE_AUTO_START;
                    }
                    else
                    {
                        dwStart = SERVICE_DEMAND_START;
                    }
                    if (BST_CHECKED == Button_GetCheck (GetDlgItem (hDlg, IDC_START_NOW)))
                    {
                        fStartNow = TRUE;
                    }
                    else
                    {
                        fStartNow = FALSE;
                    }
                    if (lstrlen (szFullPath) && S_OK == InstallWindsService (dwStart, szFullPath, fStartNow))
                    {
                        MessageBox (hDlg,
                                    TEXT("Service successfully installed"),
                                    TEXT("WINDS Services - Install"),
                                    MB_OK | MB_ICONEXCLAMATION);
                    }
                    else
                    {
                        MessageBox (hDlg,
                                    TEXT("Failed to install the service"),
                                    TEXT("WINDS Services - Install"),
                                    MB_OK | MB_ICONSTOP);
                    }
                    // Fall through
                case IDCANCEL :
                    EndDialog (hDlg, TRUE);
                    return TRUE;
            }
    }
    return FALSE;
}

// End of file for SERVICE.CPP
