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