<not part of the commit message>
The minimal regedit engine is so big that it's tempting to move it
from dll.c to a separate module
</not part of the commit message>

This is an implementation of TODO "Need to not hard-code the dll path"

Registration is implemented with a minimal regedit engine: an array of
registry values { path, name, value } is the data source. So, all
registry entries are hard-coded in dll.c.

msysGit (for PathToMsys) is searched in the following order:
- %PATH%
- $(TARGET)/..
- $(TARGET)/../..
- InstallLocation of uninstall info (machine first, then user).

TODO: verify that the found git.exe is really Git
TODO: search for InstallLocation according to the type of the current
      installation (default or -i:machine)
---
 Makefile          |   22 +--
 dll.c             |  390 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 git_shell_ext.def |    1 +
 install.reg.in    |   58 --------
 uninstall.reg     |   27 ----
 5 files changed, 382 insertions(+), 116 deletions(-)
 delete mode 100644 install.reg.in
 delete mode 100644 uninstall.reg

diff --git a/Makefile b/Makefile
index 4042879..6076285 100644
--- a/Makefile
+++ b/Makefile
@@ -23,23 +23,17 @@ factory.o: factory.h ext.h menu.h
 menu.o: menu.h ext.h debug.h systeminfo.h
 systeminfo.o: systeminfo.h

-inst%: inst%.reg all
-       regsvr32 -s $(DLL_PATH)
-       regedit -s $<
+install: all
+       regsvr32 -s -n -i:machine $(DLL_PATH)

-uninst%: uninst%.reg
-       regsvr32 -u -s $(DLL_PATH)
-       regedit -s $<
+uninstall: all
+       regsvr32 -u -s -n -i:machine $(DLL_PATH)

-install.reg: install.reg.in Makefile
-       sed < $< > $@ \
-               -e 's|@@MSYSGIT_PATH@@|$(MSYSGIT_PATH)|' \
-               -e 's|@@DLL_PATH@@|$(DLL_PATH)|'
+install-user: all
+       regsvr32 -s $(DLL_PATH)

-%-user.reg: %.reg
-       sed < $< > $@ \
-               -e 's|HKEY_LOCAL_MACHINE\\|HKEY_CURRENT_USER\\|' \
-               -e 
's|HKEY_CLASSES_ROOT\\|HKEY_CURRENT_USER\\Software\\Classes\\|'
+uninstall-user: all
+       regsvr32 -u -s $(DLL_PATH)

 clean:
        -rm -f $(OBJECTS) $(TARGET)
diff --git a/dll.c b/dll.c
index bab9d9a..40f52ae 100644
--- a/dll.c
+++ b/dll.c
@@ -1,12 +1,129 @@
 #include <shlobj.h>
+#include <stdio.h>
 #include "dll.h"
 #include "ext.h"
 #include "factory.h"
+#include "systeminfo.h"

 /*
  * The following is just the necessary infrastructure for having a .dll
  * which can be registered as a COM object.
  */
