SCCSwitcher.cpp #2

  • //
  • guest/
  • soenke_schau/
  • SCCSwitcher/
  • SCCSwitcher.cpp
  • View
  • Commits
  • Open Download .zip Download (21 KB)
// SCCSwitcher.cpp : 
// Created by Snke Schau, Germany [email protected]
#include "stdafx.h"
#include "resource.h"

#include "pubscc.h"

#define MAX_LOADSTRING          100

#define LEFTBUTTONMENU_ID_BASE  2000
#define MAX_SCCENTRIES          100

#define LEFTBUTTONMENU_ID_BASE_MAX      (LEFTBUTTONMENU_ID_BASE+MAX_SCCENTRIES)
#define RIGHTBUTTONMENU_ID_BASE         (LEFTBUTTONMENU_ID_BASE_MAX)
#define RIGHTBUTTONMENU_ID_BASE_MAX     (RIGHTBUTTONMENU_ID_BASE+MAX_SCCENTRIES)

// Global Variables:
HINSTANCE   hInst;                                  // current instance

TCHAR       aTitle[ MAX_LOADSTRING ];               // The title bar text
TCHAR       aStartSCCPrefix[ MAX_LOADSTRING ];      // Prefix for Text in "Start ..."
TCHAR       aDisableSCCMenutext[ MAX_LOADSTRING ];  // Menuentry "Disable SCC"
TCHAR       aPathToExecutable[ MAX_PATH ];          // The path to this executable

// Foward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
BOOL				ExitInstance(HINSTANCE);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK	About(HWND, UINT, WPARAM, LPARAM);

// Get an Errormessage from the resource, optional add system errorcode to message
void ErrorMessageFromResource( UINT Resource, bool AddError=false )
{
    TCHAR aCaption[ 1000 ];
    TCHAR aMessage[ 4000 ];

    // Get the strings
	LoadString( hInst, IDS_ERROR_CAPTION,   aCaption, sizeof aCaption / sizeof TCHAR );
	LoadString( hInst, Resource,            aMessage, ( sizeof aMessage / sizeof TCHAR ) / 2 );

    if ( AddError )
    {
        TCHAR    *pBuffer;

        // Get an appropriate message from sysstem
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
            NULL,
            GetLastError(),
            MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
            (TCHAR*)&pBuffer,
            sizeof( FORMAT_MESSAGE_MAX_WIDTH_MASK ),
            NULL );

        // Add to retrieved messages
        if ( pBuffer )
        {
            _tcscat( aMessage, _T("\r\n") );
            _tcscat( aMessage, pBuffer );
            LocalFree( pBuffer );
        }
    }

    // Display Box
    MessageBox( NULL, aMessage, aCaption, MB_OK|MB_ICONERROR|MB_SETFOREGROUND );
}

// Internal message used when something happens for us in the taskbar
const UINT  wm_iconmessage    = RegisterWindowMessage( _T("VCSwitcher_Iconmessage") );
// Message send when explorer restarts
const UINT  wm_TaskbarRestart = RegisterWindowMessage( _T("TaskbarCreated") );

HWND        hTrayWnd;
HICON       MyIcon;
HMENU       MainMenu;
HMENU       LeftButtonSubMenu;
HMENU       RightButtonSubMenu;
HKEY        ProviderKey;
UINT        CurrentChecked;
HKEY        WindowsRunKey = 0;
BOOL        Autostart = FALSE;

// Datastructure to store information about scc providers
struct ProviderDefinition
{
    TCHAR   aProviderName[ 1000 ];
    TCHAR   aProviderRegistryPath[ 1000 ];
    TCHAR   aProviderDll[ 1000 ];
    HICON   ProviderIcon;
};
// a max of 20 should be enough
ProviderDefinition *ProviderDefinitions;

