You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kdiff3/diff_ext_for_kdiff3/server.cpp

453 lines
13 KiB

/*
* 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;
}