diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b8b979..0d3936a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,14 @@ jobs: - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v2 + - name: Setup NuGet + uses: NuGet/setup-nuget@v2 + with: + version: '6.x' + + - name: Restore dependencies + run: nuget restore FrameworkArgb\FrameworkArgb.sln + - name: Build solution run: | msbuild FrameworkArgb\FrameworkArgb.sln /property:Configuration=${{ env.Configuration }} /property:Platform=${{ env.Platform }} @@ -38,6 +46,8 @@ jobs: cp FrameworkArgb\x64\${{ matrix.configuration }}\FrameworkArgb\FrameworkArgb.dll bundle\ cp FrameworkArgb\x64\${{ matrix.configuration }}\FrameworkArgb\FrameworkArgb.inf bundle\ cp FrameworkArgb\x64\${{ matrix.configuration }}\FrameworkArgb.pdb bundle\ + cp FrameworkArgb\x64\${{ matrix.configuration }}\Installer.exe bundle\ + cp FrameworkArgb\x64\${{ matrix.configuration }}\Installer.pdb bundle\ - name: Upload bundle uses: actions/upload-artifact@v4 diff --git a/FrameworkArgb/FrameworkArgb.sln b/FrameworkArgb/FrameworkArgb.sln index c56a63a..61c00ca 100644 --- a/FrameworkArgb/FrameworkArgb.sln +++ b/FrameworkArgb/FrameworkArgb.sln @@ -1,10 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.12.35707.178 d17.12 +VisualStudioVersion = 17.12.35707.178 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FrameworkArgb", "FrameworkArgb.vcxproj", "{599FF144-2A3A-4BC7-96C9-7E1BF2FC9CF4}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Installer", "Installer\Installer.vcxproj", "{40DB3049-D338-4C0E-9EA4-9745606B0550}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -25,6 +27,18 @@ Global {599FF144-2A3A-4BC7-96C9-7E1BF2FC9CF4}.Release|x64.ActiveCfg = Release|x64 {599FF144-2A3A-4BC7-96C9-7E1BF2FC9CF4}.Release|x64.Build.0 = Release|x64 {599FF144-2A3A-4BC7-96C9-7E1BF2FC9CF4}.Release|x64.Deploy.0 = Release|x64 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Debug|ARM.ActiveCfg = Debug|Win32 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Debug|ARM64.ActiveCfg = Debug|Win32 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Debug|x64.ActiveCfg = Debug|x64 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Debug|x64.Build.0 = Debug|x64 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Debug|x86.ActiveCfg = Debug|Win32 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Debug|x86.Build.0 = Debug|Win32 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Release|ARM.ActiveCfg = Release|Win32 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Release|ARM64.ActiveCfg = Release|Win32 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Release|x64.ActiveCfg = Release|x64 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Release|x64.Build.0 = Release|x64 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Release|x86.ActiveCfg = Release|Win32 + {40DB3049-D338-4C0E-9EA4-9745606B0550}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FrameworkArgb/FrameworkArgb.vcxproj b/FrameworkArgb/FrameworkArgb.vcxproj index 042de86..98c2ff7 100644 --- a/FrameworkArgb/FrameworkArgb.vcxproj +++ b/FrameworkArgb/FrameworkArgb.vcxproj @@ -125,7 +125,7 @@ sha256 - 0.0.0.2 + 0.0.0.3 diff --git a/FrameworkArgb/Installer/Installer.vcxproj b/FrameworkArgb/Installer/Installer.vcxproj new file mode 100644 index 0000000..d8da5e7 --- /dev/null +++ b/FrameworkArgb/Installer/Installer.vcxproj @@ -0,0 +1,181 @@ + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {40db3049-d338-4c0e-9ea4-9745606b0550} + Installer + 10.0.22621.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Use + precomp.h + stdcpplatest + + + Console + true + setupapi.lib;newdev.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Use + precomp.h + stdcpplatest + + + Console + true + true + true + setupapi.lib;newdev.lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Use + precomp.h + stdcpplatest + + + Console + true + setupapi.lib;newdev.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Use + precomp.h + stdcpplatest + + + Console + true + true + true + setupapi.lib;newdev.lib;%(AdditionalDependencies) + + + + + + Create + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + diff --git a/FrameworkArgb/Installer/LICENSE b/FrameworkArgb/Installer/LICENSE new file mode 100644 index 0000000..a23699d --- /dev/null +++ b/FrameworkArgb/Installer/LICENSE @@ -0,0 +1,23 @@ +MIT License +See LICENSE.ChromeOS for additional terms. + +Copyright (c) 2022 Dustin L. Howett + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/FrameworkArgb/Installer/main.cpp b/FrameworkArgb/Installer/main.cpp new file mode 100644 index 0000000..66b1b58 --- /dev/null +++ b/FrameworkArgb/Installer/main.cpp @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) Framework Computer +// Copyright (c) Dustin L. Howett +// +// Adapted from https://github.com/DHowett/FrameworkWindowsUtils/tree/main/Installer + +#include "precomp.h" + +#include // emit the GUIDs in FrameworkArgb into our own binary... +#include "../Public.h" + +#pragma region WIL Extensions +namespace wil::details { +template <> +struct string_maker> { + HRESULT make(PCWSTR source, size_t length) WI_NOEXCEPT try { + m_value = source ? std::vector(source, source + length) : std::vector(length, L'\0'); + return S_OK; + } catch(...) { + return E_OUTOFMEMORY; + } + + wchar_t* buffer() { return &m_value[0]; } + + HRESULT trim_at_existing_null(size_t length) { + m_value.resize(length); + return S_OK; + } + + std::vector release() { return std::vector(std::move(m_value)); } + + static PCWSTR get(const std::vector& value) { return value.data(); } + +private: + std::vector m_value; +}; +} // namespace wil::details + +using unique_hdevinfo = wil::unique_any; + +#pragma endregion + +static constexpr HRESULT S_REBOOT{(HRESULT)3}; + +static std::set> multiSzToSet(const std::vector& multiSz) { + std::set> r; + auto begin{multiSz.begin()}; + while(begin != multiSz.end()) { + const auto l{wcslen(&*begin)}; + if(l == 0) { // an empty string in a REG_MULTI_SZ signals the end + break; + } + r.insert(std::wstring_view{&*begin, l}); + begin += l + 1; + } + return r; +} + +static constexpr std::wstring_view gFrameworkArgbDeviceNodeHwidList{LR"(Root\FrameworkArgb)" + "\0\0", // double null-terminate as this is a HWID List + 18}; +static constexpr std::wstring_view gFrameworkArgbDeviceNode{gFrameworkArgbDeviceNodeHwidList.substr(0, 18)}; + +static HRESULT Install() { + BOOL reboot{FALSE}; + + std::filesystem::path infPath{L"FrameworkArgb.inf"}; + infPath = std::filesystem::absolute(infPath); + + GUID ClassGUID; + WCHAR ClassName[256]; + fwprintf(stderr, L"Running SetupDiGetINFClassW\r\n"); + RETURN_IF_WIN32_BOOL_FALSE( + SetupDiGetINFClassW(infPath.native().c_str(), &ClassGUID, ClassName, (DWORD)std::size(ClassName), 0)); + + unique_hdevinfo DeviceInfoSet; + fwprintf(stderr, L"Running DeviceInfoSet.reset(SetupDiCreateDeviceInfoList())\r\n"); + DeviceInfoSet.reset(SetupDiCreateDeviceInfoList(&ClassGUID, 0)); + RETURN_HR_IF(E_FAIL, !DeviceInfoSet); + + SP_DEVINFO_DATA DeviceInfoData{}; + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + // TODO: Fails if not run as admin + fwprintf(stderr, L"Running SetupDiCreateDeviceInfoW\r\n"); + RETURN_IF_WIN32_BOOL_FALSE(SetupDiCreateDeviceInfoW(DeviceInfoSet.get(), ClassName, &ClassGUID, nullptr, 0, + DICD_GENERATE_ID, &DeviceInfoData)); + + fwprintf(stderr, L"Running SetupDiSetDeviceRegistryPropertyW\r\n"); + RETURN_IF_WIN32_BOOL_FALSE(SetupDiSetDeviceRegistryPropertyW( + DeviceInfoSet.get(), &DeviceInfoData, SPDRP_HARDWAREID, (LPBYTE)gFrameworkArgbDeviceNodeHwidList.data(), + (DWORD)(gFrameworkArgbDeviceNodeHwidList.size() * sizeof(WCHAR)))); + + fwprintf(stderr, L"Running SetupDiCallClassInstaller\r\n"); + RETURN_IF_WIN32_BOOL_FALSE(SetupDiCallClassInstaller(DIF_REGISTERDEVICE, DeviceInfoSet.get(), &DeviceInfoData)); + + // TODO: Fails if driver not signed + fwprintf(stderr, L"Running UpdateDriverForPlugAndPlayDevicesW\r\n"); + RETURN_IF_WIN32_BOOL_FALSE(UpdateDriverForPlugAndPlayDevicesW(nullptr, gFrameworkArgbDeviceNodeHwidList.data(), + infPath.native().c_str(), 0, &reboot)); + return reboot ? S_REBOOT : S_OK; +} + +static HRESULT RemoveFrameworkArgbDeviceNode() { + unique_hdevinfo devs; + devs.reset(SetupDiGetClassDevsExW(nullptr, nullptr, nullptr, DIGCF_ALLCLASSES, nullptr, nullptr, nullptr)); + RETURN_HR_IF(E_FAIL, !devs); + + SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail{}; + devInfoListDetail.cbSize = sizeof(devInfoListDetail); + RETURN_HR_IF(E_FAIL, !SetupDiGetDeviceInfoListDetailW(devs.get(), &devInfoListDetail)); + + SP_DEVINFO_DATA devInfo{}; + devInfo.cbSize = sizeof(devInfo); + int found{}, failures{}; + for(DWORD devIndex = 0; SetupDiEnumDeviceInfo(devs.get(), devIndex, &devInfo); devIndex++) { + wchar_t devID[MAX_DEVICE_ID_LEN]; + RETURN_HR_IF(E_FAIL, + CR_SUCCESS != CM_Get_Device_IDW(devInfo.DevInst, devID, (DWORD)std::size(devID), 0)); + + std::vector hwIdsMultiSz; + DWORD dataType{}; + wil::AdaptFixedSizeToAllocatedResult(hwIdsMultiSz, [&](PWSTR buffer, size_t size, size_t* needed) { + DWORD neededBytes; + RETURN_IF_WIN32_BOOL_FALSE(SetupDiGetDeviceRegistryPropertyW( + devs.get(), &devInfo, SPDRP_HARDWAREID, &dataType, (PBYTE)buffer, + (DWORD)(size * sizeof(wchar_t)), &neededBytes)); + *needed = neededBytes / sizeof(wchar_t); + return S_OK; + }); + auto hwIds{multiSzToSet(hwIdsMultiSz)}; + if(hwIds.contains(gFrameworkArgbDeviceNode)) { + ++found; + fwprintf(stderr, L"[+] Removing %s... ", devID); + if(!SetupDiCallClassInstaller(DIF_REMOVE, devs.get(), &devInfo)) { + auto lastError{GetLastError()}; + failures++; + fwprintf(stderr, L"FAILED (%8.08lx)\r\n", HRESULT_FROM_SETUPAPI(lastError)); + } else { + fwprintf(stderr, L"OK\r\n"); + } + } + } + + if(!found) { + fwprintf(stderr, L"[+] No device nodes found.\r\n"); + } else { + fwprintf(stderr, L"[+] Removed %d device%s.\r\n", found - failures, + (found - failures) == 1 ? L"" : L"s"); + } + return found > 0 ? S_OK : S_FALSE; +} + +static HRESULT Remove() { + std::filesystem::path infPath{L"FrameworkArgb.inf"}; + infPath = std::filesystem::absolute(infPath); + + RETURN_IF_FAILED(RemoveFrameworkArgbDeviceNode()); + + fwprintf(stderr, L"[+] Removing FrameworkArgb.inf... "); + BOOL reboot; + if(!DiUninstallDriverW(nullptr, infPath.native().c_str(), 0, &reboot)) { + auto lastError{GetLastError()}; + fwprintf(stderr, L"FAILED (%8.08lx)\r\n", HRESULT_FROM_SETUPAPI(lastError)); + return HRESULT_FROM_SETUPAPI(lastError); + } else { + fwprintf(stderr, L"OK\r\n"); + } + + return S_OK; +} + +static int PrintUsage(wchar_t* progName) { + fwprintf(stderr, L"Usage: %s \r\n", progName); + return 1; +} + +int __cdecl wmain(_In_ int argc, _In_reads_(argc) PWSTR* argv) { + HRESULT res = S_OK; + if(argc < 2) { + return PrintUsage(argv[0]); + } + + if(wcscmp(argv[1], L"install") == 0) { + res = Install(); + } else if(wcscmp(argv[1], L"uninstall") == 0) { + res = Remove(); + } else { + return PrintUsage(argv[0]); + } + if (res == S_OK || res == S_REBOOT) + fwprintf(stderr, L"Success"); + else + fwprintf(stderr, L"Fail"); + return res; +} diff --git a/FrameworkArgb/Installer/packages.config b/FrameworkArgb/Installer/packages.config new file mode 100644 index 0000000..64523ba --- /dev/null +++ b/FrameworkArgb/Installer/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/FrameworkArgb/Installer/precomp.cpp b/FrameworkArgb/Installer/precomp.cpp new file mode 100644 index 0000000..ceeb0d6 --- /dev/null +++ b/FrameworkArgb/Installer/precomp.cpp @@ -0,0 +1 @@ +#include "precomp.h" diff --git a/FrameworkArgb/Installer/precomp.h b/FrameworkArgb/Installer/precomp.h new file mode 100644 index 0000000..d446641 --- /dev/null +++ b/FrameworkArgb/Installer/precomp.h @@ -0,0 +1,19 @@ +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include diff --git a/FrameworkArgb/install/install_argb_driver.bat b/FrameworkArgb/install/install_argb_driver.bat index c7142e1..4d083a5 100644 --- a/FrameworkArgb/install/install_argb_driver.bat +++ b/FrameworkArgb/install/install_argb_driver.bat @@ -24,7 +24,7 @@ if '%errorlevel%' NEQ '0' ( :-------------------------------------- echo **Installing Framework ARGB driver... -"%~dp0\devcon" install "%~dp0\FrameworkArgb.inf" root\FrameworkArgb +"%~dp0\Installer" install echo Done if not defined install_all cmd /k