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