2 #include "../common/cache.h"
7 #include "../common/menuengine.h"
10 #include "../common/systeminfo.h"
13 const char *program_name
= "Git-Cheetah";
14 const char *program_version
= "Git-Cheetah.Application.1";
15 const char *program_id
= "Git-Cheetah.Application";
18 * The following is just the necessary infrastructure for having a .dll
19 * which can be registered as a COM object.
23 HRESULT STDAPICALLTYPE
DllGetClassObject(REFCLSID obj_guid
, REFIID factory_guid
,
24 void **factory_handle
)
26 if (IsEqualCLSID(obj_guid
, &CLSID_git_shell_ext
) ||
27 IsEqualCLSID(obj_guid
, &CLSID_git_menu
))
28 return class_factory_query_interface(&factory
,
29 factory_guid
, factory_handle
);
32 return CLASS_E_CLASSNOTAVAILABLE
;
35 HRESULT STDAPICALLTYPE
DllCanUnloadNow(void)
37 return (object_count
|| lock_count
) ? S_FALSE
: S_OK
;
40 BOOL WINAPI
DllMain(HINSTANCE instance
, DWORD reason
, LPVOID reserved
)
44 if (reason
== DLL_PROCESS_ATTACH
) {
45 object_count
= lock_count
= 0;
46 DisableThreadLibraryCalls(instance
);
52 /* replaces a substring pattern with a string replacement within a string
53 the replacement occurs in-place, hence string must be large enough to
56 the function does not handle recursive replacements, e.g.
57 strreplace ("foo", "bar", "another bar");
59 always returns *string
61 static char *strreplace(char *string
, const size_t size
,
62 const char *pattern
, const char *replacement
)
64 size_t len
= strlen(string
);
65 const size_t pattern_len
= strlen(pattern
);
66 const size_t replace_len
= strlen(replacement
);
68 char *found
= strstr(string
, pattern
);
71 /* if the new len is greater than size, bail out */
72 if (len
+ replace_len
- pattern_len
>= size
)
75 if (pattern_len
!= replace_len
)
76 memmove(found
+ replace_len
,
78 len
- (found
- string
) - pattern_len
+ 1);
79 memcpy(found
, replacement
, replace_len
);
80 len
+= replace_len
- pattern_len
;
82 found
= strstr(string
, pattern
);
89 * The following is the data for our minimal regedit engine,
90 * required for registration/unregistration of the extension
92 #define CLASS_CHEETAH CLASSES_ROOT "CLSID\\@@CLSID@@"
93 #define CONTEXTMENUHANDLER "shellex\\ContextMenuHandlers\\@@PROGRAM_NAME@@"
94 #define COLUMNHANDLER "shellex\\ColumnHandlers"
96 static const char *get_module_filename() {
97 static char module_filename
[MAX_PATH
] = { '\0' };
99 if (!*module_filename
) {
102 module_size
= GetModuleFileName(hInst
,
103 module_filename
, MAX_PATH
);
104 if (0 == module_size
)
108 return module_filename
;
111 /* as per "How to: Convert Between System::Guid and _GUID" */
112 static const char *get_class_id()
114 static char class_id
[MAX_REGISTRY_PATH
] = { '\0' };
117 GUID guid
= CLSID_git_shell_ext
;
119 "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
120 guid
.Data1
, guid
.Data2
, guid
.Data3
,
121 guid
.Data4
[0], guid
.Data4
[1], guid
.Data4
[2],
122 guid
.Data4
[3], guid
.Data4
[4], guid
.Data4
[5],
123 guid
.Data4
[6], guid
.Data4
[7]);
130 * Tries to find msysGit in the following order:
131 * .. and ../.. (relative to the module)
133 * as qgit (via InstallLocation of Git)
134 SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Git_is1\InstallLocation
136 static const reg_value registry_info
[] = {
137 { CURRENT_WINDOWS APPROVED_EXT
, "@@CLSID@@", "@@PROGRAM_NAME@@" },
138 { CURRENT_WINDOWS APPROVED_EXT
"\\@@CLSID@@",
140 { CURRENT_WINDOWS APPROVED_EXT
"\\@@CLSID@@",
141 NULL
,"@@PROGRAM_NAME@@" },
142 { CLASS_CHEETAH
, NULL
, NULL
},
143 { CLASS_CHEETAH
, NULL
, "@@PROGRAM_NAME@@" },
144 { CLASS_CHEETAH
"\\InProcServer32", NULL
, NULL
},
145 { CLASS_CHEETAH
"\\InProcServer32", NULL
, "@@PROGRAM_PATH@@"},
146 { CLASS_CHEETAH
"\\InProcServer32", "ThreadingModel", "Apartment" },
147 { CLASSES_ROOT
"*\\" CONTEXTMENUHANDLER
, NULL
, NULL
},
148 { CLASSES_ROOT
"*\\" CONTEXTMENUHANDLER
, NULL
, "@@CLSID@@" },
149 { CLASSES_ROOT
"Directory\\" CONTEXTMENUHANDLER
, NULL
, NULL
},
150 { CLASSES_ROOT
"Directory\\" CONTEXTMENUHANDLER
, NULL
, "@@CLSID@@" },
151 { CLASSES_ROOT
"Directory\\Background\\" CONTEXTMENUHANDLER
,
153 { CLASSES_ROOT
"Directory\\Background\\" CONTEXTMENUHANDLER
,
155 { CLASSES_ROOT
"Drive\\" CONTEXTMENUHANDLER
, NULL
, NULL
},
156 { CLASSES_ROOT
"Drive\\" CONTEXTMENUHANDLER
, NULL
, "@@CLSID@@"},
157 { CLASSES_ROOT
"Folder\\" CONTEXTMENUHANDLER
, NULL
, NULL
},
158 { CLASSES_ROOT
"Folder\\" CONTEXTMENUHANDLER
, NULL
, "@@CLSID@@" },
159 { CLASSES_ROOT
"Folder\\" COLUMNHANDLER
"\\@@CLSID@@", NULL
, NULL
},
160 { CLASSES_ROOT
"InternetShortcut\\" CONTEXTMENUHANDLER
,
162 { CLASSES_ROOT
"InternetShortcut\\" CONTEXTMENUHANDLER
,
164 { GIT_CHEETAH_REG_PATH
, NULL
, NULL
},
165 { GIT_CHEETAH_REG_PATH
,
166 GIT_CHEETAH_REG_PATHTOMSYS
, "@@MSYSGIT_PATH@@" },
170 static const reg_value debug_info
[] = {
171 { CURRENT_WINDOWS
"Explorer", "DesktopProcess", "1" },
172 { CURRENT_WINDOWS
"Explorer\\AlwaysUnloadDll", NULL
, NULL
},
173 { CURRENT_WINDOWS
"Explorer\\Advanced", "SeparateProcess", "1" },
177 static char msysgit
[MAX_PATH
] = { '\0' };
179 static BOOL
find_msysgit_in_path()
181 char *file
; /* file part of the path to git.exe */
182 DWORD dwFound
; /* length of path to git.exe */
184 dwFound
= SearchPath(NULL
, "git.exe", NULL
, MAX_PATH
, msysgit
, &file
);
185 /* if git is not in the PATH or its path is too long */
191 * git.exe is in "\bin\" from what we really need
192 * the minimal case we can handle is c:\bin\git.exe
193 * otherwise declare failure. We additionally check
194 * for Git for Windows where it is placed in ..\Git\cmd\
196 if (file
< msysgit
+ 7)
198 if (strnicmp(file
- 5, "\\bin\\", 5) && strnicmp(file
- 5, "\\cmd\\", 5))
205 static BOOL
find_msysgit_relative(const char *path
)
209 strcpy(msysgit
, get_module_filename());
210 c
= strrchr(msysgit
, '\\');
212 strcat(msysgit
, path
);
213 strcat(msysgit
, "\\bin\\git.exe");
214 if (INVALID_FILE_ATTRIBUTES
== GetFileAttributes(msysgit
)) {
215 msysgit
[0] = '\0'; /* restore the original result */
220 strcat(msysgit
, path
);
224 static BOOL
find_msysgit_uninstall(HKEY root
)
228 DWORD valuelen
= MAX_PATH
;
230 result
= RegOpenKeyEx(root
,
231 CURRENT_WINDOWS
"\\Uninstall\\Git_is1",
233 if (ERROR_SUCCESS
!= result
)
236 result
= RegQueryValue(key
, "InstallLocation",
237 (LPBYTE
)msysgit
, &valuelen
);
238 return ERROR_SUCCESS
== result
;
241 static HKEY setup_root
;
243 static const char *find_msysgit()
245 if ('\0' == msysgit
[0]) {
246 if (find_msysgit_relative(".."))
249 if (find_msysgit_relative("..\\.."))
252 if (find_msysgit_in_path())
256 find_msysgit_uninstall(setup_root
);
263 * required by registry.c
264 * supports @@PROGRAM_NAME@@, @@PROGRAM_PATH@@, @@CLSID@@ patterns
266 char *get_registry_path(const char *src
, char dst
[MAX_REGISTRY_PATH
])
272 strreplace(dst
, MAX_REGISTRY_PATH
,
273 "@@PROGRAM_NAME@@", program_name
);
274 strreplace(dst
, MAX_REGISTRY_PATH
,
275 "@@PROGRAM_PATH@@", get_module_filename());
276 strreplace(dst
, MAX_REGISTRY_PATH
,
277 "@@CLSID@@", get_class_id());
278 strreplace(dst
, MAX_REGISTRY_PATH
,
279 "@@MSYSGIT_PATH@@", find_msysgit());
284 HRESULT PASCAL
DllRegisterServer(void)
287 setup_root
= HKEY_CURRENT_USER
;
288 retval
= create_reg_entries (setup_root
, registry_info
);
289 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
,NULL
,NULL
);
293 HRESULT PASCAL
DllUnregisterServer(void)
296 setup_root
= HKEY_CURRENT_USER
;
297 retval
= delete_reg_entries(setup_root
, registry_info
);
298 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
,NULL
,NULL
);
302 /* provide means to create/delete keys:
303 - described in Debugging with The Shell;
304 - machine-wide registration and debugging info
306 possible combinations of regsvr32 command line options:
307 -n (absent) (present)
309 (absent) user reg (invalid)
310 debug user reg+debug user debug
311 machine user+machine reg machine reg
312 machinedebug user+machine reg+debug machine reg+debug
314 Obviously missing option is "machine debug". To accomplish:
315 - execute "regsvr32 -n -i:machinedebug" and then
316 - regsvr32 -u -n -i:machine
318 HRESULT PASCAL
DllInstall(BOOL bInstall
, LPCWSTR pszCmdLine
)
320 BOOL bDebug
= NULL
!= wcsstr(pszCmdLine
, L
"debug");
321 HRESULT result
= ERROR_SUCCESS
;
323 setup_root
= wcsstr(pszCmdLine
, L
"machine") ?
324 HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER
;
328 result
= create_reg_entries(setup_root
, debug_info
);
330 /* for user-only registration, use DllRegister */
331 if (ERROR_SUCCESS
== result
&&
332 HKEY_LOCAL_MACHINE
== setup_root
)
333 result
= create_reg_entries(setup_root
,
335 } else { /* uninstall */
337 result
= delete_reg_entries(setup_root
, debug_info
);
339 /* for user-only unregistration, use DllUnregister */
340 if (ERROR_SUCCESS
== result
&&
341 HKEY_LOCAL_MACHINE
== setup_root
)
342 result
= delete_reg_entries(setup_root
,
345 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
,NULL
,NULL
);