1. TortoiseHg
  2. TortoiseHg
  3. thg

Commits

Steve Borho  committed 15e0626

shellext: transplant cmenu fixes for #466 from default branch

  • Participants
  • Parent commits 67b9a44
  • Branches stable

Comments (0)

Files changed (15)

File win32/shellext/CShellExtCMenu.cpp

View file
 #include "Thgstatus.h"
 #include "Winstat.h"
 #include "InitStatus.h"
+#include "SysInfo.h"
 #include "ShellExt.h"
-
 #include "CShellExtCMenu.h"
-
 #include <map>
 
 
     mi.wID = idCmd;
     mi.fType = MFT_STRING;
 
-    HICON h = GetTortoiseIcon(iconName);
-    if (h)
+    if (SysInfo::Instance().IsVistaOrLater())
     {
-        mi.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_BITMAP | MIIM_DATA;
-        mi.dwItemData = (ULONG_PTR) h;
-        mi.hbmpItem = HBMMENU_CALLBACK;
+        TDEBUG_TRACE("    InsertMenuItemWithIcon1: Vista or later detected, using modern context menu style");
+        HBITMAP hBmp = GetTortoiseIconBitmap(iconName);
+        if (hBmp)
+        {
+            mi.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_BITMAP;
+            mi.hbmpItem = hBmp;
+        }
+        else
+        {
+            TDEBUG_TRACE("    InsertMenuItemWithIcon1: can't find " + iconName);
+            mi.fMask = MIIM_TYPE | MIIM_ID;
+        }
     }
     else
     {
-        TDEBUG_TRACE("    InsertMenuItemWithIcon1: can't find " + iconName);
-        mi.fMask = MIIM_TYPE | MIIM_ID;
+        HICON h = GetTortoiseIcon(iconName);
+        if (h)
+        {
+            mi.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_BITMAP | MIIM_DATA;
+            mi.dwItemData = (ULONG_PTR) h;
+            mi.hbmpItem = HBMMENU_CALLBACK;
+        }
+        else
+        {
+            TDEBUG_TRACE("    InsertMenuItemWithIcon1: can't find " + iconName);
+            mi.fMask = MIIM_TYPE | MIIM_ID;
+        }
     }
     InsertMenuItemW(hMenu, indexMenu, TRUE, &mi);
 
     MENUITEMINFOW mi;
     memset(&mi, 0, sizeof(mi));
     mi.cbSize = sizeof(mi);
-    mi.fMask = MIIM_SUBMENU | MIIM_STRING | MIIM_ID;
     mi.fType = MFT_STRING;
     mi.dwTypeData = const_cast<wchar_t*>(menuText.c_str());
     mi.cch = static_cast<UINT>(menuText.length());
     mi.wID = idCmd;
     mi.hSubMenu = hSubMenu;
-    HICON h = GetTortoiseIcon(iconName);
-    if (h)
+
+    if (SysInfo::Instance().IsVistaOrLater())
     {
-        mi.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_SUBMENU | MIIM_ID |
-                   MIIM_BITMAP | MIIM_DATA;
-        mi.dwItemData = (ULONG_PTR) h;
-        mi.hbmpItem = HBMMENU_CALLBACK;
+        TDEBUG_TRACE("    InsertMenuItemWithIcon1: Vista or later detected, using modern context menu style");
+        HBITMAP hBmp = GetTortoiseIconBitmap(iconName);
+        if (hBmp)
+        {
+            mi.fMask = MIIM_SUBMENU | MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_BITMAP;
+            mi.hbmpItem = hBmp;
+        }
+        else
+        {
+            TDEBUG_TRACE("    InsertSubMenuItemWithIcon2: can't find " + iconName);
+            mi.fMask = MIIM_TYPE | MIIM_ID;
+        }
     }
     else
     {
-        TDEBUG_TRACE("    InsertSubMenuItemWithIcon2: can't find " + iconName);
+        HICON h = GetTortoiseIcon(iconName);
+        if (h)
+        {
+            mi.fMask = MIIM_SUBMENU | MIIM_STRING | MIIM_ID;
+            mi.dwItemData = (ULONG_PTR) h;
+            mi.hbmpItem = HBMMENU_CALLBACK;
+        }
+        else
+        {
+            TDEBUG_TRACE("    InsertSubMenuItemWithIcon2: can't find " + iconName);
+            mi.fMask = MIIM_TYPE | MIIM_ID;
+        }
     }
+
     InsertMenuItemW(hMenu, indexMenu, TRUE, &mi);
 
     TDEBUG_TRACEW(
         }
         if (isSeparator && indexSubMenu > 0)
             RemoveMenu(hSubMenu, indexSubMenu - 1, MF_BYPOSITION);
+
+        if (SysInfo::Instance().IsVistaOrLater())
+        {
+            MENUINFO MenuInfo;
+            
+            memset(&MenuInfo, 0, sizeof(MenuInfo));
+
+            MenuInfo.cbSize  = sizeof(MenuInfo);
+            MenuInfo.fMask   = MIM_STYLE | MIM_APPLYTOSUBMENUS;
+            MenuInfo.dwStyle = MNS_CHECKORBMP;
+            
+            SetMenuInfo(hSubMenu, &MenuInfo);
+        }
     }
 
     TDEBUG_TRACE("  CShellExtCMenu::QueryContextMenu: adding main THG menu");
     InsertMenu(hMenu, indexMenu++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);
 
     InitStatus::check();
+
+    if (SysInfo::Instance().IsVistaOrLater())
+    {
+        MENUINFO MenuInfo;
+
+        memset(&MenuInfo, 0, sizeof(MenuInfo));
+
+        MenuInfo.cbSize  = sizeof(MenuInfo);
+        MenuInfo.fMask   = MIM_STYLE | MIM_APPLYTOSUBMENUS;
+        MenuInfo.dwStyle = MNS_CHECKORBMP;
+
+        SetMenuInfo(hMenu, &MenuInfo);
+    }
+
     return ResultFromShort(idCmd - idCmdFirst);
 }
 

File win32/shellext/IconBitmapUtils.cpp

