File indexing completed on 2024-06-23 05:14:17
0001 /* 0002 kuniqueservice_win.cpp 0003 0004 This file is part of Kleopatra, the KDE keymanager 0005 SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik 0006 SPDX-FileContributor: Intevation GmbH 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "kuniqueservice.h" 0012 0013 #include <QCoreApplication> 0014 #include <QDataStream> 0015 #include <QDir> 0016 0017 #include "kleopatra_debug.h" 0018 #include <windows.h> 0019 0020 #define MY_DATA_TYPE 12 0021 0022 class KUniqueService::KUniqueServicePrivate 0023 { 0024 Q_DECLARE_PUBLIC(KUniqueService) 0025 Q_DISABLE_COPY(KUniqueServicePrivate) 0026 0027 private: 0028 KUniqueService *q_ptr; 0029 HWND mResponder; 0030 HANDLE mCurrentProcess; 0031 0032 const QString getWindowName() const 0033 { 0034 return QCoreApplication::applicationName() + QStringLiteral("Responder"); 0035 } 0036 0037 HWND getForeignResponder() const 0038 { 0039 const QString qWndName = getWindowName(); 0040 wchar_t *wndName = (wchar_t *)qWndName.utf16(); 0041 HWND ret = FindWindow(wndName, wndName); 0042 qCDebug(KLEOPATRA_LOG) << "Responder handle:" << ret; 0043 return ret; 0044 } 0045 0046 void createResponder() 0047 { 0048 WNDCLASS windowClass; 0049 const QString qWndName = getWindowName(); 0050 wchar_t *wndName = (wchar_t *)qWndName.utf16(); 0051 windowClass.style = CS_GLOBALCLASS | CS_DBLCLKS; 0052 windowClass.lpfnWndProc = windowProc; 0053 windowClass.hInstance = (HINSTANCE)GetModuleHandle(NULL); 0054 windowClass.lpszClassName = wndName; 0055 windowClass.hIcon = nullptr; 0056 windowClass.hCursor = nullptr; 0057 windowClass.hbrBackground = nullptr; 0058 windowClass.lpszMenuName = nullptr; 0059 windowClass.cbClsExtra = 0; 0060 windowClass.cbWndExtra = 0; 0061 RegisterClass(&windowClass); 0062 mResponder = CreateWindow(wndName, wndName, 0, 0, 0, 10, 10, nullptr, nullptr, (HINSTANCE)GetModuleHandle(NULL), nullptr); 0063 qCDebug(KLEOPATRA_LOG) << "Created responder: " << qWndName << " with handle: " << mResponder; 0064 } 0065 0066 void handleRequest(const COPYDATASTRUCT *cds) 0067 { 0068 Q_Q(KUniqueService); 0069 if (cds->dwData != MY_DATA_TYPE) { 0070 qCDebug(KLEOPATRA_LOG) << "Responder called with invalid data type:" << cds->dwData; 0071 return; 0072 } 0073 if (mCurrentProcess) { 0074 qCDebug(KLEOPATRA_LOG) << "Already serving. Terminating process: " << mCurrentProcess; 0075 setExitValue(42); 0076 } 0077 const QByteArray serialized(static_cast<const char *>(cds->lpData), cds->cbData); 0078 QDataStream ds(serialized); 0079 quint32 curProc; 0080 ds >> curProc; 0081 mCurrentProcess = (HANDLE)curProc; 0082 QString workDir; 0083 ds >> workDir; 0084 QStringList args; 0085 ds >> args; 0086 qCDebug(KLEOPATRA_LOG) << "Process handle: " << mCurrentProcess << " requests activate with args " << args; 0087 q->emitActivateRequested(args, workDir); 0088 return; 0089 } 0090 0091 KUniqueServicePrivate(KUniqueService *q) 0092 : q_ptr(q) 0093 , mResponder(nullptr) 0094 , mCurrentProcess(nullptr) 0095 { 0096 HWND responder = getForeignResponder(); 0097 if (!responder) { 0098 // We are the responder 0099 createResponder(); 0100 return; 0101 } 0102 // We are the client 0103 0104 QByteArray serialized; 0105 QDataStream ds(&serialized, QIODevice::WriteOnly); 0106 DWORD targetId = 0; 0107 GetWindowThreadProcessId(responder, &targetId); 0108 if (!targetId) { 0109 qCDebug(KLEOPATRA_LOG) << "No process id of responder window"; 0110 return; 0111 } 0112 HANDLE targetHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, targetId); 0113 if (!targetHandle) { 0114 qCDebug(KLEOPATRA_LOG) << "No target handle. Err: " << GetLastError(); 0115 } 0116 0117 // To allow the other process to terminate the process 0118 // needs a handle to us with the required access. 0119 if (!DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), targetHandle, &mCurrentProcess, 0, FALSE, DUPLICATE_SAME_ACCESS)) { 0120 qCDebug(KLEOPATRA_LOG) << "Failed to duplicate handle"; 0121 } 0122 CloseHandle(targetHandle); 0123 0124 ds << (qint32)mCurrentProcess << QDir::currentPath() << QCoreApplication::arguments(); 0125 COPYDATASTRUCT cds; 0126 cds.dwData = MY_DATA_TYPE; 0127 cds.cbData = serialized.size(); 0128 cds.lpData = serialized.data(); 0129 0130 qCDebug(KLEOPATRA_LOG) << "Sending message to existing Window."; 0131 SendMessage(responder, WM_COPYDATA, 0, (LPARAM)&cds); 0132 // Usually we should be terminated while sending the message. 0133 qCDebug(KLEOPATRA_LOG) << "Send message returned."; 0134 } 0135 0136 static KUniqueServicePrivate *instance(KUniqueService *q) 0137 { 0138 static KUniqueServicePrivate *self; 0139 if (self) { 0140 return self; 0141 } 0142 0143 self = new KUniqueServicePrivate(q); 0144 return self; 0145 } 0146 0147 static LRESULT CALLBACK windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 0148 { 0149 if (message == WM_COPYDATA) { 0150 const COPYDATASTRUCT *cds = (COPYDATASTRUCT *)lParam; 0151 // windowProc must be static so the singleton pattern although 0152 // it doesn't make much sense in here. 0153 instance(nullptr)->handleRequest(cds); 0154 return 0; 0155 } 0156 return DefWindowProc(hWnd, message, wParam, lParam); 0157 } 0158 0159 ~KUniqueServicePrivate() 0160 { 0161 if (mResponder) { 0162 DestroyWindow(mResponder); 0163 } 0164 } 0165 0166 void setExitValue(int code) 0167 { 0168 TerminateProcess(mCurrentProcess, (unsigned int)code); 0169 mCurrentProcess = nullptr; 0170 } 0171 }; 0172 0173 KUniqueService::KUniqueService() 0174 : d_ptr(KUniqueServicePrivate::instance(this)) 0175 { 0176 } 0177 0178 KUniqueService::~KUniqueService() 0179 { 0180 delete d_ptr; 0181 } 0182 0183 void KUniqueService::setExitValue(int code) 0184 { 0185 Q_D(KUniqueService); 0186 d->setExitValue(code); 0187 }