// SCCSwitcher.cpp :
// Created by Snke Schau, Germany SCCSwitcher@schau.org
#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) * sizeof TCHAR );
}
// 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) * sizeof TCHAR ) )
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;
}