View file
+// TortoiseSVN - a Windows shell extension for easy version control
+
+// Copyright (C) 2009 - TortoiseSVN
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+// Adapted for use in TortoiseHg by Veniamin Albaev
+//
+#include "stdafx.h"
+#include "IconBitmapUtils.h"
+#include "SysInfo.h"
+#include "registry.h"
+
+IconBitmapUtils::IconBitmapUtils()
+	: hUxTheme(NULL)
+{
+	if (SysInfo::Instance().IsVistaOrLater())
+	{
+		hUxTheme = LoadLibrary(_T("UXTHEME.DLL"));
+
+		if (hUxTheme)
+		{
+			pfnGetBufferedPaintBits = (FN_GetBufferedPaintBits)::GetProcAddress(hUxTheme, "GetBufferedPaintBits");
+			pfnBeginBufferedPaint = (FN_BeginBufferedPaint)::GetProcAddress(hUxTheme, "BeginBufferedPaint");
+			pfnEndBufferedPaint = (FN_EndBufferedPaint)::GetProcAddress(hUxTheme, "EndBufferedPaint");
+		}
+	}
+}
+
+IconBitmapUtils::~IconBitmapUtils()
+{
+	if (hUxTheme)
+		FreeLibrary(hUxTheme);
+}
+
+HBITMAP IconBitmapUtils::IconToBitmap(HICON hIcon)
+{
+	if (!hIcon)
+		return NULL;
+
+	RECT rect;
+
+	rect.right = ::GetSystemMetrics(SM_CXMENUCHECK);
+	rect.bottom = ::GetSystemMetrics(SM_CYMENUCHECK);
+
+	rect.left = rect.top = 0;
+
+	HWND desktop = ::GetDesktopWindow();
+	if (desktop == NULL)
+		return NULL;
+
+	HDC screen_dev = ::GetDC(desktop);
+	if (screen_dev == NULL)
+		return NULL;
+
+	// Create a compatible DC
+	HDC dst_hdc = ::CreateCompatibleDC(screen_dev);
+	if (dst_hdc == NULL)
+	{
+		::ReleaseDC(desktop, screen_dev); 
+		return NULL;
+	}
+
+	// Create a new bitmap of icon size
+	HBITMAP bmp = ::CreateCompatibleBitmap(screen_dev, rect.right, rect.bottom);
+	if (bmp == NULL)
+	{
+		::DeleteDC(dst_hdc);
+		::ReleaseDC(desktop, screen_dev); 
+		return NULL;
+	}
+
+	// Select it into the compatible DC
+	HBITMAP old_dst_bmp = (HBITMAP)::SelectObject(dst_hdc, bmp);
+	if (old_dst_bmp == NULL)
+		return NULL;
+
+	// Fill the background of the compatible DC with the white color
+	// that is taken by menu routines as transparent
+	::SetBkColor(dst_hdc, RGB(255, 255, 255));
+	::ExtTextOut(dst_hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
+
+	// Draw the icon into the compatible DC
+	::DrawIconEx(dst_hdc, 0, 0, hIcon, rect.right, rect.bottom, 0, NULL, DI_NORMAL);
+
+	// Restore settings
+	::SelectObject(dst_hdc, old_dst_bmp);
+	::DeleteDC(dst_hdc);
+	::ReleaseDC(desktop, screen_dev); 
+
+	return bmp;
+}
+
+HBITMAP IconBitmapUtils::IconToBitmapPARGB32(HICON hIcon)
+{
+	if (!hIcon)
+		return NULL;
+
+	if (pfnBeginBufferedPaint == NULL || pfnEndBufferedPaint == NULL || pfnGetBufferedPaintBits == NULL)
+	{
+		TDEBUG_TRACE("    IconBitmapUtils::IconToBitmapPARGB32: Theme functions not found, returns NULL");
+		return NULL;
+	}
+
+	SIZE sizIcon;
+	sizIcon.cx = GetSystemMetrics(SM_CXSMICON);
+	sizIcon.cy = GetSystemMetrics(SM_CYSMICON);
+
+	RECT rcIcon;
+	SetRect(&rcIcon, 0, 0, sizIcon.cx, sizIcon.cy);
+	HBITMAP hBmp = NULL;
+
+	HDC hdcDest = CreateCompatibleDC(NULL);
+	if (hdcDest)
+	{
+		if (SUCCEEDED(Create32BitHBITMAP(hdcDest, &sizIcon, NULL, &hBmp)))
+		{
+			HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcDest, hBmp);
+			if (hbmpOld)
+			{
+				BLENDFUNCTION bfAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+				BP_PAINTPARAMS paintParams = {0};
+				paintParams.cbSize = sizeof(paintParams);
+				paintParams.dwFlags = BPPF_ERASE;
+				paintParams.pBlendFunction = &bfAlpha;
+
+				HDC hdcBuffer;
+				HPAINTBUFFER hPaintBuffer = pfnBeginBufferedPaint(hdcDest, &rcIcon, BPBF_DIB, &paintParams, &hdcBuffer);
+				if (hPaintBuffer)
+				{
+					if (DrawIconEx(hdcBuffer, 0, 0, hIcon, sizIcon.cx, sizIcon.cy, 0, NULL, DI_NORMAL))
+					//if (FillRect(hdcBuffer, &rcIcon, (HBRUSH) (0x000000FF)) != 0)
+					{
+						// If icon did not have an alpha channel we need to convert buffer to PARGB
+						ConvertBufferToPARGB32(hPaintBuffer, hdcDest, hIcon, sizIcon);
+					}
+					// This will write the buffer contents to the destination bitmap
+					pfnEndBufferedPaint(hPaintBuffer, TRUE);
+				}
+
+				SelectObject(hdcDest, hbmpOld);
+			}
+		}
+
+		DeleteDC(hdcDest);
+	}
+
+	return hBmp;
+}
+
+HRESULT IconBitmapUtils::Create32BitHBITMAP(HDC hdc, const SIZE *psize, __deref_opt_out void **ppvBits, __out HBITMAP* phBmp)
+{
+	*phBmp = NULL;
+
+	BITMAPINFO bmi;
+	SecureZeroMemory(&bmi, sizeof(bmi));
+	bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+	bmi.bmiHeader.biPlanes = 1;
+	bmi.bmiHeader.biCompression = BI_RGB;
+
+	bmi.bmiHeader.biWidth = psize->cx;
+	bmi.bmiHeader.biHeight = psize->cy;
+	bmi.bmiHeader.biBitCount = 32;
+
+	HDC hdcUsed = hdc ? hdc : GetDC(NULL);
+	if (hdcUsed)
+	{
+		*phBmp = CreateDIBSection(hdcUsed, &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
+		if (hdc != hdcUsed)
+		{
+			ReleaseDC(NULL, hdcUsed);
+		}
+	}
+	return (NULL == *phBmp) ? E_OUTOFMEMORY : S_OK;
+}
+
+HRESULT IconBitmapUtils::ConvertBufferToPARGB32(HPAINTBUFFER hPaintBuffer, HDC hdc, HICON hicon, SIZE& sizIcon)
+{
+	RGBQUAD *prgbQuad;
+	int cxRow;
+	HRESULT hr = pfnGetBufferedPaintBits(hPaintBuffer, &prgbQuad, &cxRow);
+	if (SUCCEEDED(hr))
+	{
+		Gdiplus::ARGB *pargb = reinterpret_cast<Gdiplus::ARGB *>(prgbQuad);
+		if (!HasAlpha(pargb, sizIcon, cxRow))
+		{
+			ICONINFO info;
+			if (GetIconInfo(hicon, &info))
+			{
+				if (info.hbmMask)
+				{
+					hr = ConvertToPARGB32(hdc, pargb, info.hbmMask, sizIcon, cxRow);
+				}
+
+				DeleteObject(info.hbmColor);
+				DeleteObject(info.hbmMask);
+			}
+		}
+	}
+
+	return hr;
+}
+
+bool IconBitmapUtils::HasAlpha(__in Gdiplus::ARGB *pargb, SIZE& sizImage, int cxRow)
+{
+	ULONG cxDelta = cxRow - sizImage.cx;
+	for (ULONG y = sizImage.cy; y; --y)
+	{
+		for (ULONG x = sizImage.cx; x; --x)
+		{
+			if (*pargb++ & 0xFF000000)
+			{
+				return true;
+			}
+		}
+
+		pargb += cxDelta;
+	}
+
+	return false;
+}
+
+HRESULT IconBitmapUtils::ConvertToPARGB32(HDC hdc, __inout Gdiplus::ARGB *pargb, HBITMAP hbmp, SIZE& sizImage, int cxRow)
+{
+	BITMAPINFO bmi;
+	SecureZeroMemory(&bmi, sizeof(bmi));
+	bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+	bmi.bmiHeader.biPlanes = 1;
+	bmi.bmiHeader.biCompression = BI_RGB;
+
+	bmi.bmiHeader.biWidth = sizImage.cx;
+	bmi.bmiHeader.biHeight = sizImage.cy;
+	bmi.bmiHeader.biBitCount = 32;
+
+	HRESULT hr = E_OUTOFMEMORY;
+	HANDLE hHeap = GetProcessHeap();
+	void *pvBits = HeapAlloc(hHeap, 0, bmi.bmiHeader.biWidth * 4 * bmi.bmiHeader.biHeight);
+	if (pvBits)
+	{
+		hr = E_UNEXPECTED;
+		if (GetDIBits(hdc, hbmp, 0, bmi.bmiHeader.biHeight, pvBits, &bmi, DIB_RGB_COLORS) == bmi.bmiHeader.biHeight)
+		{
+			ULONG cxDelta = cxRow - bmi.bmiHeader.biWidth;
+            Gdiplus::ARGB *pargbMask = static_cast<Gdiplus::ARGB *>(pvBits);
+
+			for (ULONG y = bmi.bmiHeader.biHeight; y; --y)
+			{
+				for (ULONG x = bmi.bmiHeader.biWidth; x; --x)
+				{
+					if (*pargbMask++)
+					{
+						// transparent pixel
+						*pargb++ = 0;
+					}
+					else
+					{
+						// opaque pixel
+						*pargb++ |= 0xFF000000;
+					}
+				}
+
+				pargb += cxDelta;
+			}
+
+			hr = S_OK;
+		}
+
+		HeapFree(hHeap, 0, pvBits);
+	}
+
+	return hr;
+}

File win32/shellext/IconBitmapUtils.h

View file
+// TortoiseSVN - a Windows shell extension for easy version control
+
+// Copyright (C) 2009 - TortoiseSVN
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+
+// Adapted for use in TortoiseHg by Veniamin Albaev
+//
+#pragma once
+#include <Uxtheme.h>
+#include <GdiPlus.h>
+#include <map>
+
+typedef HRESULT (WINAPI *FN_GetBufferedPaintBits) (HPAINTBUFFER hBufferedPaint, RGBQUAD **ppbBuffer, int *pcxRow);
+typedef HPAINTBUFFER (WINAPI *FN_BeginBufferedPaint) (HDC hdcTarget, const RECT *prcTarget, BP_BUFFERFORMAT dwFormat, BP_PAINTPARAMS *pPaintParams, HDC *phdc);
+typedef HRESULT (WINAPI *FN_EndBufferedPaint) (HPAINTBUFFER hBufferedPaint, BOOL fUpdateTarget);
+
+
+/**
+ * \ingroup utils
+ * provides helper functions for converting icons to bitmaps
+ */
+class IconBitmapUtils
+{
+public:
+	IconBitmapUtils(void);
+	~IconBitmapUtils(void);
+
+	HBITMAP IconToBitmap(HICON hIcon);
+	HBITMAP IconToBitmapPARGB32(HICON hIcon);
+	HRESULT Create32BitHBITMAP(HDC hdc, const SIZE *psize, __deref_opt_out void **ppvBits, __out HBITMAP* phBmp);
+	HRESULT ConvertBufferToPARGB32(HPAINTBUFFER hPaintBuffer, HDC hdc, HICON hicon, SIZE& sizIcon);
+	bool HasAlpha(__in Gdiplus::ARGB *pargb, SIZE& sizImage, int cxRow);
+	HRESULT ConvertToPARGB32(HDC hdc, __inout Gdiplus::ARGB *pargb, HBITMAP hbmp, SIZE& sizImage, int cxRow);
+
+
+private:
+	HMODULE hUxTheme;
+
+	FN_GetBufferedPaintBits pfnGetBufferedPaintBits;
+	FN_BeginBufferedPaint pfnBeginBufferedPaint;
+	FN_EndBufferedPaint pfnEndBufferedPaint;
+};

File win32/shellext/Makefile.nmake

View file
         InitStatus.obj \
         CShellExtCMenu.obj \
         CShellExtOverlay.obj \
+		IconBitmapUtils.obj \
+		Registry.obj \
         ShellExt.obj \
         StringUtils.obj \
+        SysInfo.obj \
         dirstate.obj \
         Winstat64.obj \
         Dirstatecache.obj \
         Thgstatus.obj \
         QueryDirstate.obj
 
-LIBS    = User32.lib Ole32.lib Shlwapi.lib Shell32.lib Advapi32.lib
+LIBS    = shlwapi.lib gdiplus.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib
 DEFFILE = ShellExt.def
 
 
 LDFLAGS_DIRSTATE = $(BASE_LDFLAGS) /SUBSYSTEM:CONSOLE
 
 
-all: THgShell.dll dirstate.exe
+all: THgShell.dll
 
 clean:
     del *.obj *.dll *.exe *.lib *.exp *.manifest

File win32/shellext/Registry.cpp

View file
+// TortoiseSVN - a Windows shell extension for easy version control
+
+// Copyright (C) 2003-2006,2008-2009 - TortoiseSVN
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+#include "stdafx.h"
+#include "registry.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef __CSTRINGT_H__
+CRegBase::CRegBase()
+{
+}
+
+CRegBase::CRegBase (const CString& key, bool force, HKEY base, REGSAM sam)
+    : CRegBaseCommon<CString> (key, force, base, sam)
+{
+	m_key.TrimLeft(_T("\\"));
+	int backslashpos = m_key.ReverseFind('\\');
+	m_path = m_key.Left(backslashpos);
+	m_path.TrimRight(_T("\\"));
+	m_key = m_key.Mid(backslashpos);
+	m_key.Trim(_T("\\"));
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+CRegStdBase::CRegStdBase()
+{
+}
+
+CRegStdBase::CRegStdBase (const tstring& key, bool force, HKEY base, REGSAM sam)
+    : CRegBaseCommon<tstring> (key, force, base, sam)
+{
+	tstring::size_type pos = key.find_last_of(_T('\\'));
+    m_path = key.substr(0, pos);
+	m_key = key.substr(pos + 1);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef __ATLTYPES_H__   // defines CRect 
+CRegRect::CRegRect(void)
+    : CRegTypedBase<CRect, CRegBase>(CRect(0,0,0,0))
+{
+}
+
+CRegRect::CRegRect(const CString& key, const CRect& def, bool force, HKEY base, REGSAM sam)
+    : CRegTypedBase<CRect, CRegBase> (key, def, force, base, sam)
+{
+	read();
+}
+
+void CRegRect::InternalRead (HKEY hKey, CRect& value)
+{
+	DWORD size = 0;
+	DWORD type = 0;
+	RegQueryValueEx(hKey, m_key, NULL, &type, NULL, (LPDWORD) &size);
+
+    auto_buffer<char> buffer (size);
+    if ((LastError = RegQueryValueEx(hKey, m_key, NULL, &type, (BYTE*) buffer.get(), &size))==ERROR_SUCCESS)
+    {
+    	ASSERT(type==REG_BINARY);
+		value = CRect((LPRECT)buffer.get());
+    }
+}
+
+void CRegRect::InternalWrite (HKEY hKey, const CRect& value)
+{
+	LastError = RegSetValueEx(hKey, m_key, 0, REG_BINARY, (BYTE *)(LPCRECT)value, sizeof(value));
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef __ATLTYPES_H__   // defines CPoint 
+CRegPoint::CRegPoint(void)
+    : CRegTypedBase<CPoint, CRegBase>(CPoint(0,0))
+{
+}
+
+CRegPoint::CRegPoint(const CString& key, const CPoint& def, bool force, HKEY base, REGSAM sam)
+    : CRegTypedBase<CPoint, CRegBase> (key, def, force, base, sam)
+{
+	read();
+}
+
+void CRegPoint::InternalRead (HKEY hKey, CPoint& value)
+{
+	DWORD size = 0;
+	DWORD type = 0;
+	RegQueryValueEx(hKey, m_key, NULL, &type, NULL, (LPDWORD) &size);
+
+    auto_buffer<char> buffer(size);
+    if ((LastError = RegQueryValueEx(hKey, m_key, NULL, &type, (BYTE*) buffer.get(), &size))==ERROR_SUCCESS)
+    {
+    	ASSERT(type==REG_BINARY);
+		value = CPoint(*(POINT*)buffer.get());
+    }
+}
+
+void CRegPoint::InternalWrite (HKEY hKey, const CPoint& value)
+{
+	LastError = RegSetValueEx(hKey, m_key, 0, REG_BINARY, (BYTE *)&value, sizeof(value));
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////
+
+#ifdef __AFXCOLL_H__   // defines CStringList 
+CRegistryKey::CRegistryKey(const CString& key, HKEY base, REGSAM sam)
+{
+	m_base = base;
+	m_hKey = NULL;
+	m_sam = sam;
+	m_path = key;
+	m_path.TrimLeft(_T("\\"));
+}
+
+CRegistryKey::~CRegistryKey()
+{
+	if (m_hKey)
+		RegCloseKey(m_hKey);
+}
+
+DWORD CRegistryKey::createKey()
+{
+	DWORD disp;
+	DWORD rc = RegCreateKeyEx(m_base, m_path, 0, _T(""), REG_OPTION_NON_VOLATILE, KEY_WRITE|m_sam, NULL, &m_hKey, &disp);
+	if (rc != ERROR_SUCCESS)
+	{
+		return rc;
+	}
+	return RegCloseKey(m_hKey);
+}
+
+DWORD CRegistryKey::removeKey()
+{
+	RegOpenKeyEx(m_base, m_path, 0, KEY_WRITE|m_sam, &m_hKey);
+	return SHDeleteKey(m_base, (LPCTSTR)m_path);
+}
+
+bool CRegistryKey::getValues(CStringList& values)
+{
+	values.RemoveAll();
+
+	if (RegOpenKeyEx(m_base, m_path, 0, KEY_EXECUTE|m_sam, &m_hKey)==ERROR_SUCCESS)
+	{
+		for (int i = 0, rc = ERROR_SUCCESS; rc == ERROR_SUCCESS; i++)
+		{ 
+			TCHAR value[255];
+			DWORD size = sizeof value / sizeof TCHAR;
+			rc = RegEnumValue(m_hKey, i, value, &size, NULL, NULL, NULL, NULL);
+			if (rc == ERROR_SUCCESS) 
+			{
+				values.AddTail(value);
+			}
+		}
+	}
+
+	return values.GetCount() > 0;
+}
+
+bool CRegistryKey::getSubKeys(CStringList& subkeys)
+{
+	subkeys.RemoveAll();
+
+	if (RegOpenKeyEx(m_base, m_path, 0, KEY_EXECUTE|m_sam, &m_hKey)==ERROR_SUCCESS)
+	{
+		for (int i = 0, rc = ERROR_SUCCESS; rc == ERROR_SUCCESS; i++)
+		{ 
+			TCHAR value[1024];
+			DWORD size = sizeof value / sizeof TCHAR;
+			FILETIME last_write_time;
+			rc = RegEnumKeyEx(m_hKey, i, value, &size, NULL, NULL, NULL, &last_write_time);
+			if (rc == ERROR_SUCCESS) 
+			{
+				subkeys.AddTail(value);
+			}
+		}
+	}
+
+	return subkeys.GetCount() > 0;
+}
+#endif
+

File win32/shellext/ShellExt.def

View file
 ; shellext: Declares the module parameters for the DLL.
 
 LIBRARY      THGSHELL
-DESCRIPTION  'Shell Extensions for TortoiseHg'
 
 EXPORTS
     DllCanUnloadNow		PRIVATE 

File win32/shellext/SysInfo.cpp

View file
+// TortoiseSVN - a Windows shell extension for easy version control
+
+// Copyright (C) 2008 - TortoiseSVN
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+#include "StdAfx.h"
+#include "SysInfo.h"
+
+SysInfo::SysInfo(void)
+{
+	SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));
+	inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+	GetVersionEx((OSVERSIONINFO *)&inf);
+}
+
+SysInfo::~SysInfo(void)
+{
+}
+
+const SysInfo& SysInfo::Instance()
+{
+	static SysInfo instance;
+	return instance;
+}
+
+
+

File win32/shellext/SysInfo.h

View file
+// TortoiseSVN - a Windows shell extension for easy version control
+
+// Copyright (C) 2008 - TortoiseSVN
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+#pragma once
+
+
+/**
+ * \ingroup Utils
+ * This singleton class handles system information
+ */
+class SysInfo
+{
+private:
+	SysInfo(void);
+	~SysInfo(void);
+public:
+	static const SysInfo& Instance();
+	
+	DWORD			GetFullVersion() const {return MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);}
+	bool			IsXP() const {return (GetFullVersion() < 0x0600);} // cover Win5.1 and 5.2 alike
+	bool			IsVista() const {return (GetFullVersion() == 0x0600);}
+	bool			IsVistaOrLater() const {return (GetFullVersion() >= 0x0600);}
+	bool			IsWin7() const {return (GetFullVersion() == 0x0601);}
+	bool			IsWin7OrLater() const {return (GetFullVersion() >= 0x0601);}
+private:
+	OSVERSIONINFOEX			inf;
+};

File win32/shellext/TortoiseUtils.cpp

View file
 #include "FCNTL.H"
 
 #include "shlwapi.h"
+#include "IconBitmapUtils.h"
 
 
 LPWSTR hf_mbtowc(LPWSTR lpw, LPCSTR lpa, int nChars)
     return h;
 }
 
+HBITMAP GetTortoiseIconBitmap(const std::string& iconname)
+{
+	IconBitmapUtils bmpUtils;
+    typedef std::map<std::string, HBITMAP> BitmapCacheT;
+    static BitmapCacheT bmpcache_;
+
+   BitmapCacheT::const_iterator i = bmpcache_.find(iconname);
+    if (i != bmpcache_.end())
+        return i->second;
+
+    if (bmpcache_.size() > 200)
+    {
+        TDEBUG_TRACE("**** GetTortoiseIconBitmap: error: too many bitmaps in cache");
+        return 0;
+    }
+
+	HICON hIcon = GetTortoiseIcon(iconname);
+	if (!hIcon)
+		return 0;
+
+	HBITMAP hBmp = bmpUtils.IconToBitmapPARGB32(hIcon);
+	if (!hBmp)
+	{
+		TDEBUG_TRACE("**** GetTortoiseIconBitmap: error: something wrong in bmpUtils.ConvertToPARGB32(hIcon)");
+		return 0;
+	}
+
+    bmpcache_[iconname] = hBmp;
+
+    TDEBUG_TRACE(
+        "GetTortoiseIconBitmap: added '" << iconname << "' to bmpcache_"
+        " (" << bmpcache_.size() << " bitmaps in cache)"
+    );
+
+    return hBmp;
+}
+
 
 std::string GetHgRepoRoot(const std::string& path)
 {

File win32/shellext/TortoiseUtils.h

View file
 std::string BaseName(const std::string&);
 bool LaunchCommand(const std::string& command, const std::string& cwd);
 HICON GetTortoiseIcon(const std::string & iconname);
+HBITMAP GetTortoiseIconBitmap(const std::string& iconname);
 std::string GetHgRepoRoot(const std::string& path);
 bool IsHgRepo(const std::string& path);
 int GetRegistryConfig(const std::string& name, std::string& res);

File win32/shellext/auto_buffer.h

View file
+// TortoiseSVN - a Windows shell extension for easy version control
+
+// Copyright (C) 2009 - TortoiseSVN
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+#pragma once
+
+/**
+ * A simplified analog to std::auto_ptr<> that encapsulates
+ * an array allocated dynamically via new[].
+ *
+ * Use this where you could not use a std::auto_ptr<> (works
+ * for single elements only) nor a std::vector<> (no guarantees
+ * w.r.t. to internal organization, i.e. no access to mem buffer).
+ */
+
+template<class T>
+class auto_buffer
+{
+private:
+
+    T* buffer;
+
+    /// no copy nor assignment
+
+    auto_buffer(const auto_buffer&);
+    auto_buffer& operator=(const auto_buffer&);
+
+public:
+
+	explicit auto_buffer (size_t size = 0) throw()
+        : buffer (size == 0 ? NULL : new T[size])
+	{	
+	}
+
+	~auto_buffer()
+	{	
+		delete[] buffer;
+	}
+
+	operator T*() const throw()
+    {	
+		return buffer;
+	}
+
+	operator void*() const throw()
+    {	
+		return buffer;
+	}
+
+    operator bool() const throw()
+    {
+        return buffer != NULL;
+    }
+
+	T* operator->() const throw()
+	{
+		return buffer;
+    }
+
+	T *get() const throw()
+	{
+		return buffer;
+    }
+
+    T* release() throw()
+    {
+        T* temp = buffer;
+        buffer = NULL;
+        return temp;
+    }
+
+    void reset (size_t newSize = 0)
+    {
+        delete[] buffer;
+        buffer = (newSize == 0 ? NULL : new T[newSize]);
+    }
+};

File win32/shellext/registry.h

View file
+// TortoiseSVN - a Windows shell extension for easy version control
+
+// Copyright (C) 2003-2009 - TortoiseSVN
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+#pragma once
+#include <string>
+#include <memory>
+#include "shlwapi.h"
+#include "tstring.h"
+#include "auto_buffer.h"
+
+#ifndef ASSERT
+#define ASSERT(x)
+#endif
+
+/**
+ * \ingroup Utils
+ * Base class for the registry classes.
+ *
+ * \par requirements 
+ * - win98 or later, win2k or later, win95 with IE4 or later, winNT4 with IE4 or later
+ * - import library Shlwapi.lib
+ */
+
+template<class S>
+class CRegBaseCommon
+{
+protected:
+
+    /**
+     * String type specific operations.
+     */
+
+    virtual LPCTSTR GetPlainString (const S& s) const = 0;
+    virtual DWORD GetLength (const S& s) const = 0;
+
+public:	//methods
+
+    /** Default constructor.
+     */
+    CRegBaseCommon();
+	/**
+	 * Constructor.
+	 * \param key the path to the key, including the key. example: "Software\\Company\\SubKey\\MyValue"
+	 * \param force set to TRUE if no cache should be used, i.e. always read and write directly from/to registry
+	 * \param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
+	 */
+	CRegBaseCommon(const S& key, bool force, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+
+	/**
+	 * Removes the whole registry key including all values. So if you set the registry
+	 * entry to be HKCU\Software\Company\Product\key\value there will only be
+	 * HKCU\Software\Company\Product key in the registry.
+	 * \return ERROR_SUCCESS or an nonzero error code. Use FormatMessage() to get an error description.
+	 */
+	DWORD removeKey();
+	/**
+	 * Removes the value of the registry object. If you set the registry entry to
+	 * be HKCU\Software\Company\Product\key\value there will only be
+	 * HKCU\Software\Company\Product\key\ in the registry.
+	 * \return ERROR_SUCCESS or an nonzero error code. Use FormatMessage() to get an error description.
+	 */
+	LONG removeValue();
+
+	/**
+	 * Returns the string of the last error occurred.
+	 */
+	virtual S getErrorString()
+	{
+		LPVOID lpMsgBuf;
+
+		FormatMessage(
+			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
+			FORMAT_MESSAGE_FROM_SYSTEM |
+			FORMAT_MESSAGE_IGNORE_INSERTS,
+			NULL,
+			LastError,
+			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+			(LPTSTR) &lpMsgBuf,
+			0, NULL );
+
+		return (LPCTSTR)lpMsgBuf;
+	};
+
+    /// get failure info for last operation
+
+    LONG GetLastError() const
+    {
+        return LastError;
+    }
+
+    /// used in subclass templates to specify the correct string type
+
+    typedef S StringT;  
+
+protected:	
+    
+    //members
+	HKEY m_base;		///< handle to the registry base
+	S m_key;		    ///< the name of the value
+	S m_path;		    ///< the path to the key
+	LONG LastError;		///< the value of the last error occurred
+	REGSAM m_sam;		///< the security attributes to pass to the registry command
+
+	bool m_read;		///< indicates if the value has already been read from the registry
+	bool m_force;		///< indicates if no cache should be used, i.e. always read and write directly from registry
+    bool m_exists;      ///< true, if the registry actually exists
+};
+
+// implement CRegBaseCommon<> members
+
+template<class S>
+CRegBaseCommon<S>::CRegBaseCommon()
+    : m_base (HKEY_CURRENT_USER)
+    , m_key()
+    , m_path()
+    , LastError (ERROR_SUCCESS)
+	, m_sam (0)
+    , m_read (false)
+    , m_force (false)
+    , m_exists (false)
+{
+}
+
+template<class S>
+CRegBaseCommon<S>::CRegBaseCommon (const S& key, bool force, HKEY base, REGSAM sam)
+    : m_base (base)
+	, m_key (key)
+    , m_path()
+    , LastError (ERROR_SUCCESS)
+	, m_sam (sam)
+    , m_read (false)
+    , m_force (force)
+    , m_exists (false)
+{
+}
+
+template<class S>
+DWORD CRegBaseCommon<S>::removeKey() 
+{ 
+    m_exists = false;
+    m_read = true;
+
+    HKEY hKey = NULL;
+    RegOpenKeyEx (m_base, GetPlainString (m_path), 0, KEY_WRITE|m_sam, &hKey); 
+    return SHDeleteKey(m_base, GetPlainString (m_path)); 
+}
+
+template<class S>
+LONG CRegBaseCommon<S>::removeValue() 
+{ 
+    m_exists = false;
+    m_read = true;
+
+    HKEY hKey = NULL;
+    RegOpenKeyEx(m_base, GetPlainString (m_path), 0, KEY_WRITE|m_sam, &hKey); 
+    return RegDeleteValue(hKey, GetPlainString (m_key)); 
+}
+
+/**
+ * \ingroup Utils
+ * Base class for MFC type registry classes.
+ */
+
+#ifdef __CSTRINGT_H__
+class CRegBase : public CRegBaseCommon<CString>
+{
+protected:
+
+    /**
+     * String type specific operations.
+     */
+
+    virtual LPCTSTR GetPlainString (const CString& s) const {return (LPCTSTR)s;}
+    virtual DWORD GetLength (const CString& s) const {return s.GetLength();}
+
+public:	//methods
+
+    /** Default constructor.
+     */
+    CRegBase();
+	/**
+	 * Constructor.
+	 * \param key the path to the key, including the key. example: "Software\\Company\\SubKey\\MyValue"
+	 * \param force set to TRUE if no cache should be used, i.e. always read and write directly from/to registry
+	 * \param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
+	 */
+	CRegBase(const CString& key, bool force, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+
+	/**
+	 * Returns the string of the last error occurred.
+	 */
+	CString getErrorString()
+	{
+        CString error = CRegBaseCommon<CString>::getErrorString();
+#if defined IDS_REG_ERROR
+		CString sTemp;
+		sTemp.Format(IDS_REG_ERROR, (LPCTSTR)m_key, (LPCTSTR)error);
+		return sTemp;
+#else
+		return error;
+#endif
+	};
+};
+#endif
+
+
+
+/**
+ * \ingroup Utils
+ * Base class for STL string type registry classes.
+ */
+
+class CRegStdBase : public CRegBaseCommon<tstring>
+{
+protected:
+
+    /**
+     * String type specific operations.
+     */
+
+    virtual LPCTSTR GetPlainString (const tstring& s) const {return s.c_str();}
+    virtual DWORD GetLength (const tstring& s) const {return static_cast<DWORD>(s.size());}
+
+public:	//methods
+
+    /** Default constructor.
+     */
+    CRegStdBase();
+	/**
+	 * Constructor.
+	 * \param key the path to the key, including the key. example: "Software\\Company\\SubKey\\MyValue"
+	 * \param force set to TRUE if no cache should be used, i.e. always read and write directly from/to registry
+	 * \param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
+	 */
+	CRegStdBase(const tstring& key, bool force, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+};
+
+/**
+ * \ingroup Utils
+ * DWORD value in registry. with this class you can use DWORD values in registry
+ * like normal DWORD variables in your program.
+ * Usage:
+ * in your header file, declare your registry DWORD variable:
+ * \code
+ * CRegDWORD regvalue;
+ * \endcode
+ * next initialize the variable e.g. in the constructor of your class:
+ * \code
+ * regvalue = CRegDWORD("Software\\Company\\SubKey\\MyValue", 100);
+ * \endcode
+ * this will set the registry value "MyValue" under HKEY_CURRENT_USER with path 
+ * "Software\Company\SubKey" to the variable. If the key does not yet exist or
+ * an error occurred during read from the registry, a default
+ * value of 100 is used when accessing the variable.
+ * now the variable can be used like any other DWORD variable:
+ * \code
+ * regvalue = 200;						//stores the value 200 in the registry
+ * int temp = regvalue + 300;			//temp has value 500 now
+ * regvalue += 300;						//now the registry has the value 500 too
+ * \endcode
+ * to avoid too much access to the registry the value is cached inside the object.
+ * once the value is read, no more read accesses to the registry will be made.
+ * this means the variable will contain a wrong value if the corresponding registry
+ * entry is changed by anything else than this variable! If you think that could happen
+ * then use 
+ * \code
+ * regvalue.read();
+ * \endcode
+ * to force a refresh of the variable with the registry.
+ * a write to the registry is only made if the new value assigned with the variable
+ * is different than the last assigned value.
+ * to force a write use the method write();
+ * another option to force reads and writes to the registry is to specify TRUE as the
+ * third parameter in the constructor.
+ */
+template<class T, class Base>
+class CRegTypedBase : public Base
+{
+private:
+
+	T m_value;					///< the cached value of the registry
+	T m_defaultvalue;			///< the default value to use
+
+    /**
+     * time stamp of the last registry lookup, i.e \ref read() call 
+     */
+
+    DWORD lastRead;             
+
+    /**
+     * \ref read() will be called, if \ref lastRead differs from the
+     * current time stamp by more than this.
+     * (DWORD)(-1) -> no automatic refresh.
+     */
+
+    DWORD lookupInterval;       
+
+    /**
+     * Check time stamps etc. 
+     * If the current data is out-dated, reset the \ref m_read flag.
+     */
+
+    void HandleAutoRefresh();
+
+    /**
+     * sub-classes must provide type-specific code to extract data from
+     * and write data to an open registry key.
+     */
+
+    virtual void InternalRead (HKEY hKey, T& value) = 0;
+    virtual void InternalWrite (HKEY hKey, const T& value) = 0;
+
+public:
+
+    /**
+     * Make the value type accessible to others.
+     */
+
+    typedef T ValueT;
+
+    /**
+	 * Constructor.
+     * We use this instead of a default constructor because not all 
+     * data types may provide an adequate default constructor.
+     */
+	CRegTypedBase(const T& def);
+
+	/**
+	 * Constructor.
+	 * \param key the path to the key, including the key. example: "Software\\Company\\SubKey\\MyValue"
+	 * \param def the default value used when the key does not exist or a read error occurred
+	 * \param force set to TRUE if no cache should be used, i.e. always read and write directly from/to registry
+	 * \param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
+	 */
+    CRegTypedBase(const typename Base::StringT& key, const T& def, bool force = FALSE, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+
+	/**
+	 * Constructor.
+	 * \param updateInterval time in msec between registry lookups caused by operator const T&
+	 * \param key the path to the key, including the key. example: "Software\\Company\\SubKey\\MyValue"
+	 * \param def the default value used when the key does not exist or a read error occurred
+	 * \param force set to TRUE if no cache should be used, i.e. always read and write directly from/to registry
+	 * \param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
+	 */
+    CRegTypedBase(DWORD updateInterval, const typename Base::StringT& key, const T& def, bool force = FALSE, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+
+	/**
+	 * reads the assigned value from the registry. Use this method only if you think the registry
+	 * value could have been altered without using the CRegDWORD object.
+	 * \return the read value
+	 */
+	void   	read();						///< reads the value from the registry
+	void	write();					///< writes the value to the registry
+
+    bool    exists();                   ///< test whether registry entry exits
+    const T& defaultValue() const;      ///< return the default passed to the constructor
+
+    /**
+     * Data access.
+     */
+
+    operator const T&();
+	CRegTypedBase<T,Base>& operator=(const T& rhs);
+};
+
+// implement CRegTypedBase<> members
+
+template<class T, class Base>
+void CRegTypedBase<T, Base>::HandleAutoRefresh()
+{
+    if (m_read && (lookupInterval != (DWORD)(-1)))
+    {
+        DWORD currentTime = GetTickCount();
+        if (   (currentTime < lastRead)
+            || (currentTime > lastRead + lookupInterval))
+        {
+            m_read = false;
+        }
+    }
+}
+
+template<class T, class Base>
+CRegTypedBase<T, Base>::CRegTypedBase (const T& def)
+    : m_value (def)
+    , m_defaultvalue (def)
+    , lastRead (0)
+    , lookupInterval ((DWORD)-1)
+{
+}
+
+template<class T, class Base>
+CRegTypedBase<T, Base>::CRegTypedBase (const typename Base::StringT& key, const T& def, bool force, HKEY base, REGSAM sam)
+    : Base (key, force, base, sam)
+    , m_value (def)
+    , m_defaultvalue (def)
+    , lastRead (0)
+    , lookupInterval ((DWORD)-1)
+{
+}
+
+template<class T, class Base>
+CRegTypedBase<T, Base>::CRegTypedBase (DWORD lookupInterval, const typename Base::StringT& key, const T& def, bool force, HKEY base, REGSAM sam)
+    : Base (key, force, base, sam)
+    , m_value (def)
+    , m_defaultvalue (def)
+    , lastRead (0)
+    , lookupInterval (lookupInterval)
+{
+}
+
+template<class T, class Base>
+void CRegTypedBase<T, Base>::read()
+{
+	m_value = m_defaultvalue;
+    m_exists = false;
+
+    HKEY hKey = NULL;
+	if ((LastError = RegOpenKeyEx (m_base, GetPlainString (m_path), 0, KEY_EXECUTE|m_sam, &hKey))==ERROR_SUCCESS)
+	{
+		m_read = true;
+
+        T value = m_defaultvalue;
+        InternalRead (hKey, value);
+
+		if (LastError ==ERROR_SUCCESS)
+		{
+            m_exists = true;
+			m_value = value;
+		}
+
+        LastError = RegCloseKey(hKey);
+	}
+
+    lastRead = GetTickCount();
+}
+
+template<class T, class Base>
+void CRegTypedBase<T, Base>::write()
+{
+    HKEY hKey = NULL;
+
+    DWORD disp = 0;
+	if ((LastError = RegCreateKeyEx(m_base, GetPlainString (m_path), 0, _T(""), REG_OPTION_NON_VOLATILE, KEY_WRITE|m_sam, NULL, &hKey, &disp))!=ERROR_SUCCESS)
+	{
+		return;
+	}
+
+    InternalWrite (hKey, m_value);
+	if (LastError ==ERROR_SUCCESS)
+	{
+		m_read = true;
+        m_exists = true;
+	}
+	LastError = RegCloseKey(hKey);
+
+    lastRead = GetTickCount();
+}
+
+template<class T, class Base>
+bool CRegTypedBase<T, Base>::exists()
+{
+    if (!m_read && (LastError == ERROR_SUCCESS))
+        read();
+
+    return m_exists;
+}
+
+template<class T, class Base>
+const T& CRegTypedBase<T, Base>::defaultValue() const
+{
+    return m_defaultvalue;
+}
+
+template<class T, class Base>
+CRegTypedBase<T, Base>::operator const T&()
+{
+    HandleAutoRefresh();
+	if ((m_read)&&(!m_force))
+	{
+		LastError = ERROR_SUCCESS;
+	}
+	else
+	{
+		read();
+	}
+
+	return m_value;
+}
+
+template<class T, class Base>
+CRegTypedBase<T, Base>& CRegTypedBase<T, Base>::operator =(const T& d)
+{
+	if (m_read && (d == m_value) && !m_force)
+	{
+		//no write to the registry required, its the same value
+		LastError = ERROR_SUCCESS;
+		return *this;
+	}
+	m_value = d;
+	write();
+	return *this;
+}
+
+/**
+ * \ingroup Utils
+ * DWORD value in registry. with this class you can use DWORD values in registry
+ * like normal DWORD variables in your program.
+ * Usage:
+ * in your header file, declare your registry DWORD variable:
+ * \code
+ * CRegDWORD regvalue;
+ * \endcode
+ * next initialize the variable e.g. in the constructor of your class:
+ * \code
+ * regvalue = CRegDWORD("Software\\Company\\SubKey\\MyValue", 100);
+ * \endcode
+ * this will set the registry value "MyValue" under HKEY_CURRENT_USER with path 
+ * "Software\Company\SubKey" to the variable. If the key does not yet exist or
+ * an error occurred during read from the registry, a default
+ * value of 100 is used when accessing the variable.
+ * now the variable can be used like any other DWORD variable:
+ * \code
+ * regvalue = 200;						//stores the value 200 in the registry
+ * int temp = regvalue + 300;			//temp has value 500 now
+ * regvalue += 300;						//now the registry has the value 500 too
+ * \endcode
+ * to avoid too much access to the registry the value is cached inside the object.
+ * once the value is read, no more read accesses to the registry will be made.
+ * this means the variable will contain a wrong value if the corresponding registry
+ * entry is changed by anything else than this variable! If you think that could happen
+ * then use 
+ * \code
+ * regvalue.read();
+ * \endcode
+ * to force a refresh of the variable with the registry.
+ * a write to the registry is only made if the new value assigned with the variable
+ * is different than the last assigned value.
+ * to force a write use the method write();
+ * another option to force reads and writes to the registry is to specify TRUE as the
+ * third parameter in the constructor.
+ */
+template<class Base>
+class CRegDWORDCommon : public CRegTypedBase<DWORD,Base>
+{
+private:
+
+    /**
+     * provide type-specific code to extract data from and write data to an open registry key.
+     */
+
+    virtual void InternalRead (HKEY hKey, DWORD& value);
+    virtual void InternalWrite (HKEY hKey, const DWORD& value);
+
+public:
+
+	CRegDWORDCommon(void);
+	/**
+	 * Constructor.
+	 * \param key the path to the key, including the key. example: "Software\\Company\\SubKey\\MyValue"
+	 * \param def the default value used when the key does not exist or a read error occurred
+	 * \param force set to TRUE if no cache should be used, i.e. always read and write directly from/to registry
+	 * \param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
+	 */
+    CRegDWORDCommon(const typename Base::StringT& key, DWORD def = 0, bool force = false, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+    CRegDWORDCommon(DWORD lookupInterval, const typename Base::StringT& key, DWORD def = 0, bool force = false, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+
+    CRegDWORDCommon& operator=(DWORD rhs) {CRegTypedBase<DWORD, Base>::operator =(rhs); return *this;}
+	CRegDWORDCommon& operator+=(DWORD d) { return *this = *this + d;}
+	CRegDWORDCommon& operator-=(DWORD d) { return *this = *this - d;}
+	CRegDWORDCommon& operator*=(DWORD d) { return *this = *this * d;}
+	CRegDWORDCommon& operator/=(DWORD d) { return *this = *this / d;}
+	CRegDWORDCommon& operator%=(DWORD d) { return *this = *this % d;}
+	CRegDWORDCommon& operator<<=(DWORD d) { return *this = *this << d;}
+	CRegDWORDCommon& operator>>=(DWORD d) { return *this = *this >> d;}
+	CRegDWORDCommon& operator&=(DWORD d) { return *this = *this & d;}
+	CRegDWORDCommon& operator|=(DWORD d) { return *this = *this | d;}
+	CRegDWORDCommon& operator^=(DWORD d) { return *this = *this ^ d;}
+};
+
+// implement CRegDWORDCommon<> methods
+
+template<class Base>
+CRegDWORDCommon<Base>::CRegDWORDCommon(void)
+    : CRegTypedBase<DWORD, Base>(0)
+{
+}
+
+template<class Base>
+CRegDWORDCommon<Base>::CRegDWORDCommon(const typename Base::StringT& key, DWORD def, bool force, HKEY base, REGSAM sam)
+    : CRegTypedBase<DWORD, Base> (key, def, force, base, sam)
+{
+}
+
+template<class Base>
+CRegDWORDCommon<Base>::CRegDWORDCommon(DWORD lookupInterval, const typename Base::StringT& key, DWORD def, bool force, HKEY base, REGSAM sam)
+    : CRegTypedBase<DWORD, Base> (lookupInterval, key, def, force, base, sam)
+{
+}
+
+template<class Base>
+void CRegDWORDCommon<Base>::InternalRead (HKEY hKey, DWORD& value)
+{
+    DWORD size = sizeof(value);
+	DWORD type = 0;
+	if ((LastError = RegQueryValueEx(hKey, GetPlainString (m_key), NULL, &type, (BYTE*) &value, &size))==ERROR_SUCCESS)
+    {
+		ASSERT(type==REG_DWORD);
+    }
+}
+
+template<class Base>
+void CRegDWORDCommon<Base>::InternalWrite (HKEY hKey, const DWORD& value)
+{
+	LastError = RegSetValueEx (hKey, GetPlainString (m_key), 0, REG_DWORD,(const BYTE*) &value, sizeof(value));
+}
+
+/**
+ * \ingroup Utils
+ * CString value in registry. with this class you can use CString values in registry
+ * almost like normal CString variables in your program.
+ * Usage:
+ * in your header file, declare your registry CString variable:
+ * \code
+ * CRegString regvalue;
+ * \endcode
+ * next initialize the variable e.g. in the constructor of your class:
+ * \code
+ * regvalue = CRegString("Software\\Company\\SubKey\\MyValue", "default");
+ * \endcode
+ * this will set the registry value "MyValue" under HKEY_CURRENT_USER with path 
+ * "Software\Company\SubKey" to the variable. If the key does not yet exist or
+ * an error occurred during read from the registry, a default
+ * value of "default" is used when accessing the variable.
+ * now the variable can be used like any other CString variable:
+ * \code
+ * regvalue = "some string";			//stores the value "some string" in the registry
+ * CString temp = regvalue + "!!";		//temp has value "some string!!" now
+ * \endcode
+ * to use the normal methods of the CString class, just typecast the CRegString to a CString
+ * and do whatever you want with the string:
+ * \code
+ * ((CString)regvalue).GetLength();
+ * ((CString)regvalue).Trim();
+ * \endcode
+ * please be aware that in the second line the change in the string won't be written
+ * to the registry! To force a write use the write() method. A write() is only needed
+ * if you change the String with Methods not overloaded by CRegString.
+ * to avoid too much access to the registry the value is cached inside the object.
+ * once the value is read, no more read accesses to the registry will be made.
+ * this means the variable will contain a wrong value if the corresponding registry
+ * entry is changed by anything else than this variable! If you think that could happen
+ * then use 
+ * \code
+ * regvalue.read();
+ * \endcode
+ * to force a refresh of the variable with the registry.
+ * a write to the registry is only made if the new value assigned with the variable
+ * is different than the last assigned value.
+ * to force a write use the method write();
+ * another option to force reads and writes to the registry is to specify TRUE as the
+ * third parameter in the constructor.
+ */
+template<class Base>
+class CRegStringCommon : public CRegTypedBase<typename Base::StringT, Base>
+{
+private:
+
+    /**
+     * provide type-specific code to extract data from and write data to an open registry key.
+     */
+
+    virtual void InternalRead (HKEY hKey, typename Base::StringT& value);
+    virtual void InternalWrite (HKEY hKey, const typename Base::StringT& value);
+
+public:
+	CRegStringCommon();
+	/**
+	 * Constructor.
+	 * \param key the path to the key, including the key. example: "Software\\Company\\SubKey\\MyValue"
+	 * \param def the default value used when the key does not exist or a read error occurred
+	 * \param force set to TRUE if no cache should be used, i.e. always read and write directly from/to registry
+	 * \param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
+	 */
+    CRegStringCommon(const typename Base::StringT& key, const typename Base::StringT& def = _T(""), bool force = false, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+    CRegStringCommon(DWORD lookupInterval, const typename Base::StringT& key, const typename Base::StringT& def = _T(""), bool force = false, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+	
+    CRegStringCommon& operator=(const typename Base::StringT& rhs) {CRegTypedBase<StringT, Base>::operator =(rhs); return *this;}
+	CRegStringCommon& operator+=(const typename Base::StringT& s) { return *this = (typename Base::StringT)*this + s; }
+};
+
+// implement CRegDWORD<> methods
+
+template<class Base>
+CRegStringCommon<Base>::CRegStringCommon(void)
+    : CRegTypedBase<typename Base::StringT, Base>(typename Base::StringT())
+{
+}
+
+template<class Base>
+CRegStringCommon<Base>::CRegStringCommon(const typename Base::StringT& key, const typename Base::StringT& def, bool force, HKEY base, REGSAM sam)
+    : CRegTypedBase<typename Base::StringT, Base> (key, def, force, base, sam)
+{
+}
+
+template<class Base>
+CRegStringCommon<Base>::CRegStringCommon(DWORD lookupInterval, const typename Base::StringT& key, const typename Base::StringT& def, bool force, HKEY base, REGSAM sam)
+    : CRegTypedBase<typename Base::StringT, Base> (lookupInterval, key, def, force, base, sam)
+{
+}
+
+template<class Base>
+void CRegStringCommon<Base>::InternalRead (HKEY hKey, typename Base::StringT& value)
+{
+	DWORD size = 0;
+	DWORD type = 0;
+	LastError = RegQueryValueEx(hKey, GetPlainString (m_key), NULL, &type, NULL, &size);
+
+    auto_buffer<TCHAR> pStr (size);
+	if ((LastError = RegQueryValueEx(hKey, GetPlainString (m_key), NULL, &type, (BYTE*) pStr.get(), &size))==ERROR_SUCCESS)
+    {
+        ASSERT(type==REG_SZ || type==REG_EXPAND_SZ);
+        value = StringT (pStr.get());
+    }
+}
+
+template<class Base>
+void CRegStringCommon<Base>::InternalWrite (HKEY hKey, const typename Base::StringT& value)
+{
+	LastError = RegSetValueEx(hKey, GetPlainString (m_key), 0, REG_SZ, (BYTE *)GetPlainString (value), (GetLength(value)+1)*sizeof (TCHAR));
+}
+
+/**
+ * \ingroup Utils
+ * CRect value in registry. with this class you can use CRect values in registry
+ * almost like normal CRect variables in your program.
+ * Usage:
+ * in your header file, declare your registry CString variable:
+ * \code
+ * CRegRect regvalue;
+ * \endcode
+ * next initialize the variable e.g. in the constructor of your class:
+ * \code
+ * regvalue = CRegRect("Software\\Company\\SubKey\\MyValue", CRect(100,100,200,200));
+ * \endcode
+ * this will set the registry value "MyValue" under HKEY_CURRENT_USER with path 
+ * "Software\Company\SubKey" to the variable. If the key does not yet exist or
+ * an error occurred during read from the registry, a default
+ * value of 100,100,200,200 is used when accessing the variable.
+ * now the variable can be used like any other CRect variable:
+ * \code
+ * regvalue = CRect(40,20,300,500);				//stores the value in the registry
+ * CRect temp = regvalue + CPoint(1,1);
+ * temp |= CSize(5,5);
+ * \endcode
+ * to use the normal methods of the CRect class, just typecast the CRegRect to a CRect
+ * and do whatever you want with the rect:
+ * \code
+ * ((CRect)regvalue).MoveToX(100);
+ * ((CRect)regvalue).DeflateRect(10,10);
+ * \endcode
+ * please be aware that in the second line the change in the CRect won't be written
+ * to the registry! To force a write use the write() method. A write() is only needed
+ * if you change the CRect with Methods not overloaded by CRegRect.
+ * to avoid too much access to the registry the value is cached inside the object.
+ * once the value is read, no more read accesses to the registry will be made.
+ * this means the variable will contain a wrong value if the corresponding registry
+ * entry is changed by anything else than this variable! If you think that could happen
+ * then use 
+ * \code
+ * regvalue.read();
+ * \endcode
+ * to force a refresh of the variable with the registry.
+ * a write to the registry is only made if the new value assigned with the variable
+ * is different than the last assigned value.
+ * to force a write use the method write();
+ * another option to force reads and writes to the registry is to specify TRUE as the
+ * third parameter in the constructor.
+ */
+
+#ifdef __ATLTYPES_H__   // defines CRect 
+class CRegRect : public CRegTypedBase<CRect, CRegBase>
+{
+private:
+
+    /**
+     * provide type-specific code to extract data from and write data to an open registry key.
+     */
+
+    virtual void InternalRead (HKEY hKey, CRect& value);
+    virtual void InternalWrite (HKEY hKey, const CRect& value);
+
+public:
+	CRegRect();
+	/**
+	 * Constructor.
+	 * \param key the path to the key, including the key. example: "Software\\Company\\SubKey\\MyValue"
+	 * \param def the default value used when the key does not exist or a read error occurred
+	 * \param force set to TRUE if no cache should be used, i.e. always read and write directly from/to registry
+	 * \param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
+	 */
+	CRegRect(const CString& key, const CRect& def = CRect(), bool force = false, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+	~CRegRect(void);
+	
+    CRegRect& operator=(const CRect& rhs) {CRegTypedBase<CRect, CRegBase>::operator =(rhs); return *this;}
+	operator LPCRECT() { return (LPCRECT)(CRect)*this; }
+	operator LPRECT() { return (LPRECT)(CRect)*this; }
+	CRegRect& operator+=(POINT r) { return *this = (CRect)*this + r;}
+	CRegRect& operator+=(SIZE r) { return *this = (CRect)*this + r;}
+	CRegRect& operator+=(LPCRECT  r) { return *this = (CRect)*this + r;}
+	CRegRect& operator-=(POINT r) { return *this = (CRect)*this - r;}
+	CRegRect& operator-=(SIZE r) { return *this = (CRect)*this - r;}
+	CRegRect& operator-=(LPCRECT  r) { return *this = (CRect)*this - r;}
+	
+	CRegRect& operator&=(CRect r) { return *this = r & *this;}
+	CRegRect& operator|=(CRect r) { return *this = r | *this;}
+};
+#endif
+
+/**
+ * \ingroup Utils
+ * CPoint value in registry. with this class you can use CPoint values in registry
+ * almost like normal CPoint variables in your program.
+ * Usage:
+ * in your header file, declare your registry CPoint variable:
+ * \code
+ * CRegPoint regvalue;
+ * \endcode
+ * next initialize the variable e.g. in the constructor of your class:
+ * \code
+ * regvalue = CRegPoint("Software\\Company\\SubKey\\MyValue", CPoint(100,100));
+ * \endcode
+ * this will set the registry value "MyValue" under HKEY_CURRENT_USER with path 
+ * "Software\Company\SubKey" to the variable. If the key does not yet exist or
+ * an error occurred during read from the registry, a default
+ * value of 100,100 is used when accessing the variable.
+ * now the variable can be used like any other CPoint variable:
+ * \code
+ * regvalue = CPoint(40,20);					//stores the value in the registry
+ * CPoint temp = regvalue + CPoint(1,1);
+ * temp += CSize(5,5);
+ * \endcode
+ * to use the normal methods of the CPoint class, just typecast the CRegPoint to a CPoint
+ * and do whatever you want with the point:
+ * \code
+ * ((CRect)regvalue).Offset(100,10);
+ * \endcode
+ * please be aware that in the above example the change in the CPoint won't be written
+ * to the registry! To force a write use the write() method. A write() is only needed
+ * if you change the CPoint with Methods not overloaded by CRegPoint.
+ * to avoid too much access to the registry the value is cached inside the object.
+ * once the value is read, no more read accesses to the registry will be made.
+ * this means the variable will contain a wrong value if the corresponding registry
+ * entry is changed by anything else than this variable! If you think that could happen
+ * then use 
+ * \code
+ * regvalue.read();
+ * \endcode
+ * to force a refresh of the variable with the registry.
+ * a write to the registry is only made if the new value assigned with the variable
+ * is different than the last assigned value.
+ * to force a write use the method write();
+ * another option to force reads and writes to the registry is to specify TRUE as the
+ * third parameter in the constructor.
+ */
+
+#ifdef __ATLTYPES_H__   // defines CPoint 
+class CRegPoint : public CRegTypedBase<CPoint, CRegBase>
+{
+private:
+
+    /**
+     * provide type-specific code to extract data from and write data to an open registry key.
+     */
+
+    virtual void InternalRead (HKEY hKey, CPoint& value);
+    virtual void InternalWrite (HKEY hKey, const CPoint& value);
+
+public:
+	CRegPoint();
+	/**
+	 * Constructor.
+	 * \param key the path to the key, including the key. example: "Software\\Company\\SubKey\\MyValue"
+	 * \param def the default value used when the key does not exist or a read error occurred
+	 * \param force set to TRUE if no cache should be used, i.e. always read and write directly from/to registry
+	 * \param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
+	 */
+	CRegPoint(const CString& key, const CPoint& def = CPoint(), bool force = false, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+	~CRegPoint(void);
+	
+    CRegPoint& operator=(const CPoint& rhs) {CRegTypedBase<CPoint, CRegBase>::operator =(rhs); return *this;}
+	CRegPoint& operator+=(CPoint p) { return *this = p + *this; }
+	CRegPoint& operator-=(CPoint p) { return *this = p - *this; }
+};
+#endif
+
+/**
+ * \ingroup Utils
+ * Manages a registry key (not a value). Provides methods to create and remove the
+ * key and to query the list of values and sub keys.
+ */
+
+#ifdef __AFXCOLL_H__   // defines CStringList 
+class CRegistryKey
+{
+public:	//methods
+	/**
+	 * Constructor.
+	 * \param key the path to the key, including the key. example: "Software\\Company\\SubKey"
+	 * \param base a predefined base key like HKEY_LOCAL_MACHINE. see the SDK documentation for more information.
+	 */
+	CRegistryKey(const CString& key, HKEY base = HKEY_CURRENT_USER, REGSAM sam = 0);
+	~CRegistryKey();
+
+	/**
+	 * Creates the registry key if it does not already exist.
+	 * \return ERROR_SUCCESS or an nonzero error code. Use FormatMessage() to get an error description.
+	 */
+	DWORD createKey();
+	/**
+	 * Removes the whole registry key including all values. So if you set the registry
+	 * entry to be HKCU\Software\Company\Product\key there will only be
+	 * HKCU\Software\Company\Product key in the registry.
+	 * \return ERROR_SUCCESS or an nonzero error code. Use FormatMessage() to get an error description.
+	 */
+	DWORD removeKey();
+
+	bool getValues(CStringList& values);		///< returns the list of values
+	bool getSubKeys(CStringList& subkeys);		///< returns the list of sub keys
+
+public:	//members
+	HKEY m_base;		///< handle to the registry base
+	HKEY m_hKey;		///< handle to the open registry key
+	REGSAM m_sam;		///< the security attributes to pass to the registry command
+	CString m_path;		///< the path to the key
+};
+#endif
+
+#ifdef _MAP_
+template<class T>
+class CKeyList
+{
+private:
+
+    /// constructor parameters
+
+    typename T::StringT key;
+    typename T::ValueT defaultValue;
+    HKEY base;
+
+    /// per-index defaults
+
+    typedef std::map<int, typename T::ValueT> TDefaults;
+    TDefaults defaults;
+
+    /// the indices accessed so far
+
+    typedef std::map<int, T*> TElements;
+    mutable TElements elements;
+
+    /// auto-insert
+
+    const typename T::ValueT& GetDefault (int index) const;
+    T& GetAt (int index) const;
+
+public:
+
+    /// construction
+
+    CKeyList (const typename T::StringT& key, const typename T::ValueT& defaultValue, HKEY base = HKEY_CURRENT_USER)
+        : key (key)
+        , defaultValue (defaultValue)
+        , base (base)
+    {
+    }
+
+    /// destruction: delete all elements
+
+    ~CKeyList()
+    {
+        for ( TElements::iterator iter = elements.begin()
+            , end = elements.end()
+            ; iter != end
+            ; ++iter)
+        {
+            delete iter->second;
+        }
+    }
+
+    /// data access
+
+    const T& operator[] (int index) const
+    {
+        return GetAt (index);
+    }
+
+    T& operator[] (int index)
+    {
+        return GetAt (index);
+    }
+
+    const TDefaults& GetDefaults() const
+    {
+        return defaults;
+    }
+
+    TDefaults& GetDefaults()
+    {
+        return defaults;
+    }
+
+    const typename T::ValueT& GetDefault() const
+    {
+        return defaultValue;
+    }
+};
+
+/// auto-insert
+
+template<class T>
+const typename T::ValueT& CKeyList<T>::GetDefault (int index) const
+{
+    TDefaults::const_iterator iter = defaults.find (index);
+    return iter == defaults.end() ? defaultValue : iter->second;
+}
+
+template<class T>
+T& CKeyList<T>::GetAt (int index) const
+{
+    TElements::iterator iter = elements.find (index);
+    if (iter == elements.end())
+    {
+        TCHAR buffer [10];
+        _itot_s (index, buffer, 10);
+        typename T::StringT indexKey = key + _T ('\\') + buffer;
+
+        T* newElement = new T (indexKey, GetDefault (index), false, base);
+        iter = elements.insert (std::make_pair (index, newElement)).first;
+    }
+
+    return *iter->second;
+};
+
+#endif
+
+/**
+ * Instantiate templates for common (data type, string type) combinations.
+ */
+
+#ifdef __CSTRINGT_H__
+CRegDWORDCommon<CRegBase>;
+typedef CRegDWORDCommon<CRegBase> CRegDWORD;
+CRegStringCommon<CRegBase>;
+typedef CRegStringCommon<CRegBase> CRegString;
+
+#ifdef _MAP_
+CKeyList<CRegDWORD>;
+typedef CKeyList<CRegDWORD> CRegDWORDList;
+CKeyList<CRegString>;
+typedef CKeyList<CRegString> CRegStringList;
+#endif
+#endif
+
+CRegDWORDCommon<CRegStdBase>;
+typedef CRegDWORDCommon<CRegStdBase> CRegStdDWORD;
+CRegStringCommon<CRegStdBase>;
+typedef CRegStringCommon<CRegStdBase> CRegStdString;
+
+#ifdef _MAP_
+CKeyList<CRegStdDWORD>;
+typedef CKeyList<CRegStdDWORD> CRegStdDWORDList;
+CKeyList<CRegStdString>;
+typedef CKeyList<CRegStdString> CRegStdStringList;
+#endif
+

File win32/shellext/shellext.sln

View file
 
-Microsoft Visual Studio Solution File, Format Version 9.00
-# Visual Studio 2005
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shellext", "shellext.vcproj", "{B7E760E4-DDA6-44BA-95E5-F6EDA42321B5}"
 	ProjectSection(WebsiteProperties) = preProject
 		Debug.AspNetCompiler.Debug = "True"

File win32/shellext/shellext.vcproj

View file