From e0016d941c4eaf5cfe40b21cb367c0b8548e80e0 Mon Sep 17 00:00:00 2001 From: jthedering <51533355+jthedering@users.noreply.github.com> Date: Wed, 18 Dec 2013 21:29:51 +0000 Subject: [PATCH] Fixed: Newer drivers with registry values introduced in Windows 8.1 were treated incorrectly in earlier versions of Windows. --- Benchmark/Benchmark.vcxproj | 8 ++--- Configurator/Configurator.vcxproj | 8 ++--- DeviceAPOInfo.cpp | 7 ++-- EqualizerAPO.vcxproj | 8 ++--- RegistryHelper.cpp | 48 +++++++++++++++++++------ RegistryHelper.h | 2 ++ Wiki/Developer.txt | 59 +++++++++++++++++++++++++++++++ Wiki/Documentation.txt | 2 +- 8 files changed, 116 insertions(+), 26 deletions(-) create mode 100644 Wiki/Developer.txt diff --git a/Benchmark/Benchmark.vcxproj b/Benchmark/Benchmark.vcxproj index 730d66b..4d1a62c 100644 --- a/Benchmark/Benchmark.vcxproj +++ b/Benchmark/Benchmark.vcxproj @@ -93,7 +93,7 @@ Console true - Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) + version.lib;Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) @@ -107,7 +107,7 @@ Console true - Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) + version.lib;Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) @@ -126,7 +126,7 @@ true true true - Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) + version.lib;Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) @@ -145,7 +145,7 @@ true true true - Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) + version.lib;Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) diff --git a/Configurator/Configurator.vcxproj b/Configurator/Configurator.vcxproj index 13f57a0..7938cc6 100644 --- a/Configurator/Configurator.vcxproj +++ b/Configurator/Configurator.vcxproj @@ -117,7 +117,7 @@ Windows true - comctl32.lib;%(AdditionalDependencies) + version.lib;comctl32.lib;%(AdditionalDependencies) @@ -130,7 +130,7 @@ Windows true - comctl32.lib;%(AdditionalDependencies) + version.lib;comctl32.lib;%(AdditionalDependencies) @@ -147,7 +147,7 @@ true true true - comctl32.lib;%(AdditionalDependencies) + version.lib;comctl32.lib;%(AdditionalDependencies) @@ -164,7 +164,7 @@ true true true - comctl32.lib;%(AdditionalDependencies) + version.lib;comctl32.lib;%(AdditionalDependencies) diff --git a/DeviceAPOInfo.cpp b/DeviceAPOInfo.cpp index 3017a4c..8291786 100644 --- a/DeviceAPOInfo.cpp +++ b/DeviceAPOInfo.cpp @@ -102,8 +102,9 @@ bool DeviceAPOInfo::load(const wstring& deviceGuid) if(!found) found = tryAPOGuid(keyPath, gfxGuidValueName2, false, true); - foundAPO2Values = RegistryHelper::valueExists(keyPath + L"\\FxProperties", lfxGuidValueName2) - || RegistryHelper::valueExists(keyPath + L"\\FxProperties", gfxGuidValueName2); + foundAPO2Values = RegistryHelper::isWindowsVersionAtLeast(6, 3) // Windows 8.1 + && (RegistryHelper::valueExists(keyPath + L"\\FxProperties", lfxGuidValueName2) + || RegistryHelper::valueExists(keyPath + L"\\FxProperties", gfxGuidValueName2)); if(found) { @@ -257,4 +258,4 @@ bool DeviceAPOInfo::tryAPOGuid(const wstring& keyPath, const wchar_t* valueName, } return false; -} +} \ No newline at end of file diff --git a/EqualizerAPO.vcxproj b/EqualizerAPO.vcxproj index aae2845..2d9ba68 100644 --- a/EqualizerAPO.vcxproj +++ b/EqualizerAPO.vcxproj @@ -94,7 +94,7 @@ Windows true - wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) + version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) EqualizerAPO.def @@ -111,7 +111,7 @@ Windows true - wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) + version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) EqualizerAPO.def @@ -133,7 +133,7 @@ true true true - wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) + version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) EqualizerAPO.def @@ -152,7 +152,7 @@ true true true - wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) + version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) EqualizerAPO.def diff --git a/RegistryHelper.cpp b/RegistryHelper.cpp index 88dce56..b662fe7 100644 --- a/RegistryHelper.cpp +++ b/RegistryHelper.cpp @@ -28,6 +28,8 @@ using namespace std; +DWORD RegistryHelper::windowsVersion = 0; + wstring RegistryHelper::readValue(wstring key, wstring valuename) { wstring result; @@ -213,7 +215,7 @@ void RegistryHelper::makeWritable(wstring key) 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)) + 0, 0, 0, 0, 0, 0, &sid)) throw RegistryException(L"Error in AllocateAndInitializeSid while ensuring writability"); EXPLICIT_ACCESS ea; @@ -225,14 +227,14 @@ void RegistryHelper::makeWritable(wstring key) ea.Trustee.ptstrName = (LPWSTR)sid; PACL acl = NULL; - if(ERROR_SUCCESS != SetEntriesInAcl(1, &ea, oldAcl, &acl)) + 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) + 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)) + if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) throw RegistryException(L"Error in InitializeSecurityDescriptor while ensuring writability"); if(!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE)) @@ -272,17 +274,17 @@ void RegistryHelper::takeOwnership(wstring key) 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) + 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)) + 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)) + 0, 0, 0, 0, 0, 0, &sid)) throw RegistryException(L"Error in AllocateAndInitializeSid while taking ownership"); if(!SetSecurityDescriptorOwner(sd, sid, FALSE)) @@ -405,6 +407,32 @@ wstring RegistryHelper::getGuidString(GUID guid) return result; } +bool RegistryHelper::isWindowsVersionAtLeast(unsigned major, unsigned minor) +{ + if(windowsVersion == 0) + { + DWORD handle; + DWORD size = GetFileVersionInfoSizeW(L"kernel32.dll", &handle); + if(size != 0) + { + void* data = malloc(size); + if(GetFileVersionInfo(L"kernel32.dll", handle, size, data)) + { + VS_FIXEDFILEINFO* info; + UINT len; + if(VerQueryValueW(data, L"\\", (LPVOID*)&info, &len)) + windowsVersion = info->dwProductVersionMS; + } + free(data); + } + } + + // this will only work for major and minor up to 99 + DWORD compareVersion = ((major / 10) << 20) + ((major % 10) << 16) + ((minor / 10) << 4) + (minor % 10); + + return windowsVersion >= compareVersion; +} + wstring RegistryHelper::getSystemErrorString(long status) { wchar_t* buf; @@ -418,4 +446,4 @@ wstring RegistryHelper::getSystemErrorString(long status) } else return L""; -} +} \ No newline at end of file diff --git a/RegistryHelper.h b/RegistryHelper.h index 78c00f0..0159b1a 100644 --- a/RegistryHelper.h +++ b/RegistryHelper.h @@ -43,9 +43,11 @@ public: static unsigned long valueCount(std::wstring key); static void saveToFile(std::wstring key, std::wstring valuename, std::wstring filepath); static std::wstring getGuidString(GUID guid); + static bool isWindowsVersionAtLeast(unsigned major, unsigned minor); private: static std::wstring getSystemErrorString(long status); + static unsigned long windowsVersion; }; class RegistryException diff --git a/Wiki/Developer.txt b/Wiki/Developer.txt new file mode 100644 index 0000000..9434d87 --- /dev/null +++ b/Wiki/Developer.txt @@ -0,0 +1,59 @@ +This is the developer documentation for Equalizer APO. It is meant for developers who want to review or experiment with the Equalizer APO source code and for those who want to write their own APO. If you only want to use the software, please refer to the [user documentation](https://sourceforge.net/p/equalizerapo/wiki/). + +
+ +Disclaimer:
+ +The information in this documentation has been written to the best of my knowledge.
+It is neither complete nor guaranteed to be free of errors. + +
+ +
+ +**Table of contents:** + +[TOC] + +# Equalizer APO compilation +## Compilation prerequisites +The following software has been successfully used to compile Equalizer APO. + +1. Visual C++ 2010 Professional. The [Express edition](http://www.visualstudio.com/de-de/downloads/download-visual-studio-vs#DownloadFamilies_4) should be sufficient, but does not contain a 64 bit compiler. The [Windows SDK](http://www.microsoft.com/en-us/download/details.aspx?id=8279) or [newer Visual Studio versions](http://www.visualstudio.com/de-de/downloads/download-visual-studio-vs#DownloadFamilies_2) may contain one. + +2. [Windows Driver Kit 7.1](http://www.microsoft.com/en-us/download/details.aspx?id=11800). Newer versions of the WDK should be usable as well, but might no longer support Windows Vista. The WDK 8.1 seems not to contain the APO header files. + +3. [libsndfile](http://www.mega-nerd.com/libsndfile/). There are installers for the 32 and 64 bit version, so no need to compile from source. This is currently only used for the Benchmark application. + +4. [TCLAP](http://tclap.sourceforge.net/). As this is a template library, only the source is needed, which will be compiled into the application. This library is also only used in the Benchmark application. + +5. [NSIS](http://nsis.sourceforge.net/). Needed to create the installer. Additionaly, the plugins [NSISpcre](http://nsis.sourceforge.net/NSISpcre_plug-in) and [AccessControl](http://nsis.sourceforge.net/AccessControl_plug-in) are needed. + +## Source code organization +The Equalizer APO project consists of four parts: + +* EqualizerAPO. This is the main project, which generates the Audio Processing Object DLL, EqualizerAPO.dll. It contains the boilerplate code for COM and implements the APO interfaces, calling the class ParametricEQ, which contains the actual filtering algorithm and is also used by the Benchmark project. + +* Configurator. This is the GUI utility which is called during the setup process to allow the user to select the audio devices for which the APO should be registered. + +* Benchmark. A console program to test the audio processing implementation without actually installing it for an audio device. It can be handy when experimenting with new filter types or tuning existing ones, especially to evaluate the performance. + +* Setup. Not a Visual Studio project, but a set of NSIS scripts and additional files that are used to create the installers. + +The file build.bat in the top-level directory uses msbuild to build all three Visual Studio projects for both 32 and 64 bit and then calls NSIS to create both installers. + +# APO development +An APO (Audio Processing Object) is a user-space program module that is loaded by the Windows Audio Service to process the audio sample data before it is sent to the audio device driver. APOs are normally distributed and installed together with the audio driver and have to be signed to make sure that they don't circumvent any audio-related DRM measures. There are two kinds of APOs, GFX (global effect, applied after mixing the audio streams together) and LFX (local effect, applied before mixing). Only one GFX and one LFX APO can be registered for an output device and only one LFX APO can be registered for an input device. An APO is implemented as a COM object which is assigned a GUID under which it is registered in the system registry. More detailed information and code samples can be found in the documents [Custom Audio Effects in Windows Vista](http://msdn.microsoft.com/en-us/windows/hardware/gg463025.aspx) and [Reusing Windows Vista Audio System Effects](http://msdn.microsoft.com/en-us/library/windows/hardware/gg463044.aspx). + +To add a custom APO to an audio device, two obstacles mentioned above have to be overcome: The audio engine has to be configured to allow unsigned APOs and the existing APO assigned to the audio device has to be attached to the custom APO, so that it can still process the audio data. + +The following changes have to be done to the registry to make the Windows Audio Service load the DLL file that implements the APO: + + +1. Under the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Audio the DWORD value DisableProtectedAudioDG has to be set to 1. This disables the signature check for APOs, so that unsigned APOs will be loaded. This also means that applications requiring a secure audio path may change their behaviour or refuse to output audio altogether. + +2. The APO COM class has to be registered under HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\ ***GUID***, where ***GUID*** is the GUID value identifying the APO. A class name has to be set as the default value of the GUID key. Inside the GUID key, the key InprocServer32 has to be created, whose default value has to be set to the path to the DLL file. Also, a value ThreadingModel has to be set to an appropriate value (see [here](http://msdn.microsoft.com/en-us/library/windows/desktop/ms682390%28v=vs.85%29.aspx)). + +3. Also the key HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AudioEngine\AudioProcessingObjects\ ***GUID*** has to be created, which is normally handled by the function RegisterAPO (declared in audioenginebaseapo.h in the Windows DDK). The corresponding function UnregisterAPO can be used to remove the key. + +4. The APO has to be registered for a specific device under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render\ ***endpoint GUID*** \FxProperties. The Render path contains output devices while the Capture path contains input devices. In the FxProperties key, the value {d04e05a6-594b-4fb6-a80d-01af5eed7d1d},1 defines the GUID of an LFX APO while {d04e05a6-594b-4fb6-a80d-01af5eed7d1d},2 defines the GUID for a GFX APO. Normally, these values already exist and refer to the audio driver's APOs. To register the custom APO, one of the values has to be replaced, so the existing values have to be saved somewhere else (Equalizer APO saves them in HKEY_LOCAL_MACHINE\SOFTWARE\EqualizerAPO\Child APOs). They are needed to restore the original values when uninstalling the custom APO and, as the custom APO is meant to be used in addition to the existing APOs, it has to load and call the original APO so that it can continue to perform its function. Since Windows 8.1, the values {d04e05a6-594b-4fb6-a80d-01af5eed7d1d},5 and {d04e05a6-594b-4fb6-a80d-01af5eed7d1d},6 are also used for LFX and GFX, respectively. When an APO is registered via the new values (ending in 5 or 6), any APO registered via an old value (ending in 1 or 2) is ignored. Also since Windows 8.1, there are the values {d3993a3f-99c2-4402-b5ec-a92a0367664b},5 and {d3993a3f-99c2-4402-b5ec-a92a0367664b},6, which seem to be specifying a set of processing modes (they are of type MULTI_SZ, so can contain multiple lines). Both of these normally need to be set to {C18E2F7E-933D-4965-B7D1-1EEF228D2AF3}, which is the default processing mode. \ No newline at end of file diff --git a/Wiki/Documentation.txt b/Wiki/Documentation.txt index bff0411..716992b 100644 --- a/Wiki/Documentation.txt +++ b/Wiki/Documentation.txt @@ -1,4 +1,4 @@ -Welcome to the Wiki of Equalizer APO. This is currently the only documentation resource for this project. To begin using Equalizer APO, you should read the tutorials. +Welcome to the Wiki of Equalizer APO. This is the documentation for users of Equalizer APO. Developers might also be interested in reading the [developer documentation](https://sourceforge.net/p/equalizerapo/wiki/Developer%20documentation/). To begin using Equalizer APO, you should read the tutorials. **Table of contents:**