// Adds an icon to the taskbar
void addTaskbarIcon()
{
    // Add Icon to tray
    NOTIFYICONDATA NIData;
    NIData.cbSize           = sizeof NOTIFYICONDATA;
    NIData.hWnd             = hTrayWnd;
    NIData.uID              = 4711;
    NIData.uFlags           = NIF_MESSAGE | NIF_ICON | NIF_TIP;
    NIData.hIcon            = ProviderDefinitions[ CurrentChecked ].ProviderIcon;
    NIData.uCallbackMessage = wm_iconmessage;
    _tcscpy(NIData.szTip, ProviderDefinitions[ CurrentChecked ].aProviderName );

    Shell_NotifyIcon( NIM_ADD, &NIData );
}

// Main entrypoint for pgm
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	MSG         msg;
	HACCEL      hAccelTable;
    TCHAR       aWindowClass[ 1000 ];								// The title bar text
    DWORD       ValueType;

    // Get Path to this executable
    GetModuleFileName( hInstance, aPathToExecutable, sizeof aPathToExecutable / sizeof TCHAR );

    // Load classname of our window
	LoadString(hInstance, IDC_SCCSWITCHER, aWindowClass, sizeof aWindowClass / sizeof TCHAR );

    // Load prefix for Menuintems
	LoadString( hInst, IDS_STARTSCC_PREFIX, aStartSCCPrefix, sizeof aStartSCCPrefix / sizeof TCHAR );

    // Load Menutext to disable SCC Integration
	LoadString( hInst, IDS_DISABLE_INTEGRATION, aDisableSCCMenutext, sizeof aDisableSCCMenutext / sizeof TCHAR );

	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon( hInstance, (LPCTSTR)IDI_SCCSWITCHER);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= aWindowClass;
	wcex.hIconSm		= LoadIcon( wcex.hInstance, (LPCTSTR)IDI_SMALL);

    // Register classname
	RegisterClassEx( &wcex );

    // Store instance handle in our global variable
    hInst = hInstance;
    
    // Create hidden window to process notification messages
    hTrayWnd = CreateWindow( aWindowClass, _T(""), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL );
    if (!hTrayWnd)
        return ErrorMessageFromResource( IDS_ERROR_CANNOT_ACCESS_REGISTRY, 1 ), 1;
    
    // Load the default icon
    MyIcon = LoadIcon( hInstance, MAKEINTRESOURCE(IDI_SMALL) );
    if ( !MyIcon )
        return ErrorMessageFromResource( IDS_ERROR_CANNOT_ACCESS_REGISTRY, 1 ), 1;

    // Prepare popup menu ...
    MainMenu = LoadMenu( hInst, MAKEINTRESOURCE(IDC_SCCSWITCHER));
    if ( !MainMenu )
        return ErrorMessageFromResource( IDS_ERROR_CANNOT_ACCESS_REGISTRY, 1 ), 1;

    LeftButtonSubMenu  = CreatePopupMenu();
    RightButtonSubMenu = GetSubMenu( MainMenu, 0 );

    if ( ERROR_SUCCESS == RegCreateKeyEx( HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Run"), 0, _T(""), 0, KEY_WRITE|KEY_READ, NULL, &WindowsRunKey, NULL ) )
    {
        TCHAR   aCurrentProgram[ 1000 ];
        DWORD   CurrentProgramLength = sizeof aCurrentProgram;
        if ( ERROR_SUCCESS == RegQueryValueEx( WindowsRunKey, _T("SCCSwitcher"), 0, &ValueType, reinterpret_cast<BYTE*>(aCurrentProgram), &CurrentProgramLength ) )
            Autostart = TRUE;

        CheckMenuItem( RightButtonSubMenu, IDM_AUTOSTART, MF_BYCOMMAND|(Autostart?MF_CHECKED:MF_UNCHECKED) );
    }
    else
        EnableMenuItem( RightButtonSubMenu, IDM_AUTOSTART, MF_BYCOMMAND|MF_GRAYED );
    
    // Open registy key, where current scc provider is specified
    if ( ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE, _T("Software\\SourceCodeControlProvider"), 0, KEY_WRITE|KEY_READ, &ProviderKey ) )
        return ErrorMessageFromResource( IDS_ERROR_CANNOT_ACCESS_REGISTRY, 1 ), 1;

    // Get current provider
    TCHAR   aCurrentProviderRegistryPath[ 1000 ];
    DWORD   CurrentProviderRegistryPathLength = sizeof aCurrentProviderRegistryPath;
    RegQueryValueEx( ProviderKey, _T("ProviderRegKey"), 0, &ValueType, reinterpret_cast<BYTE*>(aCurrentProviderRegistryPath), &CurrentProviderRegistryPathLength );

    // Open registy key, where all installed scc providers are listed
    HKEY InstalledProviderKey;
    if ( ERROR_SUCCESS != RegOpenKeyEx( ProviderKey, _T("InstalledSCCProviders"), 0, KEY_READ, &InstalledProviderKey ) )
        return ErrorMessageFromResource( IDS_ERROR_CANNOT_ACCESS_REGISTRY, 1 ), 1;

    // # of subkeys is the # of installed providers
    DWORD NumberOfProviders;
    if ( ERROR_SUCCESS != RegQueryInfoKey( InstalledProviderKey, NULL, NULL, NULL, NULL, NULL, NULL, &NumberOfProviders, NULL, NULL, NULL, NULL ) )
        return ErrorMessageFromResource( IDS_ERROR_CANNOT_ACCESS_REGISTRY, 1 ), 1;

    // allocate them
    ProviderDefinitions = new ProviderDefinition[ NumberOfProviders + 1 ];

    DWORD   ProviderNameLength = sizeof ProviderDefinitions[0].aProviderName / sizeof TCHAR; // Characters
    DWORD   ProviderRegistryPathLength = sizeof ProviderDefinitions[0].aProviderRegistryPath;                  // Bytesize
    // Startindex of entries in popupmenu
    UINT    LeftButtonMenuStartIndex  = 0;
    UINT    RightButtonMenuStartIndex = 7;

    MENUITEMINFO    mii;
    mii.cbSize  = sizeof MENUITEMINFO;

    // Iterate thru provider subkeys
    for( DWORD Index = 0 ; ; Index++ )
    {
        if ( !Index )
        {
            memset( &ProviderDefinitions[ Index ], 0, sizeof ProviderDefinitions[0] );
            ProviderDefinitions[ Index ].ProviderIcon = LoadIcon( hInstance, MAKEINTRESOURCE( IDI_DISABLED_SCC ) );
            _tcscpy( ProviderDefinitions[ Index ].aProviderName, aDisableSCCMenutext );
        }
        else
        {
            if ( ERROR_SUCCESS != RegEnumValue( InstalledProviderKey, Index - 1, ProviderDefinitions[Index].aProviderName, &ProviderNameLength, NULL, &ValueType, reinterpret_cast<BYTE*>(ProviderDefinitions[Index].aProviderRegistryPath), &ProviderRegistryPathLength ) )
                break;

            ProviderNameLength = sizeof ProviderDefinitions[0].aProviderName / sizeof TCHAR; // Characters
            ProviderRegistryPathLength = sizeof ProviderDefinitions[0].aProviderRegistryPath; // Bytesize

            // Default is to use a default icon
            ProviderDefinitions[Index].ProviderIcon = 0;

            // Open realprovider key
            HKEY RealProviderKey;
            if ( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, ProviderDefinitions[Index].aProviderRegistryPath, 0, KEY_READ, &RealProviderKey ) )
            {
                DWORD   ProviderDllLength = sizeof ProviderDefinitions[0].aProviderDll;
                if ( ERROR_SUCCESS != RegQueryValueEx( RealProviderKey, _T("SCCServerPath"), 0, &ValueType, reinterpret_cast<BYTE*>(ProviderDefinitions[Index].aProviderDll), &ProviderDllLength ) )
                    _tcscpy( ProviderDefinitions[Index].aProviderDll, _T("This shouln't be so! but who cares?") );

                // Check if an Entry "IconDll" is available. If so, the entry is treated as 
                // a Path to a Modulename  - Exe or Dll -.
                // If a trailing ";<number>" is specified, <number> is used as a Resource index into the
                // Module. If unspecified, 1 is used
                TCHAR   anIconDll[ 1000 ];
                DWORD   IconDllLength = sizeof anIconDll;
                if ( ERROR_SUCCESS == RegQueryValueEx( RealProviderKey, _T("IconDll"), 0, &ValueType, reinterpret_cast<BYTE*>(anIconDll), &IconDllLength ) )
                {
                    TCHAR   *pFilename = _tcstok(anIconDll,_T(",;"));
                    TCHAR   *pIndex = _tcstok(NULL,_T(",;"));
                    if (!pIndex)
                        pIndex = _T("1");

                    // Load module
                    HMODULE IconInstance = LoadLibraryEx( anIconDll, NULL, DONT_RESOLVE_DLL_REFERENCES );

                    // Load icon from module
                    HICON   anIcon = LoadIcon( IconInstance, MAKEINTRESOURCE(_ttoi(pIndex)) );
                    if ( anIcon )
                    {
                        // Assign it to provider info
                        ProviderDefinitions[ Index ].ProviderIcon = anIcon;
                        DestroyIcon( anIcon );
                    }
                    // time for library bye bye
                    FreeLibrary( IconInstance );
                }

                RegCloseKey( RealProviderKey );
            }
        }
        // If no icon available, provide default icon
        if ( !ProviderDefinitions[ Index ].ProviderIcon )
            ProviderDefinitions[ Index ].ProviderIcon = MyIcon;

        mii.fMask      = MIIM_ID|MIIM_DATA|MIIM_STATE|MIIM_TYPE;
        mii.fType      = MFT_STRING;
        mii.dwTypeData = ProviderDefinitions[ Index ].aProviderName;

        // Is this the current provider?
        if ( _tcscmp( aCurrentProviderRegistryPath, ProviderDefinitions[Index].aProviderRegistryPath ) )
            // No -> unchecked
            mii.fState = 0;
        else
        {
            // Yes -> check item
            mii.fState = MFS_CHECKED;
            CurrentChecked = Index;
        }

        // The Id is 2000 plus the current index
        mii.wID = Index + LEFTBUTTONMENU_ID_BASE;

        // Insert it!
        InsertMenuItem( RightButtonSubMenu, RightButtonMenuStartIndex++, TRUE, &mii );

        if ( Index )
        {
            mii.fMask      = MIIM_ID|MIIM_DATA|MIIM_TYPE;
            mii.fType      = MFT_STRING;

            TCHAR aBuffer[ 200 ];
            _stprintf( aBuffer, aStartSCCPrefix, ProviderDefinitions[ Index ].aProviderName );

            mii.dwTypeData = aBuffer;

            // The Id is RIGHTBUTTONMENU_ID_BASE plus the current index
            mii.wID = Index + RIGHTBUTTONMENU_ID_BASE;
            InsertMenuItem( LeftButtonSubMenu, LeftButtonMenuStartIndex++, TRUE, &mii );
        }
    }

    // Mark current as the default
    if ( CurrentChecked )
        SetMenuDefaultItem( LeftButtonSubMenu, CurrentChecked + RIGHTBUTTONMENU_ID_BASE, FALSE );

    // Close it
    RegCloseKey( InstalledProviderKey );

    // Add icon to taskbar
    addTaskbarIcon();

    // Load Accelerators
	hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_SCCSWITCHER);

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

    // Remove Icon from Tray
    NOTIFYICONDATA NIData;
    NIData.cbSize = sizeof NOTIFYICONDATA;
    NIData.hWnd = hTrayWnd;
    NIData.uID = 4711;

    Shell_NotifyIcon( NIM_DELETE, &NIData );

    // Cleanup
    RegCloseKey( ProviderKey );

    if ( WindowsRunKey )
        RegCloseKey( WindowsRunKey );
    
    delete [] ProviderDefinitions;

    DestroyMenu( LeftButtonSubMenu );
    DestroyMenu( MainMenu );

	return msg.wParam;
}

