/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include namespace { HRESULT RegRead(HKEY hRootKey, const wchar_t* subKey, const wchar_t* valName, wchar_t* valData, size_t cchData) { HKEY hKey; LSTATUS iRetVal = RegCreateKeyExW( hRootKey, subKey, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_READ, nullptr, &hKey, nullptr); if (iRetVal != ERROR_SUCCESS) return HRESULT_FROM_WIN32(iRetVal); DWORD cbData = cchData * sizeof(valData[0]); DWORD dwType; iRetVal = RegQueryValueExW(hKey, valName, nullptr, &dwType, reinterpret_cast(valData), &cbData); RegCloseKey(hKey); if ((iRetVal == ERROR_SUCCESS) && (dwType != REG_SZ)) { return E_FAIL; } return HRESULT_FROM_WIN32(iRetVal); } HRESULT RegWrite(HKEY hRootKey, const wchar_t* subKey, const wchar_t* valName, const wchar_t* valData, HKEY *hKeyResult = nullptr) { HKEY hKey; LSTATUS iRetVal = RegCreateKeyExW( hRootKey, subKey, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, &hKey, nullptr); if (iRetVal != ERROR_SUCCESS) return HRESULT_FROM_WIN32(iRetVal); if (valData) { DWORD cbData = static_cast(wcslen(valData)*sizeof(valData[0])); iRetVal = RegSetValueExW(hKey, valName, 0, REG_SZ, reinterpret_cast(valData), cbData); } if (hKeyResult && (iRetVal == ERROR_SUCCESS)) *hKeyResult = hKey; else RegCloseKey(hKey); return HRESULT_FROM_WIN32(iRetVal); } HRESULT RegDel(HKEY hRootKey, const wchar_t* subKey) { LSTATUS iRetVal = RegDeleteKeyW(hRootKey, subKey); return HRESULT_FROM_WIN32(iRetVal); } } // namespace // see http://stackoverflow.com/questions/284619 // see https://msdn.microsoft.com/en-us/library/ms691424 // see https://msdn.microsoft.com/en-us/library/ms694514 Registrar::Registrar(REFIID riidCLSID) { m_ConstructionResult = (StringFromGUID2(riidCLSID, m_sCLSID, nGUIDlen) == 0) ? E_UNEXPECTED: S_OK; } HRESULT Registrar::RegisterObject(REFIID riidTypeLib, const wchar_t* sProgram, const wchar_t* sComponent, std::initializer_list aVersions, const wchar_t* Path) { if (!wcslen(sComponent) || !wcslen(sProgram)) return E_INVALIDARG; if (FAILED(m_ConstructionResult)) return m_ConstructionResult; // HKEY_CLASSES_ROOT // \CLSID // \{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} // (default) = "MyLibrary MyControl Class" // \InprocServer32 // (default) = "c:\foo\control.dll" // ThreadingModel = "Apartment" // \ProgID // (default) = "MyLibrary.MyControl" // \Programmable // \TypeLib // (default) = "{YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY}" wchar_t sBufKey[MAX_PATH]; wchar_t sBufVal[MAX_PATH]; // CLSID swprintf(sBufKey, MAX_PATH, L"CLSID\\%s", m_sCLSID); swprintf(sBufVal, MAX_PATH, L"%s %s Class", sProgram, sComponent); HKEY hKeyCLSID; HRESULT hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal, &hKeyCLSID); if (FAILED(hr)) return hr; { class HKeyGuard { public: HKeyGuard(HKEY aKey) : m_hKey(aKey) {} ~HKeyGuard() { RegCloseKey(m_hKey); } private: HKEY m_hKey; }; HKeyGuard hKeyCLSIDGuard(hKeyCLSID); // InprocServer32 HKEY hKeyInprocServer32; hr = RegWrite(hKeyCLSID, L"InprocServer32", L"", Path, &hKeyInprocServer32); if (FAILED(hr)) return hr; { HKeyGuard hKeyInProcServer32Guard(hKeyInprocServer32); hr = RegWrite(hKeyInprocServer32, L"", L"ThreadingModel", L"Apartment"); if (FAILED(hr)) return hr; } // ProgID swprintf(sBufVal, MAX_PATH, L"%s.%s", sProgram, sComponent); hr = RegWrite(hKeyCLSID, L"ProgID", L"", sBufVal); if (FAILED(hr)) return hr; // Programmable hr = RegWrite(hKeyCLSID, L"Programmable", nullptr, nullptr); if (FAILED(hr)) return hr; // TypeLib if (::StringFromGUID2(riidTypeLib, sBufVal, nGUIDlen) == 0) return E_UNEXPECTED; hr = RegWrite(hKeyCLSID, L"TypeLib", L"", sBufVal); if (FAILED(hr)) return hr; } // ProgID return RegisterProgIDs(sProgram, sComponent, aVersions); } HRESULT Registrar::UnRegisterObject(const wchar_t* sProgram, const wchar_t* sComponent, std::initializer_list aVersions) { if (FAILED(m_ConstructionResult)) return m_ConstructionResult; // ProgID UnRegisterProgIDs(sProgram, sComponent, aVersions); // CLSID wchar_t sBuf[MAX_PATH]; swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\InProcServer32", m_sCLSID); RegDel(HKEY_CLASSES_ROOT, sBuf); swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\ProgId", m_sCLSID); RegDel(HKEY_CLASSES_ROOT, sBuf); swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\Programmable", m_sCLSID); RegDel(HKEY_CLASSES_ROOT, sBuf); swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\TypeLib", m_sCLSID); RegDel(HKEY_CLASSES_ROOT, sBuf); swprintf(sBuf, MAX_PATH, L"CLSID\\%s", m_sCLSID); RegDel(HKEY_CLASSES_ROOT, sBuf); return S_OK; } HRESULT Registrar::RegisterProgID(const wchar_t* sProgram, const wchar_t* sComponent, int nVersion, bool bSetDefault) { // HKEY_CLASSES_ROOT // \MyLibrary.MyControl // (default) = "MyLibrary MyControl Class" // \CurVer // (default) = "MyLibrary.MyControl.1" // \MyLibrary.MyControl.1 // (default) = "MyLibrary MyControl Class" // \CLSID // (default) = "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" if (FAILED(m_ConstructionResult)) return m_ConstructionResult; wchar_t sBufKey[MAX_PATH]; swprintf(sBufKey, MAX_PATH, L"%s.%s.%d", sProgram, sComponent, nVersion); wchar_t sBufVal[MAX_PATH]; swprintf(sBufVal, MAX_PATH, L"%s %s Class", sProgram, sComponent); RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal); swprintf(sBufKey, MAX_PATH, L"%s.%s.%d\\CLSID", sProgram, sComponent, nVersion); HRESULT hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", m_sCLSID); if (SUCCEEDED(hr) && bSetDefault) { swprintf(sBufKey, MAX_PATH, L"%s.%s", sProgram, sComponent); swprintf(sBufVal, MAX_PATH, L"%s %s Class", sProgram, sComponent); hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal); swprintf(sBufKey, MAX_PATH, L"%s.%s\\CurVer", sProgram, sComponent); swprintf(sBufVal, MAX_PATH, L"%s.%s.%d", sProgram, sComponent, nVersion); hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal); } return hr; } HRESULT Registrar::RegisterProgIDs(const wchar_t* sProgram, const wchar_t* sComponent, std::initializer_list aVersions) { HRESULT hr = S_OK; bool bDefaultRegistered = false; for (int nVersion : aVersions) { if (SUCCEEDED(hr)) { hr = RegisterProgID(sProgram, sComponent, nVersion, !bDefaultRegistered); bDefaultRegistered = true; } } return hr; } HRESULT Registrar::UnRegisterProgID(const wchar_t* sProgram, const wchar_t* sComponent, int nVersion) { if (FAILED(m_ConstructionResult)) return m_ConstructionResult; wchar_t sBuf[MAX_PATH]; swprintf(sBuf, MAX_PATH, L"%s.%s.%d\\CLSID", sProgram, sComponent, nVersion); wchar_t sCurCLSID[nGUIDlen]; HRESULT hr = RegRead(HKEY_CLASSES_ROOT, sBuf, L"", sCurCLSID, nGUIDlen); if (FAILED(hr)) return hr; if (wcsncmp(sCurCLSID, m_sCLSID, nGUIDlen) != 0) { // The ProgID points to a different CLSID; most probably it's intercepted // by a different application, so don't remove it return S_FALSE; } RegDel(HKEY_CLASSES_ROOT, sBuf); swprintf(sBuf, MAX_PATH, L"%s.%s.%d", sProgram, sComponent, nVersion); hr = RegDel(HKEY_CLASSES_ROOT, sBuf); wchar_t sBufKey[MAX_PATH]; swprintf(sBufKey, MAX_PATH, L"%s.%s\\CurVer", sProgram, sComponent); wchar_t sBufVal[MAX_PATH]; if (SUCCEEDED(RegRead(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal, MAX_PATH)) && (wcsncmp(sBufVal, sBuf, MAX_PATH) == 0)) { // Only unreg default if this version is current default RegDel(HKEY_CLASSES_ROOT, sBufKey); swprintf(sBuf, MAX_PATH, L"%s.%s", sProgram, sComponent); HRESULT hr1 = RegDel(HKEY_CLASSES_ROOT, sBuf); // Always return a failure result if we failed somewhere if (FAILED(hr1)) hr = hr1; } return hr; } HRESULT Registrar::UnRegisterProgIDs(const wchar_t* sProgram, const wchar_t* sComponent, std::initializer_list aVersions) { HRESULT hr = S_OK; // Try all ProgIDs regardless of error, but make sure to return failure result if some failed for (int nVersion : aVersions) { HRESULT hrLast = UnRegisterProgID(sProgram, sComponent, nVersion); if (SUCCEEDED(hr)) hr = hrLast; } return hr; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */