Get rid of magic numbers
[TortoiseGit.git] / src / TortoiseShell / TortoiseStub.cpp
blob7dbb2793810f3ee13328371c40736c5dd3b00c6e
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2014, 2016-2017 - TortoiseGit
4 // Copyright (C) 2007, 2009, 2013-2014, 2018 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "../targetver.h"
21 #include <Windows.h>
22 #include <tchar.h>
23 #include "Debug.h"
25 const HINSTANCE NIL = (HINSTANCE)((char*)(0)-1);
27 static HINSTANCE hInst = nullptr;
29 static HINSTANCE hTortoiseGit = nullptr;
30 static LPFNGETCLASSOBJECT pDllGetClassObject = nullptr;
31 static LPFNCANUNLOADNOW pDllCanUnloadNow = nullptr;
33 static wchar_t DebugDllPath[MAX_PATH] = { 0 };
35 static BOOL DebugActive(void)
37 static const WCHAR TGitRootKey[] = L"Software\\TortoiseGit";
38 static const WCHAR DebugShellValue[] = L"DebugShell";
39 static const WCHAR DebugShellPathValue[] = L"DebugShellPath";
41 DWORD bDebug = 0;
43 HKEY hKey = HKEY_CURRENT_USER;
44 LONG Result = ERROR;
45 DWORD Type = REG_DWORD;
46 DWORD Len = sizeof(DWORD);
48 BOOL bDebugActive = FALSE;
51 TRACE(L"DebugActive() - Enter\n");
53 if (IsDebuggerPresent())
55 Result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TGitRootKey, 0, KEY_READ, &hKey);
56 if (Result == ERROR_SUCCESS)
58 Result = RegQueryValueEx(hKey, DebugShellValue, nullptr, &Type, (BYTE*)&bDebug, &Len);
59 if ((Result == ERROR_SUCCESS) && (Type == REG_DWORD) && (Len == sizeof(DWORD)) && bDebug)
61 TRACE(L"DebugActive() - debug active\n");
62 bDebugActive = TRUE;
63 Len = sizeof(wchar_t)*MAX_PATH;
64 Type = REG_SZ;
65 Result = RegQueryValueEx(hKey, DebugShellPathValue, nullptr, &Type, (BYTE*)DebugDllPath, &Len);
66 if ((Result == ERROR_SUCCESS) && (Type == REG_SZ) && bDebug)
67 TRACE(L"DebugActive() - debug path set\n");
70 RegCloseKey(hKey);
73 else
75 Result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TGitRootKey, 0, KEY_READ, &hKey);
76 if (Result == ERROR_SUCCESS)
78 Len = sizeof(wchar_t)*MAX_PATH;
79 Type = REG_SZ;
80 Result = RegQueryValueEx(hKey, DebugShellPathValue, nullptr, &Type, (BYTE*)DebugDllPath, &Len);
81 if ((Result == ERROR_SUCCESS) && (Type == REG_SZ) && bDebug)
82 TRACE(L"DebugActive() - debug path set\n");
83 RegCloseKey(hKey);
87 TRACE(L"WantRealVersion() - Exit\n");
88 return bDebugActive;
91 /**
92 * \ingroup TortoiseShell
93 * Check whether to load the full TortoiseGit.dll or not.
95 static BOOL WantRealVersion(void)
97 static const WCHAR TGitRootKey[] = L"Software\\TortoiseGit";
98 static const WCHAR ExplorerOnlyValue[] = L"LoadDllOnlyInExplorer";
100 static const WCHAR ExplorerEnvPath[] = L"%SystemRoot%\\explorer.exe";
103 DWORD bExplorerOnly = 0;
104 WCHAR ModuleName[MAX_PATH] = {0};
106 HKEY hKey = HKEY_CURRENT_USER;
107 DWORD Type = REG_DWORD;
108 DWORD Len = sizeof(DWORD);
110 BOOL bWantReal = TRUE;
112 TRACE(L"WantRealVersion() - Enter\n");
114 LONG Result = RegOpenKeyEx(HKEY_CURRENT_USER, TGitRootKey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
115 if (Result == ERROR_SUCCESS)
117 Result = RegQueryValueEx(hKey, ExplorerOnlyValue, nullptr, &Type, (BYTE*)&bExplorerOnly, &Len);
118 if ((Result == ERROR_SUCCESS) && (Type == REG_DWORD) && (Len == sizeof(DWORD)) && bExplorerOnly)
120 TRACE(L"WantRealVersion() - Explorer Only\n");
122 // check if the current process is in fact the explorer
123 Len = GetModuleFileName(nullptr, ModuleName, _countof(ModuleName));
124 if (Len)
126 TRACE(L"Process is %s\n", ModuleName);
128 WCHAR ExplorerPath[MAX_PATH] = {0};
129 Len = ExpandEnvironmentStrings(ExplorerEnvPath, ExplorerPath, _countof(ExplorerPath));
130 if (Len && (Len <= _countof(ExplorerPath)))
132 TRACE(L"Explorer path is %s\n", ExplorerPath);
133 bWantReal = !lstrcmpi(ModuleName, ExplorerPath);
136 // we also have to allow the verclsid.exe process - that process determines
137 // first whether the shell is allowed to even use an extension.
138 Len = lstrlen(ModuleName);
139 if ((Len > wcslen(L"\\verclsid.exe")) && (lstrcmpi(&ModuleName[Len - wcslen(L"\\verclsid.exe")], L"\\verclsid.exe") == 0))
140 bWantReal = TRUE;
144 RegCloseKey(hKey);
147 TRACE(L"WantRealVersion() - Exit\n");
148 return bWantReal;
151 static void LoadRealLibrary(void)
153 static const char GetClassObject[] = "DllGetClassObject";
154 static const char CanUnloadNow[] = "DllCanUnloadNow";
156 WCHAR ModuleName[MAX_PATH] = {0};
157 DWORD Len = 0;
158 HINSTANCE hUseInst = hInst;
159 DebugDllPath[0] = 0;
161 if (hTortoiseGit)
162 return;
164 if (!WantRealVersion())
166 TRACE(L"LoadRealLibrary() - Bypass\n");
167 hTortoiseGit = NIL;
168 return;
170 // if HKCU\Software\TortoiseGit\DebugShell is set, load the dlls from the location of the current process
171 // which is for our debug purposes an instance of usually TortoiseProc. That way we can force the load
172 // of the debug dlls.
173 if (DebugActive())
174 hUseInst = nullptr;
175 Len = GetModuleFileName(hUseInst, ModuleName, _countof(ModuleName));
176 if (!Len)
178 TRACE(L"LoadRealLibrary() - Fail\n");
179 hTortoiseGit = NIL;
180 return;
183 // truncate the string at the last '\' char
184 while(Len > 0)
186 --Len;
187 if (ModuleName[Len] == '\\')
189 ModuleName[Len] = L'\0';
190 break;
193 if (Len == 0)
195 TRACE(L"LoadRealLibrary() - Fail\n");
196 hTortoiseGit = NIL;
197 return;
199 #ifdef _WIN64
200 lstrcat(ModuleName, L"\\TortoiseGit.dll");
201 #else
202 lstrcat(ModuleName, L"\\TortoiseGit32.dll");
203 #endif
204 if (DebugDllPath[0])
205 lstrcpy(ModuleName, DebugDllPath);
206 TRACE(L"LoadRealLibrary() - Load %s\n", ModuleName);
208 hTortoiseGit = LoadLibraryEx(ModuleName, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
209 if (!hTortoiseGit)
211 TRACE(L"LoadRealLibrary() - Fail\n");
212 hTortoiseGit = NIL;
213 return;
216 TRACE(L"LoadRealLibrary() - Success\n");
217 pDllGetClassObject = nullptr;
218 pDllCanUnloadNow = nullptr;
219 pDllGetClassObject = (LPFNGETCLASSOBJECT)GetProcAddress(hTortoiseGit, GetClassObject);
220 if (!pDllGetClassObject)
222 TRACE(L"LoadRealLibrary() - Fail\n");
223 FreeLibrary(hTortoiseGit);
224 hTortoiseGit = NIL;
225 return;
227 pDllCanUnloadNow = (LPFNCANUNLOADNOW)GetProcAddress(hTortoiseGit, CanUnloadNow);
228 if (!pDllCanUnloadNow)
230 TRACE(L"LoadRealLibrary() - Fail\n");
231 FreeLibrary(hTortoiseGit);
232 hTortoiseGit = NIL;
233 return;
237 static void UnloadRealLibrary(void)
239 if (!hTortoiseGit)
240 return;
242 if (hTortoiseGit != NIL)
243 FreeLibrary(hTortoiseGit);
245 hTortoiseGit = nullptr;
246 pDllGetClassObject = nullptr;
247 pDllCanUnloadNow = nullptr;
250 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID /*Reserved*/)
252 #ifdef _DEBUG
253 // if no debugger is present, then don't load the dll.
254 // this prevents other apps from loading the dll and locking
255 // it.
257 BOOL bInShellTest = FALSE;
258 TCHAR buf[MAX_PATH + 1] = {0}; // MAX_PATH ok, the test really is for debugging anyway.
259 DWORD pathLength = GetModuleFileName(nullptr, buf, _countof(buf) - 1);
261 if (pathLength >= 14)
263 if ((lstrcmpi(&buf[pathLength-14], L"\\ShellTest.exe")) == 0)
265 bInShellTest = TRUE;
267 if ((_wcsicmp(&buf[pathLength-13], L"\\verclsid.exe")) == 0)
269 bInShellTest = TRUE;
273 if (!IsDebuggerPresent() && !bInShellTest)
275 TRACE(L"In debug load preventer\n");
276 return FALSE;
278 #endif
280 switch(Reason)
282 case DLL_PROCESS_ATTACH:
283 hInst = hInstance;
284 DebugDllPath[0] = 0;
285 break;
287 /*case DLL_THREAD_ATTACH:
288 break;
290 case DLL_THREAD_DETACH:
291 break;
293 case DLL_PROCESS_DETACH:
294 break;*/
297 return TRUE;
300 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
302 TRACE(L"DllGetClassObject() - Enter\n");
304 LoadRealLibrary();
305 if (!pDllGetClassObject)
307 if (ppv)
308 *ppv = nullptr;
310 TRACE(L"DllGetClassObject() - Bypass\n");
311 return CLASS_E_CLASSNOTAVAILABLE;
314 TRACE(L"DllGetClassObject() - Forward\n");
315 return pDllGetClassObject(rclsid, riid, ppv);
318 STDAPI DllCanUnloadNow(void)
320 TRACE(L"DllCanUnloadNow() - Enter\n");
322 if (pDllCanUnloadNow)
324 TRACE(L"DllCanUnloadNow() - Forward\n");
325 HRESULT Result = pDllCanUnloadNow();
326 if (Result != S_OK)
327 return Result;
330 TRACE(L"DllCanUnloadNow() - Unload\n");
331 UnloadRealLibrary();
332 return S_OK;