// Changes to the specified provider
// visual controls are changed to reflect internal state
void switchToProvider( UINT ProviderNo )
{
    MENUITEMINFO mii;
    mii.cbSize = sizeof MENUITEMINFO;

    // Uncheck old entry
    mii.fMask = MIIM_STATE;
    mii.fState = 0;
    SetMenuItemInfo( RightButtonSubMenu, LEFTBUTTONMENU_ID_BASE + CurrentChecked, FALSE, &mii );

    CurrentChecked = ProviderNo;

    // Check new entry
    mii.fState = MFS_CHECKED;
    SetMenuItemInfo( RightButtonSubMenu, LEFTBUTTONMENU_ID_BASE + CurrentChecked, FALSE, &mii );

    // Mark current as default
    SetMenuDefaultItem( LeftButtonSubMenu, CurrentChecked + RIGHTBUTTONMENU_ID_BASE, FALSE );

    // Change icon and tip
    NOTIFYICONDATA NIData;
    NIData.cbSize = sizeof NOTIFYICONDATA;
    NIData.hWnd   = hTrayWnd;
    NIData.uID    = 4711;
    NIData.uFlags = NIF_ICON | NIF_TIP;
    NIData.hIcon  = ProviderDefinitions[ CurrentChecked ].ProviderIcon;
    _tcscpy(NIData.szTip, ProviderDefinitions[ CurrentChecked ].aProviderName );

    Shell_NotifyIcon( NIM_MODIFY, &NIData );

    // Set registry value
    RegSetValueEx( ProviderKey, _T("ProviderRegKey"), 0, REG_SZ, reinterpret_cast<BYTE*>(ProviderDefinitions[ CurrentChecked ].aProviderRegistryPath), _tcslen(ProviderDefinitions[ CurrentChecked ].aProviderRegistryPath)+1 );
}

// Start userinterface of SCC provider
void startSCCUI( UINT ProviderNo )
{
    HINSTANCE SCCProvider = LoadLibrary( ProviderDefinitions[ ProviderNo ].aProviderDll );
    if ( SCCProvider )
    {
        tSccInitialize   pSccInitialize     = reinterpret_cast<tSccInitialize>(GetProcAddress(SCCProvider, "SccInitialize" ));
        tSccUninitialize pSccUninitialize   = reinterpret_cast<tSccUninitialize>(GetProcAddress(SCCProvider, "SccUninitialize" ));
        tSccRunScc       pSccRunScc         = reinterpret_cast<tSccRunScc>(GetProcAddress(SCCProvider, "SccRunScc" ));

        void *pContext;

        char    aProviderName[ SCC_max_name ];
        LONG    Caps;
        char    aProviderPath[ SCC_max_init_path ];
        DWORD   co_comment_len;
        DWORD   comment_len;

        pSccInitialize( &pContext, NULL, "SCCSwitcher", aProviderName, &Caps, aProviderPath, &co_comment_len, &comment_len );
        char *aFileArgs[] = { NULL };
        pSccRunScc( pContext, NULL, 0, aFileArgs );
        pSccUninitialize( pContext );
        FreeLibrary( SCCProvider );
    }
}

