diff --git a/Configurator/Configurator.cpp b/Configurator/Configurator.cpp index 7beffc7..a3721b7 100644 --- a/Configurator/Configurator.cpp +++ b/Configurator/Configurator.cpp @@ -89,6 +89,11 @@ void Configurator::onInitDialog(HWND hDlg) LoadStringW(hInstance, IDS_ALREADY_INSTALLED, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); ListView_SetItemText(deviceList, itemCount, 2, stringBuf); } + else if(it->originalApoGuid == APOGUID_NOKEY) + { + LoadStringW(hInstance, IDS_CAN_BE_INSTALLED_EXPERIMENTAL, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); + ListView_SetItemText(deviceList, itemCount, 2, stringBuf); + } else { LoadStringW(hInstance, IDS_CAN_BE_INSTALLED, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); diff --git a/Configurator/Configurator.rc b/Configurator/Configurator.rc index 70a8074..b3a47ab 100644 Binary files a/Configurator/Configurator.rc and b/Configurator/Configurator.rc differ diff --git a/Configurator/resource.h b/Configurator/resource.h index 0a5b33c..b50aaa1 100644 Binary files a/Configurator/resource.h and b/Configurator/resource.h differ diff --git a/DeviceAPOInfo.cpp b/DeviceAPOInfo.cpp index 8e7543c..51d785b 100644 --- a/DeviceAPOInfo.cpp +++ b/DeviceAPOInfo.cpp @@ -30,6 +30,7 @@ using namespace std; static const wchar_t* connectionValueName = L"{a45c254e-df1c-4efd-8020-67d146a850e0},2"; static const wchar_t* deviceValueName = L"{b3f8fa53-0004-438e-9003-51a46e139bfc},6"; static const wchar_t* apoGuidValueName = L"{d04e05a6-594b-4fb6-a80d-01af5eed7d1d},1"; +static const wchar_t* fxTitleValueName = L"{b725f130-47ef-101a-a5f1-02608c9eebac},10"; vector DeviceAPOInfo::loadAllInfos() { @@ -49,21 +50,32 @@ vector DeviceAPOInfo::loadAllInfos() bool DeviceAPOInfo::load(const wstring& deviceGuid) { - if(!RegistryHelper::keyExists(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties")) - return false; - this->deviceGuid = deviceGuid; connectionName = RegistryHelper::readValue(renderKeyPath L"\\" + deviceGuid + L"\\Properties", connectionValueName); deviceName = RegistryHelper::readValue(renderKeyPath L"\\" + deviceGuid + L"\\Properties", deviceValueName); - originalApoGuid = RegistryHelper::readValue(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties", apoGuidValueName); - - GUID apoGuid; - if(!SUCCEEDED(CLSIDFromString(originalApoGuid.c_str(), &apoGuid))) - return false; isInstalled = false; - if(apoGuid == __uuidof(EqualizerAPO)) - isInstalled = true; + + if(!RegistryHelper::keyExists(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties")) + { + unsigned long deviceState = RegistryHelper::readDWORDValue(renderKeyPath L"\\" + deviceGuid, L"DeviceState"); + if(deviceState == 0x10000004) + // Ignore special internal devices + return false; + + originalApoGuid = APOGUID_NOKEY; + } + else + { + originalApoGuid = RegistryHelper::readValue(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties", apoGuidValueName); + + GUID apoGuid; + if(!SUCCEEDED(CLSIDFromString(originalApoGuid.c_str(), &apoGuid))) + return false; + + if(apoGuid == __uuidof(EqualizerAPO)) + isInstalled = true; + } return true; } @@ -74,8 +86,28 @@ void DeviceAPOInfo::install() RegistryHelper::writeValue(APP_REGPATH L"\\Child APOs", deviceGuid, originalApoGuid); - RegistryHelper::saveToFile(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties", apoGuidValueName, - L"backup_" + StringHelper::replaceIllegalCharacters(deviceName) + L"_" + StringHelper::replaceIllegalCharacters(connectionName) + L".reg"); + if(originalApoGuid == APOGUID_NOKEY) + { + try + { + RegistryHelper::createKey(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties"); + } + catch(RegistryException e) + { + // Permissions were not sufficient, so change them + RegistryHelper::takeOwnership(renderKeyPath L"\\" + deviceGuid); + RegistryHelper::makeWritable(renderKeyPath L"\\" + deviceGuid); + + RegistryHelper::createKey(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties"); + } + + RegistryHelper::writeValue(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties", fxTitleValueName, L"Equalizer APO"); + } + else + { + RegistryHelper::saveToFile(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties", apoGuidValueName, + L"backup_" + StringHelper::replaceIllegalCharacters(deviceName) + L"_" + StringHelper::replaceIllegalCharacters(connectionName) + L".reg"); + } RegistryHelper::writeValue(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties", apoGuidValueName, RegistryHelper::getGuidString(__uuidof(EqualizerAPO))); } @@ -84,7 +116,10 @@ void DeviceAPOInfo::uninstall() { wstring originalChildApoGuid = RegistryHelper::readValue(APP_REGPATH L"\\Child APOs", deviceGuid); - RegistryHelper::writeValue(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties", apoGuidValueName, originalChildApoGuid); + if(originalChildApoGuid == APOGUID_NOKEY) + RegistryHelper::deleteKey(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties"); + else + RegistryHelper::writeValue(renderKeyPath L"\\" + deviceGuid + L"\\FxProperties", apoGuidValueName, originalChildApoGuid); RegistryHelper::deleteValue(APP_REGPATH L"\\Child APOs", deviceGuid); diff --git a/DeviceAPOInfo.h b/DeviceAPOInfo.h index 837748a..d9e10e4 100644 --- a/DeviceAPOInfo.h +++ b/DeviceAPOInfo.h @@ -22,6 +22,8 @@ #include #include +#define APOGUID_NOKEY L"!KEY" + class DeviceAPOInfo { public: diff --git a/EqualizerAPO.cpp b/EqualizerAPO.cpp index f6d6f02..3bcbb3d 100644 --- a/EqualizerAPO.cpp +++ b/EqualizerAPO.cpp @@ -130,47 +130,51 @@ HRESULT EqualizerAPO::Initialize(UINT32 cbDataSize, BYTE* pbyData) TraceF(L"Child APO GUID: %s", apoGuid.c_str()); - GUID childGuid; - hr = CLSIDFromString(apoGuid.c_str(), &childGuid); - if(FAILED(hr)) + if(apoGuid != APOGUID_NOKEY) { - LogF(L"Can't convert guid string to guid"); - return hr; + GUID childGuid; + hr = CLSIDFromString(apoGuid.c_str(), &childGuid); + if(FAILED(hr)) + { + LogF(L"Can't convert guid string to guid"); + return hr; + } + + hr = CoCreateInstance(childGuid, NULL, CLSCTX_INPROC_SERVER, __uuidof(IAudioProcessingObject), (void**)&childAPO); + if(FAILED(hr)) + { + LogF(L"Error in CoCreateInstance for child apo"); + resetChild(); + return hr; + } + + hr = childAPO->QueryInterface(__uuidof(IAudioProcessingObjectRT), (void**)&childRT); + if(FAILED(hr)) + { + LogF(L"Error in QueryInterface for child apo RT"); + resetChild(); + return hr; + } + + hr = childAPO->QueryInterface(__uuidof(IAudioProcessingObjectConfiguration), (void**)&childCfg); + if(FAILED(hr)) + { + LogF(L"Error in QueryInterface for child apo configuration"); + resetChild(); + return hr; + } + + hr = childAPO->Initialize(cbDataSize, pbyData); + if(FAILED(hr)) + { + LogF(L"Error in Initialize of child apo"); + resetChild(); + return hr; + } + + TraceF(L"Successfully created and initialized child APO"); } - hr = CoCreateInstance(childGuid, NULL, CLSCTX_INPROC_SERVER, __uuidof(IAudioProcessingObject), (void**)&childAPO); - if(FAILED(hr)) - { - LogF(L"Error in CoCreateInstance for child apo"); - resetChild(); - return hr; - } - - hr = childAPO->QueryInterface(__uuidof(IAudioProcessingObjectRT), (void**)&childRT); - if(FAILED(hr)) - { - LogF(L"Error in QueryInterface for child apo RT"); - resetChild(); - return hr; - } - - hr = childAPO->QueryInterface(__uuidof(IAudioProcessingObjectConfiguration), (void**)&childCfg); - if(FAILED(hr)) - { - LogF(L"Error in QueryInterface for child apo configuration"); - resetChild(); - return hr; - } - - hr = childAPO->Initialize(cbDataSize, pbyData); - if(FAILED(hr)) - { - LogF(L"Error in Initialize of child apo"); - resetChild(); - return hr; - } - - TraceF(L"Successfully created and initialized child APO"); return hr; } diff --git a/RegistryHelper.cpp b/RegistryHelper.cpp index 4f0593b..5f744f4 100644 --- a/RegistryHelper.cpp +++ b/RegistryHelper.cpp @@ -22,6 +22,7 @@ #define WIN32_LEAN_AND_MEAN #include #include +#include #include "RegistryHelper.h" @@ -72,6 +73,48 @@ wstring RegistryHelper::readValue(wstring key, wstring valuename) return result; } +unsigned long RegistryHelper::readDWORDValue(wstring key, wstring valuename) +{ + unsigned long result; + + LSTATUS status; + HKEY keyHandle; + status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key.c_str(), 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &keyHandle); + if(status != ERROR_SUCCESS) + throw RegistryException(L"Error while opening registry key HKEY_LOCAL_MACHINE\\" + key + L": " + getSystemErrorString(status)); + + DWORD type; + DWORD bufSize; + status = RegQueryValueExW(keyHandle, valuename.c_str(), NULL, &type, NULL, &bufSize); + if(status != ERROR_SUCCESS) + { + RegCloseKey(keyHandle); + throw RegistryException(L"Error while reading registry value HKEY_LOCAL_MACHINE\\" + key + L"\\" + valuename + L": " + getSystemErrorString(status)); + } + + if(type != REG_DWORD) + { + RegCloseKey(keyHandle); + throw RegistryException(L"Registry value HKEY_LOCAL_MACHINE\\" + key + L"\\" + valuename + L" has wrong type"); + } + + BYTE* buf = new BYTE[bufSize]; + status = RegQueryValueExW(keyHandle, valuename.c_str(), NULL, NULL, buf, &bufSize); + + RegCloseKey(keyHandle); + + if(status != ERROR_SUCCESS) + { + delete buf; + throw RegistryException(L"Error while reading registry value HKEY_LOCAL_MACHINE\\" + key + L"\\" + valuename + L": " + getSystemErrorString(status)); + } + + result = ((unsigned long*)buf)[0]; + delete buf; + + return result; +} + void RegistryHelper::writeValue(wstring key, wstring valuename, wstring value) { LSTATUS status; @@ -123,6 +166,118 @@ void RegistryHelper::deleteKey(wstring key) throw RegistryException(L"Error while deleting registry key HKEY_LOCAL_MACHINE\\" + key + L": " + getSystemErrorString(status)); } +void RegistryHelper::makeWritable(wstring key) +{ + LSTATUS status; + HKEY keyHandle; + status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key.c_str(), 0, READ_CONTROL | WRITE_DAC | KEY_WOW64_64KEY, &keyHandle); + if(status != ERROR_SUCCESS) + throw RegistryException(L"Error while opening registry key HKEY_LOCAL_MACHINE\\" + key + L": " + getSystemErrorString(status)); + + DWORD descriptorSize = 0; + RegGetKeySecurity(keyHandle, DACL_SECURITY_INFORMATION, NULL, &descriptorSize); + + PSECURITY_DESCRIPTOR oldSd = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, descriptorSize); + status = RegGetKeySecurity(keyHandle, DACL_SECURITY_INFORMATION, oldSd, &descriptorSize); + if(status != ERROR_SUCCESS) + throw RegistryException(L"Error while getting security information for registry key HKEY_LOCAL_MACHINE\\" + key + L": " + getSystemErrorString(status)); + + BOOL aclPresent, aclDefaulted; + PACL oldAcl = NULL; + if(!GetSecurityDescriptorDacl(oldSd, &aclPresent, &oldAcl, &aclDefaulted)) + throw RegistryException(L"Error in GetSecurityDescriptorDacl while ensuring writability"); + + PSID sid = NULL; + SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_AUTHORITY; + if(!AllocateAndInitializeSid(&authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, &sid)) + throw RegistryException(L"Error in AllocateAndInitializeSid while ensuring writability"); + + EXPLICIT_ACCESS ea; + ea.grfAccessPermissions = KEY_ALL_ACCESS; + ea.grfAccessMode = SET_ACCESS; + ea.grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT; + ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea.Trustee.ptstrName = (LPWSTR)sid; + + PACL acl = NULL; + if(ERROR_SUCCESS != SetEntriesInAcl(1, &ea, oldAcl, &acl)) + throw RegistryException(L"Error in SetEntriesInAcl while ensuring writability"); + + PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if(NULL == sd) + throw RegistryException(L"Error in LocalAlloc while ensuring writability"); + + if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) + throw RegistryException(L"Error in InitializeSecurityDescriptor while ensuring writability"); + + if(!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE)) + throw RegistryException(L"Error in SetSecurityDescriptorDacl while ensuring writability"); + + status = RegSetKeySecurity(keyHandle, DACL_SECURITY_INFORMATION, sd); + if(status != ERROR_SUCCESS) + throw RegistryException(L"Error while setting security information for registry key HKEY_LOCAL_MACHINE\\" + key + L": " + getSystemErrorString(status)); + + FreeSid(sid); + LocalFree(acl); + HeapFree(GetProcessHeap(), 0, oldSd); + LocalFree(sd); +} + +void RegistryHelper::takeOwnership(wstring key) +{ + HANDLE tokenHandle; + if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &tokenHandle)) + throw RegistryException(L"Error in OpenProcessToken while taking ownership"); + + LUID luid; + if(!LookupPrivilegeValue(NULL, SE_TAKE_OWNERSHIP_NAME, &luid)) + throw RegistryException(L"Error in LookupPrivilegeValue while taking ownership"); + + TOKEN_PRIVILEGES tp; + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if(!AdjustTokenPrivileges(tokenHandle, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) + throw RegistryException(L"Error in AdjustTokenPrivileges while taking ownership"); + + LSTATUS status; + HKEY keyHandle; + status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key.c_str(), 0, WRITE_OWNER | KEY_WOW64_64KEY, &keyHandle); + if(status != ERROR_SUCCESS) + throw RegistryException(L"Error while opening registry key HKEY_LOCAL_MACHINE\\" + key + L": " + getSystemErrorString(status)); + + PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if(NULL == sd) + throw RegistryException(L"Error in SetPrivilege while taking ownership"); + + if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) + throw RegistryException(L"Error in InitializeSecurityDescriptor while taking ownership"); + + PSID sid = NULL; + SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_AUTHORITY; + if(!AllocateAndInitializeSid(&authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, &sid)) + throw RegistryException(L"Error in AllocateAndInitializeSid while taking ownership"); + + if(!SetSecurityDescriptorOwner(sd, sid, FALSE)) + throw RegistryException(L"Error in SetSecurityDescriptorOwner while taking ownership"); + + status = RegSetKeySecurity(keyHandle, OWNER_SECURITY_INFORMATION, sd); + if(status != ERROR_SUCCESS) + throw RegistryException(L"Error while setting security information for registry key HKEY_LOCAL_MACHINE\\" + key + L": " + getSystemErrorString(status)); + + tp.Privileges[0].Attributes = 0; + + if(!AdjustTokenPrivileges(tokenHandle, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) + throw RegistryException(L"Error in AdjustTokenPrivileges while taking ownership"); + + FreeSid(sid); + LocalFree(sd); +} + bool RegistryHelper::keyExists(wstring key) { bool result; diff --git a/RegistryHelper.h b/RegistryHelper.h index 64f2094..c66ca30 100644 --- a/RegistryHelper.h +++ b/RegistryHelper.h @@ -29,10 +29,13 @@ class RegistryHelper { public: static std::wstring readValue(std::wstring key, std::wstring valuename); + static unsigned long readDWORDValue(std::wstring key, std::wstring valuename); static void writeValue(std::wstring key, std::wstring valuename, std::wstring value); static void deleteValue(std::wstring key, std::wstring valuename); static void createKey(std::wstring key); static void deleteKey(std::wstring key); + static void makeWritable(std::wstring key); + static void takeOwnership(std::wstring key); static std::vector enumSubKeys(std::wstring key); static bool keyExists(std::wstring key); static unsigned long valueCount(std::wstring key); diff --git a/Setup/Setup.nsi b/Setup/Setup.nsi index 68b6be8..d930a73 100644 --- a/Setup/Setup.nsi +++ b/Setup/Setup.nsi @@ -1,4 +1,7 @@ !include "MUI2.nsh" +!include "NSISpcre.nsh" + +!insertmacro REReplace !searchparse /file ..\version.h `#define MAJOR ` MAJOR !searchparse /file ..\version.h `#define MINOR ` MINOR @@ -74,6 +77,12 @@ Function .onInit ;Use default installation folder otherwise StrCmp $INSTDIR "" 0 +2 StrCpy $INSTDIR "$PROGRAMFILES64\EqualizerAPO" + + !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder + ;Try to replace version number in start menu folder + ${REReplace} $0 "Equalizer APO [0-9]+\.[0-9]+" "$StartMenuFolder" "Equalizer APO ${MAJOR}.${MINOR}" 1 + StrCmp $0 "" +2 0 + StrCpy $StartMenuFolder "$0" Call initCheck FunctionEnd @@ -130,6 +139,7 @@ Section "Install" SecInstall !insertmacro MUI_STARTMENU_WRITE_END WriteRegStr HKLM ${UNINST_REGPATH} "DisplayName" "Equalizer APO" + WriteRegStr HKLM ${UNINST_REGPATH} "DisplayVersion" "${MAJOR}.${MINOR}" WriteRegStr HKLM ${UNINST_REGPATH} "UninstallString" '"$INSTDIR\Uninstall.exe"' WriteRegDWORD HKLM ${UNINST_REGPATH} "NoModify" 1 WriteRegDWORD HKLM ${UNINST_REGPATH} "NoRepair" 1 diff --git a/StringHelper.cpp b/StringHelper.cpp index b0d9ade..fa2d0b7 100644 --- a/StringHelper.cpp +++ b/StringHelper.cpp @@ -1,3 +1,21 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2013 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ #include #include diff --git a/version.h b/version.h index 3caebc4..149392f 100644 --- a/version.h +++ b/version.h @@ -1,2 +1,2 @@ #define MAJOR 0 -#define MINOR 6 +#define MINOR 7