| |
2 |
2 |
|
| |
3 |
3 |
|
| |
4 |
4 |
|
| |
5 |
5 |
|
| |
6 |
6 |
|
| |
7 |
7 |
|
| |
8 |
8 |
|
| |
9 |
9 |
|
| |
10 |
10 |
|
| |
11 |
11 |
|
| |
12 |
12 |
|
| |
13 |
13 |
|
| |
14 |
14 |
|
| |
15 |
15 |
|
| |
16 |
16 |
|
| |
17 |
17 |
|
| |
18 |
18 |
|
| |
19 |
19 |
#include "stdafx.h" |
| |
20 |
20 |
#include "TShellExtensionClient.h" |
| |
21 |
21 |
#include "objbase.h" |
| |
|
22 |
#include "../chext/Logger.h" |
| |
22 |
23 |
|
| |
23 |
24 |
#ifdef _DEBUG |
| |
24 |
25 |
#define new DEBUG_NEW |
| |
25 |
26 |
#endif |
| |
26 |
27 |
|
| |
27 |
28 |
TShellExtensionClient::TShellExtensionClient() : |
| |
28 |
29 |
m_piShellExtControl(nullptr), |
| |
29 |
30 |
m_bInitialized(false) |
| |
30 |
31 |
{ |
| |
31 |
32 |
} |
| |
32 |
33 |
|
| |
33 |
34 |
TShellExtensionClient::~TShellExtensionClient() |
| |
34 |
35 |
{ |
| |
35 |
36 |
FreeControlInterface(); |
| |
36 |
37 |
UninitializeCOM(); |
| |
37 |
38 |
} |
| |
38 |
39 |
|
| |
39 |
40 |
HRESULT TShellExtensionClient::InitializeCOM() |
| |
40 |
41 |
{ |
| |
41 |
42 |
if(m_bInitialized) |
| |
42 |
43 |
return S_FALSE; |
| |
43 |
44 |
|
| |
44 |
45 |
HRESULT hResult = CoInitializeEx(nullptr, COINIT_MULTITHREADED); |
| |
45 |
46 |
if(SUCCEEDED(hResult)) |
| |
46 |
47 |
m_bInitialized = true; |
| |
47 |
48 |
else if(hResult == RPC_E_CHANGED_MODE) |
| |
48 |
49 |
return S_FALSE; |
| |
49 |
50 |
|
| |
50 |
51 |
return hResult; |
| |
51 |
52 |
} |
| |
52 |
53 |
|
| |
53 |
54 |
void TShellExtensionClient::UninitializeCOM() |
| |
54 |
55 |
{ |
| |
55 |
56 |
if(m_bInitialized) |
| |
56 |
57 |
{ |
| |
57 |
58 |
CoUninitialize(); |
| |
58 |
59 |
m_bInitialized = false; |
| |
59 |
60 |
} |
| |
60 |
61 |
} |
| |
61 |
62 |
|
| |
62 |
|
HRESULT TShellExtensionClient::RegisterShellExtDll(const CString& strPath, long lClientVersion, long& rlExtensionVersion, CString& rstrExtensionStringVersion) |
| |
|
63 |
bool TShellExtensionClient::DetectRegExe() |
| |
63 |
64 |
{ |
| |
64 |
|
if(strPath.IsEmpty()) |
| |
65 |
|
return E_INVALIDARG; |
| |
|
65 |
const DWORD dwSize = 32768; |
| |
|
66 |
wchar_t szData[dwSize]; |
| |
|
67 |
DWORD dwResult = ::GetModuleFileName(nullptr, szData, dwSize); |
| |
|
68 |
if (dwResult == 0) |
| |
|
69 |
return false; |
| |
|
70 |
szData[dwResult] = L'\0'; |
| |
66 |
71 |
|
| |
67 |
|
HRESULT hResult = S_OK; |
| |
|
72 |
std::wstring wstrDir = szData; |
| |
68 |
73 |
|
| |
69 |
|
if(SUCCEEDED(hResult)) |
| |
70 |
|
hResult = InitializeCOM(); |
| |
|
74 |
size_t stPos = wstrDir.find_last_of(L'\\'); |
| |
|
75 |
if (stPos != std::wstring::npos) |
| |
|
76 |
wstrDir.erase(wstrDir.begin() + stPos + 1, wstrDir.end()); |
| |
71 |
77 |
|
| |
72 |
|
|
| |
73 |
|
if(SUCCEEDED(hResult)) |
| |
74 |
|
FreeControlInterface(); |
| |
|
78 |
#ifdef _WIN64 |
| |
|
79 |
wstrDir += _T("regchext64.exe"); |
| |
|
80 |
#else |
| |
|
81 |
wstrDir += _T("regchext.exe"); |
| |
|
82 |
#endif |
| |
75 |
83 |
|
| |
76 |
|
|
| |
77 |
|
|
| |
78 |
|
if(SUCCEEDED(hResult)) |
| |
|
84 |
m_strRegExe = wstrDir; |
| |
|
85 |
|
| |
|
86 |
return true; |
| |
|
87 |
} |
| |
|
88 |
|
| |
|
89 |
logger::TLoggerPtr& TShellExtensionClient::GetLogger() |
| |
79 |
90 |
{ |
| |
80 |
|
HRESULT (STDAPICALLTYPE *pfn)(void) = nullptr; |
| |
81 |
|
HINSTANCE hMod = LoadLibrary(strPath); |
| |
82 |
|
if(hMod == nullptr) |
| |
83 |
|
hResult = HRESULT_FROM_WIN32(GetLastError()); |
| |
84 |
|
if(SUCCEEDED(hResult) && !hMod) |
| |
85 |
|
hResult = E_FAIL; |
| |
86 |
|
if(SUCCEEDED(hResult)) |
| |
|
91 |
if(!m_spLog) |
| |
|
92 |
m_spLog = logger::MakeLogger(GetLogFileData(), L"ShellExtClient"); |
| |
|
93 |
|
| |
|
94 |
return m_spLog; |
| |
|
95 |
} |
| |
|
96 |
|
| |
|
97 |
ERegistrationResult TShellExtensionClient::RegisterShellExtDll(long lClientVersion, long& rlExtensionVersion, CString& rstrExtensionStringVersion) |
| |
87 |
98 |
{ |
| |
88 |
|
(FARPROC&)pfn = GetProcAddress(hMod, "DllRegisterServer"); |
| |
89 |
|
if(pfn == nullptr) |
| |
90 |
|
hResult = E_FAIL; |
| |
91 |
|
if(SUCCEEDED(hResult)) |
| |
92 |
|
hResult = (*pfn)(); |
| |
|
99 |
LOG_INFO(GetLogger()) << L"Registering shell extension"; |
| |
93 |
100 |
|
| |
94 |
|
FreeLibrary(hMod); |
| |
|
101 |
HRESULT hResult = InitializeCOM(); |
| |
|
102 |
if(FAILED(hResult)) |
| |
|
103 |
{ |
| |
|
104 |
LOG_ERROR(GetLogger()) << L"Failed to initialize COM. Error: " << hResult; |
| |
|
105 |
return eFailure; |
| |
95 |
106 |
} |
| |
|
107 |
|
| |
|
108 |
|
| |
|
109 |
LOG_DEBUG(GetLogger()) << L"Freeing control interface"; |
| |
|
110 |
FreeControlInterface(); |
| |
|
111 |
|
| |
|
112 |
LOG_DEBUG(GetLogger()) << L"Detecting regchext binary"; |
| |
|
113 |
if(!DetectRegExe()) |
| |
|
114 |
{ |
| |
|
115 |
LOG_ERROR(GetLogger()) << L"Failed to detect regchext binary"; |
| |
|
116 |
return eFailure; |
| |
96 |
117 |
} |
| |
97 |
118 |
|
| |
|
119 |
LOG_DEBUG(GetLogger()) << L"Executing regchext binary"; |
| |
98 |
120 |
|
| |
99 |
|
if(SCODE_CODE(hResult) == ERROR_ACCESS_DENIED) |
| |
100 |
|
{ |
| |
101 |
121 |
|
| |
102 |
122 |
SHELLEXECUTEINFO sei; |
| |
103 |
123 |
memset(&sei, 0, sizeof(sei)); |
| |
104 |
124 |
sei.cbSize = sizeof(sei); |
| |
105 |
|
sei.fMask = SEE_MASK_UNICODE; |
| |
|
125 |
sei.fMask = SEE_MASK_UNICODE | SEE_MASK_NOCLOSEPROCESS; |
| |
106 |
126 |
sei.lpVerb = _T("runas"); |
| |
107 |
|
sei.lpFile = _T("regsvr32.exe"); |
| |
108 |
|
CString strParams = CString(_T("/s \"")) + strPath + CString(_T("\"")); |
| |
109 |
|
sei.lpParameters = strParams; |
| |
|
127 |
sei.lpFile = m_strRegExe.c_str(); |
| |
|
128 |
sei.lpParameters = _T(""); |
| |
110 |
129 |
sei.nShow = SW_SHOW; |
| |
111 |
130 |
|
| |
112 |
131 |
if(!ShellExecuteEx(&sei)) |
| |
113 |
|
hResult = E_FAIL; |
| |
114 |
|
else |
| |
115 |
|
hResult = S_OK; |
| |
|
132 |
{ |
| |
|
133 |
DWORD dwLastError = GetLastError(); |
| |
|
134 |
LOG_ERROR(GetLogger()) << L"Failed to execute regchext binary. Error: " << dwLastError; |
| |
|
135 |
return eFailure; |
| |
116 |
136 |
} |
| |
117 |
137 |
|
| |
118 |
|
if(SUCCEEDED(hResult)) |
| |
|
138 |
LOG_DEBUG(GetLogger()) << L"Waiting for registration process to finish"; |
| |
|
139 |
if(SUCCEEDED(hResult) && WaitForSingleObject(sei.hProcess, 10000) != WAIT_OBJECT_0) |
| |
119 |
140 |
{ |
| |
120 |
|
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr); |
| |
|
141 |
DWORD dwLastError = GetLastError(); |
| |
|
142 |
LOG_ERROR(GetLogger()) << L"Waiting failed. Last error: " << dwLastError; |
| |
|
143 |
CloseHandle(sei.hProcess); |
| |
121 |
144 |
|
| |
|
145 |
return eFailure; |
| |
|
146 |
} |
| |
|
147 |
|
| |
|
148 |
DWORD dwExitCode = 0; |
| |
|
149 |
if (!GetExitCodeProcess(sei.hProcess, &dwExitCode)) |
| |
|
150 |
{ |
| |
|
151 |
DWORD dwLastError = GetLastError(); |
| |
|
152 |
LOG_ERROR(GetLogger()) << L"Failed to retrieve process exit code. Last error: " << dwLastError; |
| |
|
153 |
CloseHandle(sei.hProcess); |
| |
|
154 |
|
| |
|
155 |
return eFailure; |
| |
|
156 |
} |
| |
|
157 |
CloseHandle(sei.hProcess); |
| |
|
158 |
|
| |
|
159 |
LOG_INFO(GetLogger()) << L"Registration result: " << dwExitCode; |
| |
|
160 |
|
| |
|
161 |
ERegistrationResult eResult = (ERegistrationResult)dwExitCode; |
| |
|
162 |
|
| |
|
163 |
if(eResult == eSuccess || eResult == eSuccessNative) |
| |
|
164 |
{ |
| |
122 |
165 |
|
| |
123 |
166 |
|
| |
124 |
167 |
|
| |
125 |
168 |
int iTries = 3; |
| |
126 |
169 |
do |
| |
127 |
170 |
{ |
| |
|
171 |
LOG_DEBUG(GetLogger()) << L"Trying to enable native shell extension";; |
| |
128 |
172 |
hResult = EnableExtensionIfCompatible(lClientVersion, rlExtensionVersion, rstrExtensionStringVersion); |
| |
129 |
173 |
if(hResult == REGDB_E_CLASSNOTREG) |
| |
130 |
174 |
{ |
| |
131 |
|
ATLTRACE(_T("Class CLSID_CShellExtControl still not registered...\r\n")); |
| |
|
175 |
LOG_ERROR(GetLogger()) << L"Class CLSID_CShellExtControl still not registered"; |
| |
132 |
176 |
Sleep(500); |
| |
133 |
177 |
} |
| |
134 |
178 |
} |
| |
135 |
179 |
while(--iTries && hResult == REGDB_E_CLASSNOTREG); |
| |
|
180 |
|
| |
|
181 |
if(FAILED(hResult)) |
| |
|
182 |
{ |
| |
|
183 |
LOG_INFO(GetLogger()) << L"Shell Extension requires system restart";; |
| |
|
184 |
eResult = eSuccessNeedRestart; |
| |
136 |
185 |
} |
| |
|
186 |
} |
| |
137 |
187 |
|
| |
138 |
|
return hResult; |
| |
|
188 |
return eResult; |
| |
139 |
189 |
} |
| |
140 |
190 |
|
| |
141 |
|
HRESULT TShellExtensionClient::UnRegisterShellExtDll(const CString& strPath) |
| |
|
191 |
ERegistrationResult TShellExtensionClient::UnRegisterShellExtDll() |
| |
142 |
192 |
{ |
| |
143 |
|
if(strPath.IsEmpty()) |
| |
144 |
|
return E_INVALIDARG; |
| |
|
193 |
LOG_INFO(GetLogger()) << L"Unregistering shell extension"; |
| |
145 |
194 |
|
| |
146 |
|
HRESULT hResult = S_OK; |
| |
|
195 |
HRESULT hResult = InitializeCOM(); |
| |
|
196 |
if(FAILED(hResult)) |
| |
|
197 |
{ |
| |
|
198 |
LOG_ERROR(GetLogger()) << L"Failed to initialize COM. Error: " << hResult; |
| |
|
199 |
return eFailure; |
| |
|
200 |
} |
| |
147 |
201 |
|
| |
148 |
|
if(SUCCEEDED(hResult)) |
| |
149 |
|
hResult = InitializeCOM(); |
| |
150 |
|
|
| |
151 |
|
|
| |
152 |
|
if(SUCCEEDED(hResult)) |
| |
|
202 |
|
| |
|
203 |
LOG_DEBUG(GetLogger()) << L"Freeing control interface"; |
| |
153 |
204 |
FreeControlInterface(); |
| |
154 |
205 |
|
| |
155 |
|
|
| |
156 |
|
|
| |
157 |
|
if(SUCCEEDED(hResult)) |
| |
|
206 |
LOG_DEBUG(GetLogger()) << L"Detecting regchext binary"; |
| |
|
207 |
if(!DetectRegExe()) |
| |
158 |
208 |
{ |
| |
159 |
|
HRESULT (STDAPICALLTYPE *pfn)(void) = nullptr; |
| |
160 |
|
HINSTANCE hMod = LoadLibrary(strPath); |
| |
161 |
|
if(hMod == nullptr) |
| |
162 |
|
hResult = HRESULT_FROM_WIN32(GetLastError()); |
| |
163 |
|
if(SUCCEEDED(hResult) && !hMod) |
| |
164 |
|
hResult = E_FAIL; |
| |
165 |
|
if(SUCCEEDED(hResult)) |
| |
166 |
|
{ |
| |
167 |
|
(FARPROC&)pfn = GetProcAddress(hMod, "DllUnregisterServer"); |
| |
168 |
|
if(pfn == nullptr) |
| |
169 |
|
hResult = E_FAIL; |
| |
170 |
|
if(SUCCEEDED(hResult)) |
| |
171 |
|
hResult = (*pfn)(); |
| |
172 |
|
|
| |
173 |
|
FreeLibrary(hMod); |
| |
|
209 |
LOG_ERROR(GetLogger()) << L"Failed to detect regchext binary"; |
| |
|
210 |
return eFailure; |
| |
174 |
211 |
} |
| |
175 |
|
} |
| |
176 |
212 |
|
| |
|
213 |
LOG_DEBUG(GetLogger()) << L"Executing regchext binary"; |
| |
177 |
214 |
|
| |
178 |
|
if(SCODE_CODE(hResult) == ERROR_ACCESS_DENIED) |
| |
179 |
|
{ |
| |
180 |
215 |
|
| |
181 |
216 |
SHELLEXECUTEINFO sei; |
| |
182 |
217 |
memset(&sei, 0, sizeof(sei)); |
| |
183 |
218 |
sei.cbSize = sizeof(sei); |
| |
184 |
|
sei.fMask = SEE_MASK_UNICODE; |
| |
|
219 |
sei.fMask = SEE_MASK_UNICODE | SEE_MASK_NOCLOSEPROCESS; |
| |
185 |
220 |
sei.lpVerb = _T("runas"); |
| |
186 |
|
sei.lpFile = _T("regsvr32.exe"); |
| |
187 |
|
CString strParams = CString(_T("/u /s \"")) + strPath + CString(_T("\"")); |
| |
188 |
|
sei.lpParameters = strParams; |
| |
|
221 |
sei.lpFile = m_strRegExe.c_str(); |
| |
|
222 |
sei.lpParameters = _T("/u"); |
| |
189 |
223 |
sei.nShow = SW_SHOW; |
| |
190 |
224 |
|
| |
191 |
225 |
if(!ShellExecuteEx(&sei)) |
| |
192 |
|
hResult = E_FAIL; |
| |
193 |
|
else |
| |
194 |
|
hResult = S_OK; |
| |
|
226 |
{ |
| |
|
227 |
DWORD dwLastError = GetLastError(); |
| |
|
228 |
LOG_ERROR(GetLogger()) << L"Failed to execute regchext binary. Error: " << dwLastError; |
| |
|
229 |
return eFailure; |
| |
195 |
230 |
} |
| |
196 |
231 |
|
| |
197 |
|
if(SUCCEEDED(hResult)) |
| |
198 |
|
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr); |
| |
|
232 |
LOG_DEBUG(GetLogger()) << L"Waiting for deregistration process to finish"; |
| |
|
233 |
if(SUCCEEDED(hResult) && WaitForSingleObject(sei.hProcess, 10000) != WAIT_OBJECT_0) |
| |
|
234 |
{ |
| |
|
235 |
DWORD dwLastError = GetLastError(); |
| |
|
236 |
LOG_ERROR(GetLogger()) << L"Waiting failed. Last error: " << dwLastError; |
| |
|
237 |
CloseHandle(sei.hProcess); |
| |
199 |
238 |
|
| |
200 |
|
return hResult; |
| |
|
239 |
return eFailure; |
| |
201 |
240 |
} |
| |
202 |
241 |
|
| |
|
242 |
DWORD dwExitCode = 0; |
| |
|
243 |
if(!GetExitCodeProcess(sei.hProcess, &dwExitCode)) |
| |
|
244 |
{ |
| |
|
245 |
DWORD dwLastError = GetLastError(); |
| |
|
246 |
LOG_ERROR(GetLogger()) << L"Failed to retrieve process exit code. Last error: " << dwLastError; |
| |
|
247 |
CloseHandle(sei.hProcess); |
| |
|
248 |
|
| |
|
249 |
return eFailure; |
| |
|
250 |
} |
| |
|
251 |
CloseHandle(sei.hProcess); |
| |
|
252 |
|
| |
|
253 |
LOG_INFO(GetLogger()) << L"Deregistration result: " << dwExitCode; |
| |
|
254 |
|
| |
|
255 |
return (ERegistrationResult)dwExitCode; |
| |
|
256 |
} |
| |
|
257 |
|
| |
203 |
258 |
HRESULT TShellExtensionClient::EnableExtensionIfCompatible(long lClientVersion, long& rlExtensionVersion, CString& rstrExtensionStringVersion) |
| |
204 |
259 |
{ |
| |
205 |
260 |
rlExtensionVersion = 0; |
| |
206 |
261 |
rstrExtensionStringVersion.Empty(); |
| |
207 |
262 |
|
| |
208 |
263 |
BSTR bstrVersion = nullptr; |
| |
209 |
264 |
|
| |
210 |
265 |
HRESULT hResult = RetrieveControlInterface(); |
| |
211 |
266 |
if(SUCCEEDED(hResult) && !m_piShellExtControl) |
| |
212 |
267 |
hResult = E_FAIL; |
| |
213 |
268 |
if(SUCCEEDED(hResult)) |
| |
214 |
269 |
hResult = m_piShellExtControl->GetVersion(&rlExtensionVersion, &bstrVersion); |
| |
215 |
270 |
if(SUCCEEDED(hResult)) |
| |
216 |
271 |
{ |
| |
217 |
272 |
|
| |
218 |
273 |
bool bVersionMatches = (lClientVersion == rlExtensionVersion); |
| |
219 |
274 |
hResult = m_piShellExtControl->SetFlags(bVersionMatches ? eShellExt_Enabled : 0, eShellExt_Enabled); |
| |
220 |
275 |
if(SUCCEEDED(hResult)) |
| |
221 |
276 |
hResult = bVersionMatches ? S_OK : S_FALSE; |
| |
222 |
277 |
} |