// Changes the autostart setting
void toggleAutostart()
{
    if ( Autostart )
    {
        // Remove
        if ( ERROR_SUCCESS == RegDeleteValue( WindowsRunKey, _T("SCCSwitcher") ) )
            Autostart = FALSE;
    }
    else
    {
        // Add
        if ( ERROR_SUCCESS == RegSetValueEx( WindowsRunKey, _T("SCCSwitcher"), 0, REG_SZ, reinterpret_cast<BYTE*>(aPathToExecutable), _tcslen(aPathToExecutable)+1 ) )
            Autostart = TRUE;
    }

    // Reflect change in menu
    CheckMenuItem( RightButtonSubMenu, IDM_AUTOSTART, MF_BYCOMMAND|(Autostart?MF_CHECKED:MF_UNCHECKED) );
}

// Main windows message loop
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int             wmId, wmEvent;

    // During desktop restart this message is broadcasted to all toplevel windows. 
    // The icon must be added again to be visible. 
    // (Some of MS programmers haven't read this chapter ;-) -  Watch most icons disappear when you kill and then restart explorer.exe )
    if ( message == wm_TaskbarRestart )
        addTaskbarIcon();

    // A notification for the taskbar icon is received
    if ( message == wm_iconmessage )
    {
        switch( lParam )
        {
        // It's a left button down event!
        case WM_LBUTTONDOWN:
            {
            POINT Point;
            // Get current position of mouse
            GetCursorPos( &Point );
            // And start tracking the menu

            // For the use of this and the WM_NULL Message below -> see Q135788 at MSDN
            SetForegroundWindow(hWnd);

            TrackPopupMenuEx( LeftButtonSubMenu, TPM_RIGHTALIGN|TPM_BOTTOMALIGN|TPM_LEFTBUTTON, Point.x, Point.y, hWnd, NULL );

            // see above
            PostMessage( hWnd, WM_NULL, 0, 0 );
            }
            break;

        // It's a right button down event!
        case WM_RBUTTONDOWN:
            {
            POINT Point;
            // Get current position of mouse
            GetCursorPos( &Point );
            // And start tracking the menu

            // For the use of this and the WM_NULL Message below -> see Q135788 at MSDN
            SetForegroundWindow(hWnd);

            TrackPopupMenuEx( RightButtonSubMenu, TPM_RIGHTALIGN|TPM_BOTTOMALIGN|TPM_RIGHTBUTTON, Point.x, Point.y, hWnd, NULL );

            // see above
            PostMessage( hWnd, WM_NULL, 0, 0 );
            }
            break;

        case WM_LBUTTONDBLCLK:
            // Start Userinterface of SCC provider via. SCC interface
            startSCCUI( CurrentChecked );
            break;

        default:
            break;
        }
        return 0;
    }

    switch (message) 
    {
    case WM_SETTINGCHANGE:
        break;

    case WM_COMMAND:
        wmId    = LOWORD(wParam); 
        wmEvent = HIWORD(wParam); 

        // Parse the menu selections:
        switch ( wmId )
        {
        case IDM_ABOUT:
            // Show an about box
            DialogBox( hInst, (LPCTSTR)IDD_ABOUTBOX, NULL, (DLGPROC)About );
            break;

        case IDM_EXIT:
            // TTL. Destruction triggers Quit message later
            DestroyWindow(hWnd);
            break;

        case IDM_START_SCC:
            // Start Userinterface of SCC provider via. SCC interface
            startSCCUI( CurrentChecked );
            break;

        case IDM_AUTOSTART:
            // Toggle the current autostart setting
            toggleAutostart();
            break;

        default:
            if ( wmId >= LEFTBUTTONMENU_ID_BASE &&
                 wmId < LEFTBUTTONMENU_ID_BASE_MAX )
            {
                // A new provider is selected -> Switch to it
                switchToProvider( wmId - LEFTBUTTONMENU_ID_BASE );
            }
            else
                if ( wmId >= RIGHTBUTTONMENU_ID_BASE &&
                     wmId < RIGHTBUTTONMENU_ID_BASE_MAX )
                {
                    // Start Userinterface of SCC provider via. SCC interface
                    startSCCUI( wmId - RIGHTBUTTONMENU_ID_BASE );
                }
                else
                    return DefWindowProc( hWnd, message, wParam, lParam );
        }
        break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc( hWnd, message, wParam, lParam );
    }
    return 0;
}

// Message handler for about box.
LRESULT CALLBACK About( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
    switch (message)
    {
    case WM_INITDIALOG:
        return TRUE;
        
    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
        {
            EndDialog(hDlg, LOWORD(wParam));
            return TRUE;
        }
        break;
    }
    return FALSE;
}

# Change User Description Committed
#3 633 Soenke Schau Fixed a flaw in the Unicode Version - Thanks to Johan Nilsson
#2 531 Soenke Schau Added an option to disable SCC Integration
#1 519 Soenke Schau Initial version of SCCSwitcher.