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"
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";
43 HKEY hKey
= HKEY_CURRENT_USER
;
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");
63 Len
= sizeof(wchar_t)*MAX_PATH
;
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");
75 Result
= RegOpenKeyEx(HKEY_LOCAL_MACHINE
, TGitRootKey
, 0, KEY_READ
, &hKey
);
76 if (Result
== ERROR_SUCCESS
)
78 Len
= sizeof(wchar_t)*MAX_PATH
;
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");
87 TRACE(L
"WantRealVersion() - Exit\n");
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
));
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))
147 TRACE(L
"WantRealVersion() - Exit\n");
151 static void LoadRealLibrary(void)
153 static const char GetClassObject
[] = "DllGetClassObject";
154 static const char CanUnloadNow
[] = "DllCanUnloadNow";
156 WCHAR ModuleName
[MAX_PATH
] = {0};
158 HINSTANCE hUseInst
= hInst
;
164 if (!WantRealVersion())
166 TRACE(L
"LoadRealLibrary() - Bypass\n");
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.
175 Len
= GetModuleFileName(hUseInst
, ModuleName
, _countof(ModuleName
));
178 TRACE(L
"LoadRealLibrary() - Fail\n");
183 // truncate the string at the last '\' char
187 if (ModuleName
[Len
] == '\\')
189 ModuleName
[Len
] = L
'\0';
195 TRACE(L
"LoadRealLibrary() - Fail\n");
200 lstrcat(ModuleName
, L
"\\TortoiseGit.dll");
202 lstrcat(ModuleName
, L
"\\TortoiseGit32.dll");
205 lstrcpy(ModuleName
, DebugDllPath
);
206 TRACE(L
"LoadRealLibrary() - Load %s\n", ModuleName
);
208 hTortoiseGit
= LoadLibraryEx(ModuleName
, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH
);
211 TRACE(L
"LoadRealLibrary() - Fail\n");
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
);
227 pDllCanUnloadNow
= (LPFNCANUNLOADNOW
)GetProcAddress(hTortoiseGit
, CanUnloadNow
);
228 if (!pDllCanUnloadNow
)
230 TRACE(L
"LoadRealLibrary() - Fail\n");
231 FreeLibrary(hTortoiseGit
);
237 static void UnloadRealLibrary(void)
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*/)
253 // if no debugger is present, then don't load the dll.
254 // this prevents other apps from loading the dll and locking
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)
267 if ((_wcsicmp(&buf
[pathLength
-13], L
"\\verclsid.exe")) == 0)
273 if (!IsDebuggerPresent() && !bInShellTest
)
275 TRACE(L
"In debug load preventer\n");
282 case DLL_PROCESS_ATTACH
:
287 /*case DLL_THREAD_ATTACH:
290 case DLL_THREAD_DETACH:
293 case DLL_PROCESS_DETACH:
300 STDAPI
DllGetClassObject(REFCLSID rclsid
, REFIID riid
, LPVOID
*ppv
)
302 TRACE(L
"DllGetClassObject() - Enter\n");
305 if (!pDllGetClassObject
)
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();
330 TRACE(L
"DllCanUnloadNow() - Unload\n");