+static HINSTANCE hInst;
+
+static const char *get_module_filename() {
+       static char module_filename[MAX_PATH] = { '\0' };
+
+       if ('\0' == module_filename[0]) {
+               DWORD module_size;
+
+               module_size = GetModuleFileName(hInst, module_filename, 
MAX_PATH);
+               if (0 == module_size)
+                       return NULL;
+       }
+
+       return module_filename;
+}
+
+#define MAX_REGISTRY_PATH MAX_PATH
+
+/* as per "How to: Convert Between System::Guid and _GUID" */
+static const char *get_class_id()
+{
+       static char class_id[MAX_REGISTRY_PATH] = { '\0' };
+
+       if ('\0' == class_id[0]) {
+               GUID guid = CLSID_git_shell_ext;
+               sprintf (class_id,
+                       "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+                       guid.Data1, guid.Data2, guid.Data3,
+                       guid.Data4[0], guid.Data4[1], guid.Data4[2], 
guid.Data4[3],
+                       guid.Data4[4], guid.Data4[5], guid.Data4[6], 
guid.Data4[7]);
+       }
+
+       return class_id;
+}
+
+/*
+ * Tries to find msysGit in the following order:
+ * %PATH%
+ * .. and ../.. (relative to the module)
+ * as qgit (via InstallLocation of Git)
+ SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Git_is1\InstallLocation
+ */
+static char msysgit[MAX_PATH] = { '\0' };
+
+static BOOL find_msysgit_in_path()
+{
+       char *file; // file part of the path to git.exe
+       DWORD dwFound; // length of path to git.exe
+
+       dwFound = SearchPath(NULL, "git.exe", NULL, MAX_PATH, msysgit, &file);
+       if (0 == dwFound ||     // git.exe is not in the PATH or
+               dwFound > MAX_PATH) // well, it's weird, and we're unprepared
+               return FALSE;
+
+       *(file - 5) = '\0'; // git exe is in "\bin\" from what we really need
+       return TRUE;
+}
+
+static BOOL find_msysgit_relative(const char *path)
+{
+       char *c;
+
+       strcpy(msysgit, get_module_filename());
+       c = strrchr(msysgit, '\\');
+       c[1] = '\0';
+       strcat(msysgit, path);
+       strcat(msysgit, "\\bin\\git.exe");
+       if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(msysgit)) {
+               msysgit[0] = '\0'; // restore the original result
+               return FALSE;
+       }
+
+       c[1] = '\0';
+       strcat(msysgit, path);
+       return TRUE;
+}
+
+static BOOL find_msysgit_uninstall(HKEY root)
+{
+       HKEY key;
+       HRESULT result;
+       DWORD valuelen = MAX_PATH;
+
+       result = RegOpenKeyEx(root,
+               
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1",
+               0, KEY_READ, &key);
+       if (ERROR_SUCCESS != result)
+               return FALSE;
+
+       result = RegQueryValue(key, "InstallLocation", (LPBYTE)msysgit, 
&valuelen);
+       return ERROR_SUCCESS == result;
+}
+
+static const char *find_msysgit()
+{
+       if ('\0' == msysgit[0]) {
+               if (find_msysgit_in_path())
+                       return msysgit;
+
+               // try .. from our own directory
+               if (find_msysgit_relative(".."))
+                       return msysgit;
+
+               // try ../..
+               if (find_msysgit_relative("..\\.."))
+                       return msysgit;
+
+               /* TODO: find an elegant way to pass the installation type
+                        down here */
+               if (! find_msysgit_uninstall(HKEY_LOCAL_MACHINE))
+                       find_msysgit_uninstall(HKEY_CURRENT_USER);
+       }
+
+       return msysgit;
+}

 HRESULT PASCAL DllGetClassObject(REFCLSID obj_guid, REFIID factory_guid,
                void **factory_handle)
@@ -25,33 +142,272 @@ HRESULT PASCAL DllCanUnloadNow(void)
    return (object_count || lock_count) ? S_FALSE : S_OK;
 }

