From b13a08bdf41b11238a03a3f8b827d6309be69393 Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sat, 20 Feb 2016 10:03:33 +0100 Subject: [PATCH 1/2] Rewrote the native stuff to allow hot reloading of the SDK-library --- Native/_CUESDK.cs | 146 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 121 insertions(+), 25 deletions(-) diff --git a/Native/_CUESDK.cs b/Native/_CUESDK.cs index 87c6bf8..7271717 100644 --- a/Native/_CUESDK.cs +++ b/Native/_CUESDK.cs @@ -10,6 +10,8 @@ internal static class _CUESDK { #region Libary Management + private static IntPtr _dllHandle = IntPtr.Zero; + /// /// Gets the loaded architecture (x64/x86). /// @@ -17,72 +19,166 @@ internal static class _CUESDK static _CUESDK() { + LoadCUESDK(); + } + + /// + /// Reloads the SDK. + /// + internal static void Reload() + { + UnloadCUESDK(); + LoadCUESDK(); + } + + private static void LoadCUESDK() + { + if (_dllHandle != IntPtr.Zero) return; + // HACK: Load library at runtime to support both, x86 and x64 with one managed dll - LoadLibrary((LoadedArchitecture = Environment.Is64BitProcess ? "x64" : "x86") + "/CUESDK_2013.dll"); + _dllHandle = LoadLibrary((LoadedArchitecture = Environment.Is64BitProcess ? "x64" : "x86") + "/CUESDK_2013.dll"); + + _corsairSetLedsColorsPointer = (CorsairSetLedsColorsPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "CorsairSetLedsColors"), typeof(CorsairSetLedsColorsPointer)); + _corsairGetDeviceCountPointer = (CorsairGetDeviceCountPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "CorsairGetDeviceCount"), typeof(CorsairGetDeviceCountPointer)); + _corsairGetDeviceInfoPointer = (CorsairGetDeviceInfoPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "CorsairGetDeviceInfo"), typeof(CorsairGetDeviceInfoPointer)); + _corsairGetLedPositionsPointer = (CorsairGetLedPositionsPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "CorsairGetLedPositions"), typeof(CorsairGetLedPositionsPointer)); + _corsairGetLedIdForKeyNamePointer = (CorsairGetLedIdForKeyNamePointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "CorsairGetLedIdForKeyName"), typeof(CorsairGetLedIdForKeyNamePointer)); + _corsairRequestControlPointer = (CorsairRequestControlPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "CorsairRequestControl"), typeof(CorsairRequestControlPointer)); + _corsairPerformProtocolHandshakePointer = (CorsairPerformProtocolHandshakePointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "CorsairPerformProtocolHandshake"), typeof(CorsairPerformProtocolHandshakePointer)); + _corsairGetLastErrorPointer = (CorsairGetLastErrorPointer)Marshal.GetDelegateForFunctionPointer(GetProcAddress(_dllHandle, "CorsairGetLastError"), typeof(CorsairGetLastErrorPointer)); + } + + private static void UnloadCUESDK() + { + if (_dllHandle == IntPtr.Zero) return; + + // ReSharper disable once EmptyEmbeddedStatement - DarthAffe 20.02.2016: We might need to reduce the internal reference counter more than once to set the library free + while (FreeLibrary(_dllHandle)) ; + _dllHandle = IntPtr.Zero; } [DllImport("kernel32.dll")] private static extern IntPtr LoadLibrary(string dllToLoad); + [DllImport("kernel32.dll")] + private static extern bool FreeLibrary(IntPtr dllHandle); + + [DllImport("kernel32.dll")] + private static extern IntPtr GetProcAddress(IntPtr dllHandle, string name); + + #endregion + + #region SDK-METHODS + + #region Pointers + + private static CorsairSetLedsColorsPointer _corsairSetLedsColorsPointer; + private static CorsairGetDeviceCountPointer _corsairGetDeviceCountPointer; + private static CorsairGetDeviceInfoPointer _corsairGetDeviceInfoPointer; + private static CorsairGetLedPositionsPointer _corsairGetLedPositionsPointer; + private static CorsairGetLedIdForKeyNamePointer _corsairGetLedIdForKeyNamePointer; + private static CorsairRequestControlPointer _corsairRequestControlPointer; + private static CorsairPerformProtocolHandshakePointer _corsairPerformProtocolHandshakePointer; + private static CorsairGetLastErrorPointer _corsairGetLastErrorPointer; + + #endregion + + #region Delegates + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate bool CorsairSetLedsColorsPointer(int size, IntPtr ledsColors); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate int CorsairGetDeviceCountPointer(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate IntPtr CorsairGetDeviceInfoPointer(int deviceIndex); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate IntPtr CorsairGetLedPositionsPointer(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate CorsairKeyboardKeyId CorsairGetLedIdForKeyNamePointer(char keyName); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate bool CorsairRequestControlPointer(CorsairAccessMode accessMode); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate _CorsairProtocolDetails CorsairPerformProtocolHandshakePointer(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate CorsairError CorsairGetLastErrorPointer(); + #endregion - #region SDK-IMPORTS + // ReSharper disable EventExceptionNotDocumented /// /// CUE-SDK: set specified leds to some colors. The color is retained until changed by successive calls. This function does not take logical layout into account /// - [DllImport("CUESDK_2013.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern bool CorsairSetLedsColors(int size, IntPtr ledsColors); - - //#if WIN64 - // [DllImport("CUESDK.x64_2013.dll", CallingConvention = CallingConvention.Cdecl)] - //#else - // [DllImport("CUESDK_2013.dll", CallingConvention = CallingConvention.Cdecl)] - //#endif - //internal static extern bool CorsairSetLedsColorsAsync(int size, CorsairLedColor* ledsColors, void(*CallbackType)(void*, bool, CorsairError), void* context); + internal static bool CorsairSetLedsColors(int size, IntPtr ledsColors) + { + return _corsairSetLedsColorsPointer(size, ledsColors); + } /// /// CUE-SDK: returns number of connected Corsair devices that support lighting control. /// - [DllImport("CUESDK_2013.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern int CorsairGetDeviceCount(); + internal static int CorsairGetDeviceCount() + { + return _corsairGetDeviceCountPointer(); + } /// /// CUE-SDK: returns information about device at provided index /// - [DllImport("CUESDK_2013.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr CorsairGetDeviceInfo(int deviceIndex); + internal static IntPtr CorsairGetDeviceInfo(int deviceIndex) + { + return _corsairGetDeviceInfoPointer(deviceIndex); + } /// /// CUE-SDK: provides list of keyboard LEDs with their physical positions. /// - [DllImport("CUESDK_2013.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr CorsairGetLedPositions(); + internal static IntPtr CorsairGetLedPositions() + { + return _corsairGetLedPositionsPointer(); + } /// /// CUE-SDK: retrieves led id for key name taking logical layout into account. /// - [DllImport("CUESDK_2013.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern CorsairKeyboardKeyId CorsairGetLedIdForKeyName(char keyName); + internal static CorsairKeyboardKeyId CorsairGetLedIdForKeyName(char keyName) + { + return _corsairGetLedIdForKeyNamePointer(keyName); + } /// /// CUE-SDK: requestes control using specified access mode. /// By default client has shared control over lighting so there is no need to call CorsairRequestControl unless client requires exclusive control /// - [DllImport("CUESDK_2013.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern bool CorsairRequestControl(CorsairAccessMode accessMode); + internal static bool CorsairRequestControl(CorsairAccessMode accessMode) + { + return _corsairRequestControlPointer(accessMode); + } /// /// CUE-SDK: checks file and protocol version of CUE to understand which of SDK functions can be used with this version of CUE /// - [DllImport("CUESDK_2013.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern _CorsairProtocolDetails CorsairPerformProtocolHandshake(); + internal static _CorsairProtocolDetails CorsairPerformProtocolHandshake() + { + return _corsairPerformProtocolHandshakePointer(); + } /// /// CUE-SDK: returns last error that occured while using any of Corsair* functions /// - [DllImport("CUESDK_2013.dll", CallingConvention = CallingConvention.Cdecl)] - internal static extern CorsairError CorsairGetLastError(); + internal static CorsairError CorsairGetLastError() + { + return _corsairGetLastErrorPointer(); + } + + // ReSharper restore EventExceptionNotDocumented #endregion } From a64d27c5ae32daf50d3f438cd69be600f574104d Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sat, 20 Feb 2016 10:05:33 +0100 Subject: [PATCH 2/2] Implemented a reinitialize-functionality --- CueSDK.cs | 69 ++++++++++++++++++++++++++++ Devices/Generic/AbstractCueDevice.cs | 9 ++++ Devices/Generic/CorsairLed.cs | 16 ++++++- Examples/SimpleDevTest/Program.cs | 9 ++-- 4 files changed, 98 insertions(+), 5 deletions(-) diff --git a/CueSDK.cs b/CueSDK.cs index 011d0a4..400548e 100644 --- a/CueSDK.cs +++ b/CueSDK.cs @@ -1,5 +1,6 @@ // ReSharper disable MemberCanBePrivate.Global +using System.Collections.Generic; using System.Runtime.InteropServices; using CUE.NET.Devices.Generic; using CUE.NET.Devices.Generic.Enums; @@ -124,6 +125,74 @@ public static void Initialize(bool exclusiveAccess = false) } } + /// + /// Reinitialize the CUE-SDK and temporarily hand back full control to CUE. + /// + public static void Reinitialize() + { + Reinitialize(HasExclusiveAccess); + } + + /// + /// Reinitialize the CUE-SDK and temporarily hand back full control to CUE. + /// + /// Specifies whether the application should request exclusive access or not. + public static void Reinitialize(bool exclusiveAccess) + { + if (ProtocolDetails == null) + throw new WrapperException("CueSDK isn't initialized."); + + KeyboardSDK?.ResetLeds(); + MouseSDK?.ResetLeds(); + HeadsetSDK?.ResetLeds(); + + _CUESDK.Reload(); + + _CUESDK.CorsairPerformProtocolHandshake(); + + CorsairError error = LastError; + if (error != CorsairError.Success) + Throw(error); + + if (ProtocolDetails.BreakingChanges) + throw new WrapperException("The SDK currently used isn't compatible with the installed version of CUE.\r\n" + + $"CUE-Version: {ProtocolDetails.ServerVersion} (Protocol {ProtocolDetails.ServerProtocolVersion})\r\n" + + $"SDK-Version: {ProtocolDetails.SdkVersion} (Protocol {ProtocolDetails.SdkProtocolVersion})"); + + if (exclusiveAccess) + if (!_CUESDK.CorsairRequestControl(CorsairAccessMode.ExclusiveLightingControl)) + Throw(LastError); + HasExclusiveAccess = exclusiveAccess; + + int deviceCount = _CUESDK.CorsairGetDeviceCount(); + Dictionary reloadedDevices = new Dictionary(); + for (int i = 0; i < deviceCount; i++) + { + GenericDeviceInfo info = new GenericDeviceInfo((_CorsairDeviceInfo)Marshal.PtrToStructure(_CUESDK.CorsairGetDeviceInfo(i), typeof(_CorsairDeviceInfo))); + if (!info.CapsMask.HasFlag(CorsairDeviceCaps.Lighting)) + continue; // Everything that doesn't support lighting control is useless + + reloadedDevices.Add(info.Type, info); + + error = LastError; + if (error != CorsairError.Success) + Throw(error); + } + + if (KeyboardSDK != null) + if (!reloadedDevices.ContainsKey(CorsairDeviceType.Keyboard) + || KeyboardSDK.KeyboardDeviceInfo.Model != reloadedDevices[CorsairDeviceType.Keyboard].Model) + throw new WrapperException("The previously loaded Keyboard got disconnected."); + if (MouseSDK != null) + if (!reloadedDevices.ContainsKey(CorsairDeviceType.Mouse) + || MouseSDK.MouseDeviceInfo.Model != reloadedDevices[CorsairDeviceType.Mouse].Model) + throw new WrapperException("The previously loaded Mouse got disconnected."); + if (HeadsetSDK != null) + if (!reloadedDevices.ContainsKey(CorsairDeviceType.Headset) + || HeadsetSDK.HeadsetDeviceInfo.Model != reloadedDevices[CorsairDeviceType.Headset].Model) + throw new WrapperException("The previously loaded Headset got disconnected."); + } + private static void Throw(CorsairError error) { ProtocolDetails = null; diff --git a/Devices/Generic/AbstractCueDevice.cs b/Devices/Generic/AbstractCueDevice.cs index 7d3ce60..b29ff7a 100644 --- a/Devices/Generic/AbstractCueDevice.cs +++ b/Devices/Generic/AbstractCueDevice.cs @@ -296,6 +296,15 @@ protected void ManageException(Exception ex) OnException?.Invoke(this, new OnExceptionEventArgs(ex)); } + /// + /// Resets all loaded LEDs back to default. + /// + internal void ResetLeds() + { + foreach (CorsairLed led in Leds.Values) + led.Reset(); + } + #endregion } } diff --git a/Devices/Generic/CorsairLed.cs b/Devices/Generic/CorsairLed.cs index 9c3e3fe..78f9026 100644 --- a/Devices/Generic/CorsairLed.cs +++ b/Devices/Generic/CorsairLed.cs @@ -54,19 +54,33 @@ public Color Color #endregion #region Constructors - + internal CorsairLed() { } #endregion #region Methods + /// + /// Updates the LED to the requested color. + /// internal void Update() { _color = RequestedColor; IsUpdated = false; } + /// + /// Resets the LED back to default + /// + internal void Reset() + { + _color = Color.Transparent; + RequestedColor = Color.Transparent; + IsUpdated = false; + IsLocked = false; + } + #endregion } } diff --git a/Examples/SimpleDevTest/Program.cs b/Examples/SimpleDevTest/Program.cs index ad9274c..2b26511 100644 --- a/Examples/SimpleDevTest/Program.cs +++ b/Examples/SimpleDevTest/Program.cs @@ -44,8 +44,9 @@ public static void Main(string[] args) Wait(3); - keyboard.Brush = CueProfiles.LoadProfileByID()[null]; - keyboard.Update(); + CueSDK.Reinitialize(); + //keyboard.Brush = CueProfiles.LoadProfileByID()[null]; + //keyboard.Update(); Wait(3); @@ -54,8 +55,8 @@ public static void Main(string[] args) // OR work with a key group containing all keys and leave the background black - this should be always the prefered solution keyboard.Brush = new SolidColorBrush(Color.Black); keyboard.Update(); - keyboard.Brush = CueProfiles.LoadProfileByID()["K95 RGB Default 2"]; - keyboard.Update(); + //keyboard.Brush = CueProfiles.LoadProfileByID()["K95 RGB Default 2"]; + //keyboard.Update(); Wait(3);