#include "PluginInstaller.h" #include "Config.h" #include "resource.h" #include #include #include namespace { constexpr wchar_t kDllFileName[] = L"PlandentMstscScriptHook.dll"; std::wstring JoinPath(const std::wstring& left, const std::wstring& right) { if (left.empty()) { return right; } if (left.back() == L'\\' || left.back() == L'/') { return left + right; } return left + L"\\" + right; } std::wstring ModuleDirectory() { std::vector buffer(32768, L'\0'); DWORD len = GetModuleFileNameW(nullptr, buffer.data(), static_cast(buffer.size())); if (len == 0 || len >= buffer.size()) { return {}; } std::wstring path(buffer.data(), len); size_t slash = path.find_last_of(L"\\/"); return slash == std::wstring::npos ? std::wstring{} : path.substr(0, slash); } bool EnsureDirectory(const std::wstring& path, std::wstring& error) { if (CreateDirectoryW(path.c_str(), nullptr) || GetLastError() == ERROR_ALREADY_EXISTS) { return true; } error = L"Verzeichnis konnte nicht erstellt werden: " + path + L" (Fehler " + std::to_wstring(GetLastError()) + L")"; return false; } bool ExtractResourceToFile(const std::wstring& destination, std::wstring& error) { HMODULE module = GetModuleHandleW(nullptr); HRSRC resource = FindResourceW(module, MAKEINTRESOURCEW(IDR_PLUGIN_DLL), RT_RCDATA); if (resource == nullptr) { // Fallback für Entwicklung: Plugin-DLL direkt neben der EXE akzeptieren. std::wstring adjacent = JoinPath(ModuleDirectory(), kDllFileName); DWORD attr = GetFileAttributesW(adjacent.c_str()); if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0) { if (CopyFileW(adjacent.c_str(), destination.c_str(), FALSE)) { return true; } error = L"Plugin-DLL konnte nicht kopiert werden (Fehler " + std::to_wstring(GetLastError()) + L")."; return false; } error = L"Die native Plugin-DLL ist weder eingebettet noch neben der Konfigurations-EXE vorhanden."; return false; } HGLOBAL loaded = LoadResource(module, resource); DWORD size = SizeofResource(module, resource); const void* data = loaded != nullptr ? LockResource(loaded) : nullptr; if (data == nullptr || size == 0) { error = L"Eingebettete Plugin-DLL konnte nicht gelesen werden."; return false; } HANDLE file = CreateFileW( destination.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (file == INVALID_HANDLE_VALUE) { error = L"Plugin-DLL konnte nicht geschrieben werden: " + destination + L" (Fehler " + std::to_wstring(GetLastError()) + L")"; return false; } DWORD written = 0; BOOL ok = WriteFile(file, data, size, &written, nullptr); DWORD writeError = ok ? ERROR_SUCCESS : GetLastError(); CloseHandle(file); if (!ok || written != size) { DeleteFileW(destination.c_str()); error = L"Plugin-DLL konnte nicht vollständig geschrieben werden (Fehler " + std::to_wstring(writeError) + L")."; return false; } return true; } } namespace installer { std::wstring InstallDirectory() { PWSTR localAppData = nullptr; HRESULT hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr, &localAppData); if (FAILED(hr) || localAppData == nullptr) { return {}; } std::wstring result = std::wstring(localAppData) + L"\\Plandent\\MstscScriptHook"; CoTaskMemFree(localAppData); return result; } std::wstring InstalledDllPath() { return JoinPath(InstallDirectory(), kDllFileName); } std::wstring LogPath() { return JoinPath(InstallDirectory(), L"plugin.log"); } bool Install(std::wstring& error) { PWSTR localAppData = nullptr; HRESULT hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr, &localAppData); if (FAILED(hr) || localAppData == nullptr) { error = L"%LOCALAPPDATA% konnte nicht ermittelt werden."; return false; } std::wstring base(localAppData); CoTaskMemFree(localAppData); std::wstring plandent = JoinPath(base, L"Plandent"); std::wstring targetDir = JoinPath(plandent, L"MstscScriptHook"); if (!EnsureDirectory(plandent, error) || !EnsureDirectory(targetDir, error)) { return false; } const std::wstring targetDll = JoinPath(targetDir, kDllFileName); if (!ExtractResourceToFile(targetDll, error)) { return false; } if (!config::RegisterPlugin(targetDll, error)) { return false; } return true; } bool Uninstall(std::wstring& message, bool& fullyRemoved) { fullyRemoved = false; std::wstring error; if (!config::UnregisterPlugin(error)) { message = error; return false; } std::wstring dll = InstalledDllPath(); DWORD attr = GetFileAttributesW(dll.c_str()); if (attr == INVALID_FILE_ATTRIBUTES) { fullyRemoved = true; message = L"Plugin-Registrierung wurde entfernt."; return true; } if (DeleteFileW(dll.c_str())) { RemoveDirectoryW(InstallDirectory().c_str()); // Darf fehlschlagen, z. B. wenn plugin.log existiert. fullyRemoved = true; message = L"Plugin-Registrierung und DLL wurden entfernt."; return true; } DWORD lastError = GetLastError(); if (lastError == ERROR_SHARING_VIOLATION || lastError == ERROR_ACCESS_DENIED) { message = L"Die Registrierung wurde entfernt. Die DLL wird wahrscheinlich noch von mstsc.exe verwendet. " L"Alle Remote-Desktop-Clients schließen und anschließend erneut auf „Plugin entfernen“ klicken."; return true; } message = L"Registrierung wurde entfernt, die DLL konnte aber nicht gelöscht werden (Fehler " + std::to_wstring(lastError) + L")."; return true; } Status GetStatus() { Status status; std::wstring registered = config::GetRegisteredPluginPath(); if (registered.empty()) { status.text = L"Plugin ist nicht registriert."; return status; } std::wstring expanded = config::ExpandEnvironment(registered); DWORD attr = GetFileAttributesW(expanded.c_str()); if (attr == INVALID_FILE_ATTRIBUTES || (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { status.text = L"Plugin ist registriert, DLL fehlt jedoch: " + expanded; return status; } status.installed = true; status.text = L"Plugin ist registriert: " + expanded; return status; } }