-HRESULT PASCAL DllRegisterServer(void)
+BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
 {
-       char module[MAX_PATH];
-       wchar_t module_name[MAX_PATH];
-       ITypeLib *typelib = NULL;
+       hInst = instance;

-       GetModuleFileName(NULL, module, MAX_PATH);
-       MultiByteToWideChar(CP_ACP, 0, module, -1, module_name, MAX_PATH);
-       if (LoadTypeLib(module_name, &typelib) == S_OK) {
-               HRESULT result = RegisterTypeLib(typelib, module_name, NULL);
-               typelib->lpVtbl->Release(typelib);
-               return result;
+       if (reason == DLL_PROCESS_ATTACH) {
+               object_count = lock_count = 0;
+               DisableThreadLibraryCalls(instance);
        }
-       return 1;
+
+        return 1;
+}
+
+/* replaces a substring pattern with a string replacement within a string
+   the replacement occurs in-place, hence string must be large enough to
+   hold the result
+
+   the function does not handle recursive replacements, e.g.
+     strreplace ("foo", "bar", "another bar"); // unexpected results
+
+   always returns *string
+*/
+static char *strreplace(char *string,
+                                               const char *pattern,
+                                               const char *replacement)
+{
+       char *tail;
+       char *found = strstr(string, pattern);
+
+       while (found) {
+               tail = strdup(found + strlen(pattern));
+
+               *found = '\0';
+               strcat(string, replacement);
+               strcat(string, tail);
+
+               free(tail);
+
+               found = strstr(string, pattern);
+       }
+
+       return string;
+}
+
+/*
+ * The following is required for registration/unregistration of a DLL
+ * It's basically a simplified regedit engine that supports
+ * @@CLSID@@ pattern to be replaced with CLSID of the extension;
+ * @@PROGRAM_NAME@@ - program_name variable;
+ * @@PROGRAM_PATH@@ - path to the extension;
+ * @@MSYSGIT_PATH@@ - path to msysgit;
+ *
+ * It attempts to convert values to LONG to create REG_DWORD values
+ */
+
+typedef struct REG_VALUE {
+       char *path;
+       char *name;
+       char *value;
+} REG_VALUE;
+
+static const REG_VALUE registry_info[] = {
+       { "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell
Extensions\\Approved", "@@CLSID@@", "@@PROGRAM_NAME@@" },
+       { "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell
Extensions\\Approved\\@@CLSID@@", NULL, NULL },
+       { "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell
Extensions\\Approved\\@@CLSID@@", NULL,"@@PROGRAM_NAME@@" },
+       { "SOFTWARE\\Classes\\CLSID\\@@CLSID@@", NULL, NULL },
+       { "SOFTWARE\\Classes\\CLSID\\@@CLSID@@", NULL, "@@PROGRAM_NAME@@" },
+       { "SOFTWARE\\Classes\\CLSID\\@@CLSID@@\\InProcServer32", NULL, NULL },
+       { "SOFTWARE\\Classes\\CLSID\\@@CLSID@@\\InProcServer32", NULL,
"@@PROGRAM_PATH@@"},
+       { "SOFTWARE\\Classes\\CLSID\\@@CLSID@@\\InProcServer32",
"ThreadingModel", "Apartment" },
+       { 
"SOFTWARE\\Classes\\*\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, NULL },
+       { 
"SOFTWARE\\Classes\\*\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, "@@CLSID@@" },
+       { 
"SOFTWARE\\Classes\\Directory\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, NULL },
+       { 
"SOFTWARE\\Classes\\Directory\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, "@@CLSID@@" },
+       { 
"SOFTWARE\\Classes\\Directory\\Background\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, NULL },
+       { 
"SOFTWARE\\Classes\\Directory\\Background\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, "@@CLSID@@" },
+       { 
"SOFTWARE\\Classes\\Drive\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, NULL },
+       { 
"SOFTWARE\\Classes\\Drive\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, "@@CLSID@@"},
+       { 
"SOFTWARE\\Classes\\Folder\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, NULL },
+       { 
"SOFTWARE\\Classes\\Folder\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, "@@CLSID@@" },
+       { 
"SOFTWARE\\Classes\\InternetShortcut\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, NULL },
+       { 
"SOFTWARE\\Classes\\InternetShortcut\\shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@",
NULL, "@@CLSID@@" },
+       { GIT_CHEETAH_REG_PATH, NULL, NULL },
+       { GIT_CHEETAH_REG_PATH, GIT_CHEETAH_REG_PATHTOMSYS, "@@MSYSGIT_PATH@@" 
},
+       { NULL, NULL, NULL }
+};
+
+static const REG_VALUE debug_info[] = {
+       { "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer",
"DesktopProcess", "1" },
+       { 
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AlwaysUnloadDll",
NULL, NULL },
+       { "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
"SeparateProcess", "1" },
+       { NULL, NULL, NULL }
+};
+
+/* supports @@PROGRAM_NAME@@, @@PROGRAM_PATH@@, @@CLSID@@ patterns */
+static char *get_registry_path(const char *src, char dst[MAX_REGISTRY_PATH])
+{
+       if (NULL == src)
+               return NULL;
+
+       strcpy(dst, src);
+       strreplace(dst, "@@PROGRAM_NAME@@", program_name);
+       strreplace(dst, "@@PROGRAM_PATH@@", get_module_filename());
+       strreplace(dst, "@@CLSID@@", get_class_id());
+       strreplace(dst, "@@MSYSGIT_PATH@@", find_msysgit());
+
+       return dst;
+}
+
+/* uses get_registry_path to replace patterns */
+static HRESULT create_reg_entries(const HKEY root, REG_VALUE const info[])
+{
+       HRESULT result;
+       int i;
+
+       for (i = 0; NULL != info[i].path; ++i) {
+               char path[MAX_REGISTRY_PATH];
+               char name[MAX_REGISTRY_PATH], *regname = NULL;
+               char value [MAX_REGISTRY_PATH], *regvalue = NULL;
+
+               HKEY key;
+               DWORD disp; // required for RegCreateKeyEx, otherwise unused
+
+               get_registry_path(info[i].path, path);
+               result = RegCreateKeyEx(root, path,
+                       0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
+                       &key, &disp);
+               if (ERROR_SUCCESS != result)
+                       return (result);
+
+               regname = get_registry_path(info[i].name, name);
+               regvalue = get_registry_path(info[i].value, value);
+
+               // regname can legitimately be NULL, but if value is NULL,
+               // it's just a key
+               if (NULL != regvalue) {
+                       char *endptr;
+                       DWORD dwValue = strtoul(regvalue, &endptr, 10);
+                       if (endptr && '\0' == *endptr)
+                               result = RegSetValueEx(key, regname, 0, 
REG_DWORD,
+                               (LPBYTE)&dwValue, sizeof(dwValue));
+                       else
+                               result = RegSetValueEx(key, regname, 0, REG_SZ,
+                               (LPBYTE)regvalue, (DWORD)strlen(regvalue));
+               }
+
+               RegCloseKey(key);
+               if (ERROR_SUCCESS != result)
+                       return (result);
+       }
+
+       return ERROR_SUCCESS;
+}
+
+static inline HRESULT mask_reg_errors(HRESULT const result)
+{
+       switch (result) {
+               case ERROR_FILE_NOT_FOUND: return ERROR_SUCCESS;
+       }
+
+       return result;
+}
+
+static HRESULT delete_reg_entries(HKEY const root, REG_VALUE const info[])
+{
+       HRESULT result;
+       int i = 0;
+
+       // count items in the array
+       while (NULL != info[i++].path);
+       // at this point, i is the __count__, so
+       // make it an offset to the last element
+       --i;
+
+       // walk the array backwards (we're at the terminating triple-null)
+       do {
+               char path[MAX_REGISTRY_PATH];
+               HKEY key;
+
+               --i;
+
+               get_registry_path(info[i].path, path);
+
+               if (info[i].name || info[i].value) { // if it's a value
+                       char name[MAX_REGISTRY_PATH], *regname = NULL;
+
+                       result = mask_reg_errors(RegOpenKeyEx(root, path,
+                                                                               
0, KEY_WRITE, &key));
+                       if (ERROR_SUCCESS != result)
+                               return result;
+
+                       // because some of our errors are masked (e.g. not 
found)
+                       // don't work on this key if we could not open it
+                       if (NULL == key)
+                               continue;
+
+                       regname = get_registry_path(info[i].name, name);
+                       result = mask_reg_errors(RegDeleteValue(key, regname));
+
+                       RegCloseKey(key);
+               } else // it's a key
+                       result = mask_reg_errors(RegDeleteKey(root, path));
+
+               if (ERROR_SUCCESS != result)
+                       return (result);
+       } while (i);
+
+       return ERROR_SUCCESS;
+}
+
+HRESULT PASCAL DllRegisterServer(void)
+{
+       return create_reg_entries (HKEY_CURRENT_USER, registry_info);
 }

 HRESULT PASCAL DllUnregisterServer(void)
 {
-       return S_OK;
+       return delete_reg_entries(HKEY_CURRENT_USER, registry_info);
 }

-BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
+/* provide means to create/delete keys:
+   - described in Debugging with The Shell;
+   - machine-wide registration and debugging info
+
+   possible combinations of regsvr32 command line options:
+             -n  (absent)               (present)
+   -i:
+   (absent)      user reg               (invalid)
+   debug         user reg+debug         user debug
+   machine       user+machine reg       machine reg
+   machinedebug  user+machine reg+debug machine reg+debug
+
+   Obviously missing option is "machine debug". To accomplish:
+   - execute "regsvr32 -n -i:machinedebug" and then
+   - regsvr32 -u -n -i:machine
+*/
+HRESULT PASCAL DllInstall(BOOL bInstall, LPCWSTR pszCmdLine)
 {
-       if (reason == DLL_PROCESS_ATTACH) {
-               object_count = lock_count = 0;
-               DisableThreadLibraryCalls(instance);
+       BOOL bMachine = NULL != wcsstr(pszCmdLine, L"machine");
+       BOOL bDebug = NULL != wcsstr(pszCmdLine, L"debug");
+       HRESULT result = ERROR_INVALID_COMMAND_LINE;
+
+       if (bInstall) {
+               if (bMachine) {
+                       result = create_reg_entries(HKEY_LOCAL_MACHINE,
+                               registry_info);
+                       if (ERROR_SUCCESS == result && bDebug)
+                               result = create_reg_entries(HKEY_LOCAL_MACHINE,
+                                       debug_info);
+
+               } else if (bDebug) {
+                       result = create_reg_entries(HKEY_CURRENT_USER,
+                                       debug_info);
+               }
+       } else { // uninstall
+               if (bMachine) {
+                       result = delete_reg_entries(HKEY_LOCAL_MACHINE,
+                               registry_info);
+                       if (ERROR_SUCCESS == result && bDebug)
+                               result = delete_reg_entries(HKEY_LOCAL_MACHINE,
+                                       debug_info);
+
+               } else if (bDebug) {
+                       result = delete_reg_entries(HKEY_CURRENT_USER,
+                                       debug_info);
+               }
        }

-        return 1;
+       return result;
 }
diff --git a/git_shell_ext.def b/git_shell_ext.def
index 472c184..bea88da 100644
--- a/git_shell_ext.def
+++ b/git_shell_ext.def
@@ -5,3 +5,4 @@ DllRegisterServer PRIVATE
 DllUnregisterServer PRIVATE
 DllCanUnloadNow PRIVATE
 DllGetClassObject PRIVATE
+DllInstall PRIVATE
diff --git a/install.reg.in b/install.reg.in
deleted file mode 100644
index 2eb8f13..0000000
--- a/install.reg.in
+++ /dev/null
@@ -1,58 +0,0 @@
-Windows Registry Editor Version 5.00
-
-; This registry file creates neccessary entries for installation.
-; **** If you change this file, keep uninstall.reg in sync! ****
-;
-; This file is slated for being generated and not hard-coded.
-
-[HKEY_LOCAL_MACHINE\SOFTWARE\Git-Cheetah]
-
-[HKEY_LOCAL_MACHINE\SOFTWARE\Git-Cheetah]
-"PathToMsys"="@@MSYSGIT_PATH@@"
-
-[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell
Extensions\Approved]
-"{ca586c80-7c84-4b88-8537-726724df6929}"="Git-Cheetah"
-
-[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell
Extensions\Approved\{ca586c80-7c84-4b88-8537-726724df6929}]
[EMAIL PROTECTED]"Git-Cheetah"
-
-[HKEY_CLASSES_ROOT\CLSID\{ca586c80-7c84-4b88-8537-726724df6929}]
-
-[HKEY_CLASSES_ROOT\CLSID\{ca586c80-7c84-4b88-8537-726724df6929}]
[EMAIL PROTECTED]"Git-Cheetah"
-
-[HKEY_CLASSES_ROOT\CLSID\{ca586c80-7c84-4b88-8537-726724df6929}\InProcServer32]
[EMAIL PROTECTED]"@@DLL_PATH@@"
-
-[HKEY_CLASSES_ROOT\CLSID\{ca586c80-7c84-4b88-8537-726724df6929}\InProcServer32]
-"ThreadingModel"="Apartment"
-
-[HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\Git-Cheetah]
-
-[HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\Git-Cheetah]
[EMAIL PROTECTED]"{ca586c80-7c84-4b88-8537-726724df6929}"
-
-[HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers\Git-Cheetah]
-
-[HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers\Git-Cheetah]
[EMAIL PROTECTED]"{ca586c80-7c84-4b88-8537-726724df6929}"
-
-[HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers\Git-Cheetah]
-
-[HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers\Git-Cheetah]
[EMAIL PROTECTED]"{ca586c80-7c84-4b88-8537-726724df6929}"
-
-[HKEY_CLASSES_ROOT\Drive\shellex\ContextMenuHandlers\Git-Cheetah]
-
-[HKEY_CLASSES_ROOT\Drive\shellex\ContextMenuHandlers\Git-Cheetah]
[EMAIL PROTECTED]"{ca586c80-7c84-4b88-8537-726724df6929}"
-
-[HKEY_CLASSES_ROOT\Folder\shellex\ContextMenuHandlers\Git-Cheetah]
-
-[HKEY_CLASSES_ROOT\Folder\shellex\ContextMenuHandlers\Git-Cheetah]
[EMAIL PROTECTED]"{ca586c80-7c84-4b88-8537-726724df6929}"
-
-[HKEY_CLASSES_ROOT\InternetShortcut\shellex\ContextMenuHandlers\Git-Cheetah]
-
-[HKEY_CLASSES_ROOT\InternetShortcut\shellex\ContextMenuHandlers\Git-Cheetah]
[EMAIL PROTECTED]"{ca586c80-7c84-4b88-8537-726724df6929}"
diff --git a/uninstall.reg b/uninstall.reg
deleted file mode 100644
index bd33685..0000000
--- a/uninstall.reg
+++ /dev/null
@@ -1,27 +0,0 @@
-Windows Registry Editor Version 5.00
-
-; This registry file creates neccessary entries for uninstallation.
-; **** If you change this file, keep install.reg in sync! ****
-;
-; This file is slated for being generated and not hard-coded.
-
-[-HKEY_LOCAL_MACHINE\SOFTWARE\Git-Cheetah]
-
-[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell
Extensions\Approved]
-"{ca586c80-7c84-4b88-8537-726724df6929}"=-
-
-[-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell
Extensions\Approved\{ca586c80-7c84-4b88-8537-726724df6929}]
-
-[-HKEY_CLASSES_ROOT\CLSID\{ca586c80-7c84-4b88-8537-726724df6929}]
-
-[-HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\Git-Cheetah]
-
-[-HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers\Git-Cheetah]
-
-[-HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers\Git-Cheetah]
-
-[-HKEY_CLASSES_ROOT\Drive\shellex\ContextMenuHandlers\Git-Cheetah]
-
-[-HKEY_CLASSES_ROOT\Folder\shellex\ContextMenuHandlers\Git-Cheetah]
-
-[-HKEY_CLASSES_ROOT\InternetShortcut\shellex\ContextMenuHandlers\Git-Cheetah]
-- 
1.5.4.rc0.929.g50e2

Reply via email to