joachim99@75: /* joachim99@75: * Copyright (c) 2003-2005, Sergey Zorin. All rights reserved. joachim99@75: * joachim99@75: * This software is distributable under the BSD license. See the terms joachim99@75: * of the BSD license in the LICENSE file provided with this software. joachim99@75: * joachim99@75: */ joachim99@75: joachim99@75: #define _CRT_NON_CONFORMING_SWPRINTFS joachim99@75: #define _CRT_SECURE_NO_DEPRECATE joachim99@75: joachim99@75: #include joachim99@75: joachim99@75: #include joachim99@75: #include joachim99@75: joachim99@75: #include joachim99@75: #include joachim99@75: #include joachim99@75: joachim99@75: #include joachim99@75: #include joachim99@75: joachim99@75: //#include joachim99@75: //#include joachim99@75: //#include joachim99@75: //#include joachim99@75: joachim99@75: #include "server.h" joachim99@75: #include "class_factory.h" joachim99@75: joachim99@75: #define DllExport __declspec( dllexport ) joachim99@75: joachim99@75: // registry key util struct joachim99@75: struct REGSTRUCT { joachim99@94: LPCTSTR subkey; joachim99@94: LPCTSTR name; joachim99@94: LPCTSTR value; joachim99@75: }; joachim99@75: joachim99@75: SERVER* SERVER::_instance = 0; joachim99@75: static HINSTANCE server_instance; // Handle to this DLL itself. joachim99@75: joachim99@75: //DEFINE_GUID(CLSID_DIFF_EXT, 0xA0482097, 0xC69D, 0x4DEC, 0x8A, 0xB6, 0xD3, 0xA2, 0x59, 0xAC, 0xC1, 0x51); joachim99@75: // New class id for DIFF_EXT for KDiff3 joachim99@94: #ifdef _WIN64 joachim99@94: // {34471FFB-4002-438b-8952-E4588D0C0FE9} joachim99@94: DEFINE_GUID( CLSID_DIFF_EXT, 0x34471FFB, 0x4002, 0x438b, 0x89, 0x52, 0xE4, 0x58, 0x8D, 0x0C, 0x0F, 0xE9 ); joachim99@94: #else joachim99@75: DEFINE_GUID( CLSID_DIFF_EXT, 0x9f8528e4, 0xab20, 0x456e, 0x84, 0xe5, 0x3c, 0xe6, 0x9d, 0x87, 0x20, 0xf3 ); joachim99@94: #endif joachim99@75: joachim99@75: tstring SERVER::getRegistryKeyString( const tstring& subKey, const tstring& value ) joachim99@75: { joachim99@75: tstring keyName = m_registryBaseName; joachim99@75: if (!subKey.empty()) joachim99@75: keyName += TEXT("\\")+subKey; joachim99@75: joachim99@75: HKEY key; joachim99@75: HKEY baseKey = HKEY_CURRENT_USER; joachim99@75: tstring result; joachim99@75: for(;;) joachim99@75: { joachim99@75: if( RegOpenKeyEx( baseKey, keyName.c_str(), 0, KEY_READ, &key ) == ERROR_SUCCESS ) joachim99@75: { joachim99@75: DWORD neededSizeInBytes = 0; joachim99@75: if (RegQueryValueEx(key, value.c_str(), 0, 0, 0, &neededSizeInBytes) == ERROR_SUCCESS) joachim99@75: { joachim99@75: DWORD length = neededSizeInBytes / sizeof( TCHAR ); joachim99@75: result.resize( length ); joachim99@75: if ( RegQueryValueEx( key, value.c_str(), 0, 0, (LPBYTE)&result[0], &neededSizeInBytes ) == ERROR_SUCCESS) joachim99@75: { joachim99@75: //Everything is ok, but we want to cut off the terminating 0-character joachim99@75: result.resize( length - 1 ); joachim99@75: RegCloseKey(key); joachim99@75: return result; joachim99@75: } joachim99@75: else joachim99@75: { joachim99@75: result.resize(0); joachim99@75: } joachim99@75: } joachim99@75: joachim99@75: RegCloseKey(key); joachim99@75: } joachim99@75: if (baseKey==HKEY_LOCAL_MACHINE) joachim99@75: break; joachim99@75: baseKey = HKEY_LOCAL_MACHINE; joachim99@75: } joachim99@75: joachim99@75: // Error joachim99@75: { joachim99@75: LPTSTR message; joachim99@75: FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, joachim99@75: GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &message, 0, 0); joachim99@75: ERRORLOG( (tstring(TEXT("RegOpenKeyEx: ")+keyName+TEXT("->")+value) + TEXT(": ")) + message ); \ joachim99@75: LocalFree(message); joachim99@75: } joachim99@75: return result; joachim99@75: } joachim99@75: joachim99@75: joachim99@75: STDAPI joachim99@75: DllCanUnloadNow(void) { joachim99@75: HRESULT ret = S_FALSE; joachim99@75: joachim99@75: if(SERVER::instance()->reference_count() == 0) { joachim99@75: ret = S_OK; joachim99@75: } joachim99@75: joachim99@75: return ret; joachim99@75: } joachim99@75: joachim99@75: extern "C" int APIENTRY joachim99@75: DllMain(HINSTANCE instance, DWORD reason, LPVOID /* reserved */) { joachim99@75: // char str[1024]; joachim99@75: // char* reason_string[] = {"DLL_PROCESS_DETACH", "DLL_PROCESS_ATTACH", "DLL_THREAD_ATTACH", "DLL_THREAD_DETACH"}; joachim99@75: // sprintf(str, "instance: %x; reason: '%s'", instance, reason_string[reason]); joachim99@75: // MessageBox(0, str, TEXT("Info"), MB_OK); joachim99@75: switch (reason) { joachim99@75: case DLL_PROCESS_ATTACH: joachim99@75: server_instance = instance; joachim99@75: SERVER::instance()->save_history(); joachim99@75: MESSAGELOG(TEXT("DLL_PROCESS_ATTACH")); joachim99@75: break; joachim99@75: joachim99@75: case DLL_PROCESS_DETACH: joachim99@75: MESSAGELOG(TEXT("DLL_PROCESS_DETACH")); joachim99@75: SERVER::instance()->save_history(); joachim99@75: break; joachim99@75: } joachim99@75: joachim99@75: return 1; joachim99@75: } joachim99@75: joachim99@75: STDAPI joachim99@75: DllGetClassObject(REFCLSID rclsid, REFIID riid, void** class_object) { joachim99@75: HRESULT ret = CLASS_E_CLASSNOTAVAILABLE; joachim99@75: *class_object = 0; joachim99@75: joachim99@75: if (IsEqualIID(rclsid, CLSID_DIFF_EXT)) { joachim99@75: CLASS_FACTORY* pcf = new CLASS_FACTORY(); joachim99@75: joachim99@75: ret = pcf->QueryInterface(riid, class_object); joachim99@75: } joachim99@75: joachim99@75: return ret; joachim99@75: } joachim99@75: joachim99@75: /*extern "C" HRESULT STDAPICALLTYPE*/ STDAPI joachim99@75: DllRegisterServer() { joachim99@75: return SERVER::instance()->do_register(); joachim99@75: } joachim99@75: joachim99@75: STDAPI joachim99@75: DllUnregisterServer() { joachim99@75: return SERVER::instance()->do_unregister(); joachim99@75: } joachim99@75: joachim99@75: SERVER* SERVER::instance() joachim99@75: { joachim99@75: if(_instance == 0) joachim99@75: { joachim99@75: _instance = new SERVER(); joachim99@75: _instance->initLogging(); joachim99@75: MESSAGELOG(TEXT("New Server instance")); joachim99@75: } joachim99@75: joachim99@75: return _instance; joachim99@75: } joachim99@75: joachim99@75: SERVER::SERVER() : _reference_count(0) joachim99@75: { joachim99@75: m_registryBaseName = TEXT("Software\\KDiff3\\diff-ext"); joachim99@75: m_pRecentFiles = 0; joachim99@75: m_pLogFile = 0; joachim99@75: } joachim99@75: joachim99@75: void SERVER::initLogging() joachim99@75: { joachim99@75: tstring logFileName = getRegistryKeyString( TEXT(""), TEXT("LogFile") ); joachim99@75: if ( !logFileName.empty() ) joachim99@75: { joachim99@75: m_pLogFile = _tfopen( logFileName.c_str(), TEXT("a+, ccs=UTF-8") ); joachim99@75: if (m_pLogFile) joachim99@75: { joachim99@75: _ftprintf( m_pLogFile, TEXT("\nSERVER::SERVER()\n") ); joachim99@75: } joachim99@75: } joachim99@75: } joachim99@75: joachim99@75: SERVER::~SERVER() joachim99@75: { joachim99@75: if ( m_pLogFile ) joachim99@75: { joachim99@75: _ftprintf( m_pLogFile, TEXT("SERVER::~SERVER()\n\n") ); joachim99@75: fclose( m_pLogFile ); joachim99@75: } joachim99@75: joachim99@75: delete m_pRecentFiles; joachim99@75: } joachim99@75: joachim99@75: HINSTANCE joachim99@75: SERVER::handle() const joachim99@75: { joachim99@75: return server_instance; joachim99@75: } joachim99@75: joachim99@75: void joachim99@75: SERVER::lock() { joachim99@75: InterlockedIncrement(&_reference_count); joachim99@75: } joachim99@75: joachim99@75: void joachim99@75: SERVER::release() { joachim99@75: InterlockedDecrement(&_reference_count); joachim99@75: joachim99@75: //if(InterlockedDecrement((LPLONG)&_reference_count) == 0) joachim99@75: // delete this; joachim99@75: } joachim99@75: joachim99@75: void SERVER::logMessage( const char* function, const char* file, int line, const tstring& msg ) joachim99@75: { joachim99@75: SERVER* pServer = SERVER::instance(); joachim99@75: if ( pServer && pServer->m_pLogFile ) joachim99@75: { joachim99@75: SYSTEMTIME st; joachim99@75: GetSystemTime( &st ); joachim99@75: _ftprintf( pServer->m_pLogFile, TEXT("%04d/%02d/%02d %02d:%02d:%02d ") joachim99@75: #ifdef UNICODE joachim99@75: TEXT("%S (%S:%d) %s\n"), // integrate char-string into wchar_t string joachim99@75: #else joachim99@75: TEXT("%s (%s:%d) %s\n"), joachim99@75: #endif joachim99@75: st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, function, file, line, msg.c_str() ); joachim99@75: fflush(pServer->m_pLogFile); joachim99@75: } joachim99@75: } joachim99@75: joachim99@75: std::list& joachim99@75: SERVER::recent_files() joachim99@75: { joachim99@75: LOG(); joachim99@75: if ( m_pRecentFiles==0 ) joachim99@75: { joachim99@75: MESSAGELOG(TEXT("Reading history from registry...")); joachim99@75: m_pRecentFiles = new std::list; joachim99@75: for( int i=0; i<32; ++i ) // Max history size joachim99@75: { joachim99@75: TCHAR numAsString[10]; joachim99@75: _sntprintf( numAsString, 10, TEXT("%d"), i ); joachim99@75: tstring historyItem = getRegistryKeyString( TEXT("history"), numAsString ); joachim99@75: if ( ! historyItem.empty() ) joachim99@75: m_pRecentFiles->push_back( historyItem ); joachim99@75: } joachim99@75: } joachim99@75: return *m_pRecentFiles; joachim99@75: } joachim99@75: joachim99@75: void joachim99@75: SERVER::save_history() const joachim99@75: { joachim99@75: if( m_pRecentFiles && !m_pRecentFiles->empty() ) joachim99@75: { joachim99@75: HKEY key; joachim99@75: if( RegCreateKeyEx(HKEY_CURRENT_USER, (m_registryBaseName + TEXT("\\history")).c_str(), 0, 0, joachim99@75: REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, 0) == ERROR_SUCCESS ) joachim99@75: { joachim99@75: LOG(); joachim99@75: //DWORD len = MAX_PATH; joachim99@75: int n = 0; joachim99@75: joachim99@75: std::list::const_iterator i; joachim99@75: joachim99@75: for(i = m_pRecentFiles->begin(); i!=m_pRecentFiles->end(); ++i, ++n ) joachim99@75: { joachim99@75: tstring str = *i; joachim99@75: TCHAR numAsString[10]; joachim99@75: _sntprintf( numAsString, 10, TEXT("%d"), n ); joachim99@75: if(RegSetValueEx(key, numAsString, 0, REG_SZ, (const BYTE*)str.c_str(), (DWORD)(str.size()+1)*sizeof(TCHAR) ) != ERROR_SUCCESS) joachim99@75: { joachim99@75: LPTSTR message; joachim99@75: FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, joachim99@75: GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language joachim99@75: (LPTSTR) &message, 0, 0); joachim99@75: MessageBox(0, message, TEXT("Save history failed"), MB_OK | MB_ICONINFORMATION); joachim99@75: LocalFree(message); joachim99@75: } joachim99@75: } joachim99@75: joachim99@75: RegCloseKey(key); joachim99@75: } joachim99@75: else joachim99@75: { joachim99@75: SYSERRORLOG(TEXT("RegOpenKeyEx")); joachim99@75: } joachim99@75: } joachim99@75: } joachim99@75: joachim99@75: HRESULT joachim99@75: SERVER::do_register() { joachim99@75: LOG(); joachim99@75: TCHAR class_id[MAX_PATH]; joachim99@75: LPWSTR tmp_guid; joachim99@75: HRESULT ret = SELFREG_E_CLASS; joachim99@75: joachim99@75: if (StringFromIID(CLSID_DIFF_EXT, &tmp_guid) == S_OK) { joachim99@75: #ifdef UNICODE joachim99@75: _tcsncpy(class_id, tmp_guid, MAX_PATH); joachim99@75: #else joachim99@75: wcstombs(class_id, tmp_guid, MAX_PATH); joachim99@75: #endif joachim99@75: CoTaskMemFree((void*)tmp_guid); joachim99@75: joachim99@75: TCHAR subkey[MAX_PATH]; joachim99@75: TCHAR server_path[MAX_PATH]; joachim99@75: HKEY key; joachim99@75: LRESULT result = NOERROR; joachim99@75: DWORD dwDisp; joachim99@75: joachim99@75: GetModuleFileName(SERVER::instance()->handle(), server_path, MAX_PATH); joachim99@75: joachim99@75: REGSTRUCT entry[] = { joachim99@75: {TEXT("Software\\Classes\\CLSID\\%s"), 0, TEXT("diff-ext-for-kdiff3")}, joachim99@75: {TEXT("Software\\Classes\\CLSID\\%s\\InProcServer32"), 0, TEXT("%s")}, joachim99@75: {TEXT("Software\\Classes\\CLSID\\%s\\InProcServer32"), TEXT("ThreadingModel"), TEXT("Apartment")} joachim99@75: }; joachim99@75: joachim99@75: for(unsigned int i = 0; (i < sizeof(entry)/sizeof(entry[0])) && (result == NOERROR); i++) { joachim99@75: _sntprintf(subkey, MAX_PATH, entry[i].subkey, class_id); joachim99@75: result = RegCreateKeyEx(HKEY_CURRENT_USER, subkey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &dwDisp); joachim99@75: joachim99@75: if(result == NOERROR) { joachim99@75: TCHAR szData[MAX_PATH]; joachim99@75: joachim99@75: _sntprintf(szData, MAX_PATH, entry[i].value, server_path); joachim99@75: szData[MAX_PATH-1]=0; joachim99@75: joachim99@75: result = RegSetValueEx(key, entry[i].name, 0, REG_SZ, (LPBYTE)szData, DWORD(_tcslen(szData)*sizeof(TCHAR))); joachim99@75: } joachim99@75: joachim99@75: RegCloseKey(key); joachim99@75: } joachim99@75: joachim99@75: if(result == NOERROR) { joachim99@75: result = RegCreateKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Classes\\*\\shellex\\ContextMenuHandlers\\diff-ext-for-kdiff3"), 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &dwDisp); joachim99@75: joachim99@75: if(result == NOERROR) { joachim99@75: joachim99@75: result = RegSetValueEx(key, 0, 0, REG_SZ, (LPBYTE)class_id, DWORD(_tcslen(class_id)*sizeof(TCHAR))); joachim99@75: joachim99@75: RegCloseKey(key); joachim99@75: joachim99@75: //If running on NT, register the extension as approved. joachim99@75: OSVERSIONINFO osvi; joachim99@75: joachim99@75: osvi.dwOSVersionInfoSize = sizeof(osvi); joachim99@75: GetVersionEx(&osvi); joachim99@75: joachim99@75: // NT needs to have shell extensions "approved". joachim99@75: if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) { joachim99@75: result = RegCreateKeyEx(HKEY_CURRENT_USER, joachim99@75: TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"), joachim99@75: 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &dwDisp); joachim99@75: joachim99@75: if(result == NOERROR) { joachim99@75: TCHAR szData[MAX_PATH]; joachim99@75: joachim99@75: lstrcpy(szData, TEXT("diff-ext")); joachim99@75: joachim99@75: result = RegSetValueEx(key, class_id, 0, REG_SZ, (LPBYTE)szData, DWORD(_tcslen(szData)*sizeof(TCHAR))); joachim99@75: joachim99@75: RegCloseKey(key); joachim99@75: joachim99@75: ret = S_OK; joachim99@75: } else if (result == ERROR_ACCESS_DENIED) { joachim99@75: TCHAR msg[] = TEXT("Warning! You have unsufficient rights to write to a specific registry key.\n") joachim99@75: TEXT("The application may work anyway, but it is advised to register this module ") joachim99@75: TEXT("again while having administrator rights."); joachim99@75: joachim99@75: MessageBox(0, msg, TEXT("Warning"), MB_ICONEXCLAMATION); joachim99@75: joachim99@75: ret = S_OK; joachim99@75: } joachim99@75: } joachim99@75: else { joachim99@75: ret = S_OK; joachim99@75: } joachim99@75: } joachim99@75: } joachim99@75: } joachim99@75: joachim99@75: return ret; joachim99@75: } joachim99@75: joachim99@75: HRESULT joachim99@75: SERVER::do_unregister() { joachim99@75: LOG(); joachim99@75: TCHAR class_id[MAX_PATH]; joachim99@75: LPWSTR tmp_guid; joachim99@75: HRESULT ret = SELFREG_E_CLASS; joachim99@75: joachim99@75: if (StringFromIID(CLSID_DIFF_EXT, &tmp_guid) == S_OK) { joachim99@75: #ifdef UNICODE joachim99@75: _tcsncpy(class_id, tmp_guid, MAX_PATH); joachim99@75: #else joachim99@75: wcstombs(class_id, tmp_guid, MAX_PATH); joachim99@75: #endif joachim99@75: CoTaskMemFree((void*)tmp_guid); joachim99@75: joachim99@75: LRESULT result = NOERROR; joachim99@75: TCHAR subkey[MAX_PATH]; joachim99@75: joachim99@75: REGSTRUCT entry[] = { joachim99@75: {TEXT("Software\\Classes\\CLSID\\%s\\InProcServer32"), 0, 0}, joachim99@75: {TEXT("Software\\Classes\\CLSID\\%s"), 0, 0} joachim99@75: }; joachim99@75: joachim99@75: for(unsigned int i = 0; (i < sizeof(entry)/sizeof(entry[0])) && (result == NOERROR); i++) { joachim99@75: _stprintf(subkey, entry[i].subkey, class_id); joachim99@75: result = RegDeleteKey(HKEY_CURRENT_USER, subkey); joachim99@75: } joachim99@75: joachim99@75: if(result == NOERROR) { joachim99@75: result = RegDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Classes\\*\\shellex\\ContextMenuHandlers\\diff-ext-for-kdiff3")); joachim99@75: joachim99@75: if(result == NOERROR) { joachim99@75: //If running on NT, register the extension as approved. joachim99@75: OSVERSIONINFO osvi; joachim99@75: joachim99@75: osvi.dwOSVersionInfoSize = sizeof(osvi); joachim99@75: GetVersionEx(&osvi); joachim99@75: joachim99@75: // NT needs to have shell extensions "approved". joachim99@75: if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) { joachim99@75: HKEY key; joachim99@75: joachim99@75: RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"), 0, KEY_ALL_ACCESS, &key); joachim99@75: joachim99@75: result = RegDeleteValue(key, class_id); joachim99@75: joachim99@75: RegCloseKey(key); joachim99@75: joachim99@75: if(result == ERROR_SUCCESS) { joachim99@75: ret = S_OK; joachim99@75: } joachim99@75: } joachim99@75: else { joachim99@75: ret = S_OK; joachim99@75: } joachim99@75: } joachim99@75: } joachim99@75: } joachim99@75: joachim99@75: return ret; joachim99@75: }