File indexing completed on 2024-04-28 17:00:49
0001 /* 0002 SPDX-FileCopyrightText: 2003-2006, Sergey Zorin. All rights reserved. 0003 SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com 0004 SPDX-License-Identifier: BSD-2-Clause 0005 */ 0006 0007 #define _CRT_NON_CONFORMING_SWPRINTFS 0008 0009 #include "server.h" 0010 0011 #include <stdio.h> 0012 0013 #include <tchar.h> 0014 0015 #include <shlguid.h> 0016 #include <olectl.h> 0017 #include <objidl.h> 0018 0019 #include <objbase.h> 0020 #include <initguid.h> 0021 0022 #include <KLocalizedString> 0023 0024 #include "class_factory.h" 0025 0026 #define DllExport __declspec( dllexport ) 0027 0028 // registry key util struct 0029 struct REGSTRUCT { 0030 LPCTSTR subkey; 0031 LPCTSTR name; 0032 LPCTSTR value; 0033 }; 0034 0035 SERVER* SERVER::_instance = 0; 0036 static HINSTANCE server_instance; // Handle to this DLL itself. 0037 0038 //DEFINE_GUID(CLSID_DIFF_EXT, 0xA0482097, 0xC69D, 0x4DEC, 0x8A, 0xB6, 0xD3, 0xA2, 0x59, 0xAC, 0xC1, 0x51); 0039 // New class id for DIFF_EXT for KDiff3 0040 #ifdef Q_OS_WIN64 0041 // {34471FFB-4002-438b-8952-E4588D0C0FE9} 0042 DEFINE_GUID( CLSID_DIFF_EXT, 0x34471FFB, 0x4002, 0x438b, 0x89, 0x52, 0xE4, 0x58, 0x8D, 0x0C, 0x0F, 0xE9 ); 0043 #else 0044 #error unsupported configuration 0045 #endif 0046 0047 tstring SERVER::getRegistryKeyString( const tstring& subKey, const tstring& value ) 0048 { 0049 tstring keyName = m_registryBaseName; 0050 if (!subKey.empty()) 0051 keyName += TEXT("\\")+subKey; 0052 0053 HKEY key; 0054 HKEY baseKey = HKEY_CURRENT_USER; 0055 tstring result; 0056 for(;;) 0057 { 0058 if( RegOpenKeyEx( baseKey, keyName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &key ) == ERROR_SUCCESS ) 0059 { 0060 DWORD neededSizeInBytes = 0; 0061 if (RegQueryValueEx(key, value.c_str(), 0, 0, 0, &neededSizeInBytes) == ERROR_SUCCESS) 0062 { 0063 DWORD length = neededSizeInBytes / sizeof( TCHAR ); 0064 result.resize( length ); 0065 if ( RegQueryValueEx( key, value.c_str(), 0, 0, (LPBYTE)&result[0], &neededSizeInBytes ) == ERROR_SUCCESS) 0066 { 0067 //Everything is ok, but we want to cut off the terminating 0-character 0068 result.resize( length - 1 ); 0069 RegCloseKey(key); 0070 return result; 0071 } 0072 else 0073 { 0074 result.resize(0); 0075 } 0076 } 0077 0078 RegCloseKey(key); 0079 } 0080 if (baseKey==HKEY_LOCAL_MACHINE) 0081 break; 0082 baseKey = HKEY_LOCAL_MACHINE; 0083 } 0084 0085 // Error 0086 { 0087 LPTSTR message; 0088 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, 0089 GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &message, 0, 0); 0090 ERRORLOG( (tstring(TEXT("RegOpenKeyEx: ")+keyName+TEXT("->")+value) + TEXT(": ")) + message ); \ 0091 LocalFree(message); 0092 } 0093 return result; 0094 } 0095 0096 0097 STDAPI 0098 DllCanUnloadNow(void) { 0099 HRESULT ret = S_FALSE; 0100 0101 if(SERVER::instance()->reference_count() == 0) { 0102 ret = S_OK; 0103 } 0104 0105 return ret; 0106 } 0107 0108 extern "C" int APIENTRY 0109 DllMain(HINSTANCE instance, DWORD reason, LPVOID /* reserved */) { 0110 // char str[1024]; 0111 // char* reason_string[] = {"DLL_PROCESS_DETACH", "DLL_PROCESS_ATTACH", "DLL_THREAD_ATTACH", "DLL_THREAD_DETACH"}; 0112 // sprintf(str, "instance: %x; reason: '%s'", instance, reason_string[reason]); 0113 // MessageBox(0, str, TEXT("Info"), MB_OK); 0114 switch (reason) { 0115 case DLL_PROCESS_ATTACH: 0116 server_instance = instance; 0117 SERVER::instance()->save_history(); 0118 MESSAGELOG(TEXT("DLL_PROCESS_ATTACH")); 0119 break; 0120 0121 case DLL_PROCESS_DETACH: 0122 MESSAGELOG(TEXT("DLL_PROCESS_DETACH")); 0123 SERVER::instance()->save_history(); 0124 break; 0125 } 0126 0127 return 1; 0128 } 0129 0130 STDAPI 0131 DllGetClassObject(REFCLSID rclsid, REFIID riid, void** class_object) { 0132 HRESULT ret = CLASS_E_CLASSNOTAVAILABLE; 0133 *class_object = 0; 0134 0135 if (IsEqualIID(rclsid, CLSID_DIFF_EXT)) { 0136 CLASS_FACTORY* pcf = new CLASS_FACTORY(); 0137 0138 ret = pcf->QueryInterface(riid, class_object); 0139 } 0140 0141 return ret; 0142 } 0143 0144 /*extern "C" HRESULT STDAPICALLTYPE*/ STDAPI 0145 DllRegisterServer() { 0146 return SERVER::instance()->do_register(); 0147 } 0148 0149 STDAPI 0150 DllUnregisterServer() { 0151 return SERVER::instance()->do_unregister(); 0152 } 0153 0154 SERVER* SERVER::instance() 0155 { 0156 if(_instance == nullptr) 0157 { 0158 _instance = new SERVER(); 0159 _instance->initLogging(); 0160 MESSAGELOG(TEXT("New Server instance")); 0161 } 0162 0163 return _instance; 0164 } 0165 0166 SERVER::SERVER() : _reference_count(0) 0167 { 0168 m_registryBaseName = TEXT("Software\\KDE e.V.\\KDiff3\\diff-ext"); 0169 m_pRecentFiles = 0; 0170 m_pLogFile = 0; 0171 } 0172 0173 void SERVER::initLogging() 0174 { 0175 tstring logFileName = getRegistryKeyString( TEXT(""), TEXT("LogFile") ); 0176 if ( !logFileName.empty() ) 0177 { 0178 m_pLogFile = _tfopen( logFileName.c_str(), TEXT("a+, ccs=UTF-8") ); 0179 if (m_pLogFile) 0180 { 0181 _ftprintf( m_pLogFile, TEXT("\nSERVER::SERVER()\n") ); 0182 } 0183 } 0184 } 0185 0186 SERVER::~SERVER() 0187 { 0188 if ( m_pLogFile ) 0189 { 0190 _ftprintf( m_pLogFile, TEXT("SERVER::~SERVER()\n\n") ); 0191 fclose( m_pLogFile ); 0192 } 0193 0194 delete m_pRecentFiles; 0195 } 0196 0197 HINSTANCE 0198 SERVER::handle() const 0199 { 0200 return server_instance; 0201 } 0202 0203 void 0204 SERVER::lock() { 0205 InterlockedIncrement(&_reference_count); 0206 } 0207 0208 void 0209 SERVER::release() { 0210 InterlockedDecrement(&_reference_count); 0211 0212 //if(InterlockedDecrement((LPLONG)&_reference_count) == 0) 0213 // delete this; 0214 } 0215 0216 void SERVER::logMessage( const char* function, const char* file, int line, const tstring& msg ) 0217 { 0218 SERVER* pServer = SERVER::instance(); 0219 if ( pServer && pServer->m_pLogFile ) 0220 { 0221 SYSTEMTIME st; 0222 GetSystemTime( &st ); 0223 _ftprintf( pServer->m_pLogFile, TEXT("%04d/%02d/%02d %02d:%02d:%02d ") 0224 TEXT("%S (%S:%d) %s\n"), // integrate char-string into wchar_t string 0225 st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, function, file, line, msg.c_str() ); 0226 fflush(pServer->m_pLogFile); 0227 } 0228 } 0229 0230 std::list<tstring>& 0231 SERVER::recent_files() 0232 { 0233 LOG(); 0234 if ( m_pRecentFiles==0 ) 0235 { 0236 m_pRecentFiles = new std::list<tstring>; 0237 } 0238 else 0239 { 0240 m_pRecentFiles->clear(); 0241 } 0242 MESSAGELOG(TEXT("Reading history from registry...")); 0243 for( int i=0; i<32; ++i ) // Max history size 0244 { 0245 TCHAR numAsString[10]; 0246 _sntprintf( numAsString, 10, TEXT("%d"), i ); 0247 tstring historyItem = getRegistryKeyString( TEXT("history"), numAsString ); 0248 if ( ! historyItem.empty() ) 0249 m_pRecentFiles->push_back( historyItem ); 0250 } 0251 return *m_pRecentFiles; 0252 } 0253 0254 void 0255 SERVER::save_history() const 0256 { 0257 if( m_pRecentFiles ) 0258 { 0259 HKEY key; 0260 if( RegCreateKeyEx(HKEY_CURRENT_USER, (m_registryBaseName + TEXT("\\history")).c_str(), 0, 0, 0261 REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_WOW64_64KEY, 0, &key, 0) == ERROR_SUCCESS ) 0262 { 0263 LOG(); 0264 //DWORD len = MAX_PATH; 0265 int n = 0; 0266 0267 std::list<tstring>::const_iterator i; 0268 0269 for(i = m_pRecentFiles->begin(); i!=m_pRecentFiles->end(); ++i, ++n ) 0270 { 0271 tstring str = *i; 0272 TCHAR numAsString[10]; 0273 _sntprintf( numAsString, 10, TEXT("%d"), n ); 0274 if(RegSetValueEx(key, numAsString, 0, REG_SZ, (const BYTE*)str.c_str(), (DWORD)(str.size()+1)*sizeof(TCHAR) ) != ERROR_SUCCESS) 0275 { 0276 LPTSTR message; 0277 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, 0278 GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language 0279 (LPTSTR) &message, 0, 0); 0280 MessageBox(0, message, TEXT("KDiff3-diff-ext: Save history failed"), MB_OK | MB_ICONINFORMATION); 0281 LocalFree(message); 0282 } 0283 } 0284 for(; n<32; ++n ) 0285 { 0286 TCHAR numAsString[10]; 0287 _sntprintf( numAsString, 10, TEXT("%d"), n ); 0288 RegDeleteValue(key, numAsString ); 0289 } 0290 0291 RegCloseKey(key); 0292 } 0293 else 0294 { 0295 SYSERRORLOG(TEXT("RegOpenKeyEx")); 0296 } 0297 } 0298 } 0299 0300 HRESULT 0301 SERVER::do_register() { 0302 LOG(); 0303 TCHAR class_id[MAX_PATH]; 0304 LPWSTR tmp_guid; 0305 HRESULT ret = SELFREG_E_CLASS; 0306 0307 if (StringFromIID(CLSID_DIFF_EXT, &tmp_guid) == S_OK) { 0308 _tcsncpy(class_id, tmp_guid, MAX_PATH); 0309 0310 CoTaskMemFree((void*)tmp_guid); 0311 0312 TCHAR subkey[MAX_PATH]; 0313 TCHAR server_path[MAX_PATH]; 0314 HKEY key; 0315 LRESULT result = NOERROR; 0316 DWORD dwDisp; 0317 0318 GetModuleFileName(SERVER::instance()->handle(), server_path, MAX_PATH); 0319 0320 REGSTRUCT entry[] = { 0321 {TEXT("Software\\Classes\\CLSID\\%s"), 0, TEXT("kdiff3ext")}, 0322 {TEXT("Software\\Classes\\CLSID\\%s\\InProcServer32"), 0, TEXT("%s")}, 0323 {TEXT("Software\\Classes\\CLSID\\%s\\InProcServer32"), TEXT("ThreadingModel"), TEXT("Apartment")} 0324 }; 0325 0326 for(unsigned int i = 0; (i < sizeof(entry)/sizeof(entry[0])) && (result == NOERROR); i++) { 0327 _sntprintf(subkey, MAX_PATH, entry[i].subkey, class_id); 0328 result = RegCreateKeyEx(HKEY_CURRENT_USER, subkey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &dwDisp); 0329 0330 if(result == NOERROR) { 0331 TCHAR szData[MAX_PATH]; 0332 0333 _sntprintf(szData, MAX_PATH, entry[i].value, server_path); 0334 szData[MAX_PATH-1]=0; 0335 0336 result = RegSetValueEx(key, entry[i].name, 0, REG_SZ, (LPBYTE)szData, DWORD(_tcslen(szData)*sizeof(TCHAR))); 0337 } 0338 0339 RegCloseKey(key); 0340 } 0341 0342 if(result == NOERROR) { 0343 result = RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Classes\\*\\shellex\\ContextMenuHandlers\\kdiff3ext"), 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &dwDisp); 0344 0345 if(result == NOERROR) { 0346 0347 result = RegSetValueEx(key, 0, 0, REG_SZ, (LPBYTE)class_id, DWORD(_tcslen(class_id)*sizeof(TCHAR))); 0348 0349 RegCloseKey(key); 0350 0351 // NT needs to have shell extensions "approved". 0352 result = RegCreateKeyEx(HKEY_CURRENT_USER, 0353 TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"), 0354 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &dwDisp); 0355 0356 if(result == NOERROR) { 0357 TCHAR szData[MAX_PATH]; 0358 0359 lstrcpy(szData, TEXT("diff-ext")); 0360 0361 result = RegSetValueEx(key, class_id, 0, REG_SZ, (LPBYTE)szData, DWORD(_tcslen(szData)*sizeof(TCHAR))); 0362 0363 RegCloseKey(key); 0364 0365 ret = S_OK; 0366 } else if (result == ERROR_ACCESS_DENIED) { 0367 TCHAR msg[] = TEXT("Warning! You have unsufficient rights to write to a specific registry key.\n") 0368 TEXT("The application may work anyway, but it is advised to register this module ") 0369 TEXT("again while having administrator rights."); 0370 0371 MessageBox(0, msg, TEXT("Warning"), MB_ICONEXCLAMATION); 0372 0373 ret = S_OK; 0374 } 0375 } 0376 } 0377 } 0378 0379 return ret; 0380 } 0381 0382 HRESULT 0383 SERVER::do_unregister() { 0384 LOG(); 0385 TCHAR class_id[MAX_PATH]; 0386 LPWSTR tmp_guid; 0387 HRESULT ret = SELFREG_E_CLASS; 0388 0389 if (StringFromIID(CLSID_DIFF_EXT, &tmp_guid) == S_OK) { 0390 _tcsncpy(class_id, tmp_guid, MAX_PATH); 0391 0392 CoTaskMemFree((void*)tmp_guid); 0393 0394 LRESULT result = NOERROR; 0395 TCHAR subkey[MAX_PATH]; 0396 0397 REGSTRUCT entry[] = { 0398 {TEXT("Software\\Classes\\CLSID\\%s\\InProcServer32"), 0, 0}, 0399 {TEXT("Software\\Classes\\CLSID\\%s"), 0, 0} 0400 }; 0401 0402 for(unsigned int i = 0; (i < sizeof(entry)/sizeof(entry[0])) && (result == NOERROR); i++) { 0403 _stprintf(subkey, entry[i].subkey, class_id); 0404 result = RegDeleteKey(HKEY_CURRENT_USER, subkey); 0405 } 0406 0407 if(result == NOERROR) { 0408 result = RegDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Classes\\*\\shellex\\ContextMenuHandlers\\kdiff3ext")); 0409 0410 if(result == NOERROR) { 0411 // NT needs to have shell extensions "approved". 0412 HKEY key; 0413 0414 RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"), 0, KEY_ALL_ACCESS, &key); 0415 0416 result = RegDeleteValue(key, class_id); 0417 0418 RegCloseKey(key); 0419 0420 if(result == ERROR_SUCCESS) { 0421 ret = S_OK; 0422 } 0423 } 0424 } 0425 } 0426 0427 return ret; 0428 }