# HG changeset patch # User StephaneLenclud # Date 1422877866 -3600 # Node ID e9aefd454d1e1f0567ed91ee3bf9c4e804dd8f4a # Parent 9ffcd8ed85375bba8b9879b23f24664381c9e8cd At last we can output stuff on our display. The whole thing is still quite broken though. Rebase: No start-up but that's ok. diff -r 9ffcd8ed8537 -r e9aefd454d1e GUI/MainForm.cs --- a/GUI/MainForm.cs Mon Feb 04 00:47:01 2013 +0100 +++ b/GUI/MainForm.cs Mon Feb 02 12:51:06 2015 +0100 @@ -39,6 +39,7 @@ private Color[] plotColorPalette; private SystemTray systemTray; private SoundGraphDisplay soundGraphDisplay; + private bool displayTick; private StartupManager startupManager = new StartupManager(); private UpdateVisitor updateVisitor = new UpdateVisitor(); private SensorGadget gadget; @@ -318,18 +319,9 @@ InitializePlotForm(); - // - if (soundGraphDisplay.IsDllLoaded) - { - //Try init - //NativeWindow window; - soundGraphDisplay.Init(Handle); - //= Window.GetWindow(this); - //var wih = new WindowInteropHelper(window); - //IntPtr hWnd = wih.Handle; - } - + soundGraphDisplay.Init(); + startupMenuItem.Visible = startupManager.IsAvailable; @@ -555,6 +547,19 @@ if (wmiProvider != null) wmiProvider.Update(); + if (soundGraphDisplay != null) + { + displayTick=!displayTick; + if (displayTick) + { + soundGraphDisplay.SetText(" ---", ""); + } + else + { + soundGraphDisplay.SetText(" -+-", ""); + } + } + if (logSensors != null && logSensors.Value && delayCount >= 4) logger.Log(); @@ -777,41 +782,8 @@ const int SC_MINIMIZE = 0xF020; const int SC_CLOSE = 0xF060; - if (m.Msg == SoundGraph.WM_DSP_PLUGIN_NOTIFY) - { - //Handling messages from our iMON Display - switch ((SoundGraph.DSPNotifyCode)m.WParam.ToInt32()) - { - case SoundGraph.DSPNotifyCode.DSPNM_PLUGIN_SUCCEED: - case SoundGraph.DSPNotifyCode.DSPNM_IMON_RESTARTED: - case SoundGraph.DSPNotifyCode.DSPNM_HW_CONNECTED: - { - //Connection with our display is now open - //Check if we have LCD or VFD - //if ((lParam & DSPN_DSP_VFD) == DSPN_DSP_VFD) m_bVfdConnected = TRUE; - //if ((lParam & DSPN_DSP_LCD) == DSPN_DSP_LCD) m_bLcdConnected = TRUE; - soundGraphDisplay.DisplayPluginMessage(m.WParam.ToInt32(),false); - } - break; - - case SoundGraph.DSPNotifyCode.DSPNM_PLUGIN_FAILED: - case SoundGraph.DSPNotifyCode.DSPNM_HW_DISCONNECTED: - case SoundGraph.DSPNotifyCode.DSPNM_IMON_CLOSED: - { - //Connection with our display is closed - soundGraphDisplay.DisplayPluginMessage(m.LParam.ToInt32(), true); - } - break; - - case SoundGraph.DSPNotifyCode.DSPNM_LCD_TEXT_SCROLL_DONE: - { - //Scroll finnished - } - break; - } - - } - else if (minimizeToTray.Value && + + if (minimizeToTray.Value && m.Msg == WM_SYSCOMMAND && m.WParam.ToInt64() == SC_MINIMIZE) { SysTrayHideShow(); diff -r 9ffcd8ed8537 -r e9aefd454d1e GUI/SoundGraphDisplay.cs --- a/GUI/SoundGraphDisplay.cs Mon Feb 04 00:47:01 2013 +0100 +++ b/GUI/SoundGraphDisplay.cs Mon Feb 02 12:51:06 2015 +0100 @@ -12,164 +12,18 @@ using System.Collections.Generic; using System.Drawing; using System.Text; +using System.Diagnostics; using System.Windows.Forms; using System.Windows; using OpenHardwareMonitor.Hardware; using OpenHardwareMonitor.Utilities; using System.Runtime.InteropServices; +using UacHelpers; -static class NativeMethods -{ - [System.Flags] - public enum LoadLibraryFlags : uint - { - DONT_RESOLVE_DLL_REFERENCES = 0x00000001, - LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010, - LOAD_LIBRARY_AS_DATAFILE = 0x00000002, - LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040, - LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020, - LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008 - } - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr LoadLibrary(string dllToLoad); - - [DllImport("kernel32.dll")] - public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool FreeLibrary(IntPtr hModule); -} - -/** -Definitions taken from public Sound Graph APIs. - */ -static class SoundGraph -{ - - const int WM_APP = 0x8000; - public const int WM_DSP_PLUGIN_NOTIFY = WM_APP + 1121; - /**DSPResult - @brief These enumeration values represent the returned result for iMON Display API function calls.\n - All iMON Display API function calls return one of this result values.\n - For meaning of each result, refer the comment of each line below*/ - public enum DSPResult : int - { - DSP_SUCCEEDED = 0, //// Function Call Succeeded Without Error - DSP_E_FAIL, //// Unspecified Failure - DSP_E_OUTOFMEMORY, //// Failed to Allocate Necessary Memory - DSP_E_INVALIDARG, //// One or More Arguments Are Not Valid - DSP_E_NOT_INITED, //// API is Not Initialized - DSP_E_POINTER, //// Pointer is Not Valid - - DSP_S_INITED = 0x1000, //// API is Initialized - DSP_S_NOT_INITED, //// API is Not Initialized - DSP_S_IN_PLUGIN_MODE, //// API Can Control iMON Display (Display Plug-in Mode) - DSP_S_NOT_IN_PLUGIN_MODE, //// API Can't Control iMON Display - }; - - - /**DSPNInitResult - @brief These enumeration values represent the result status for requesting Display Plug-in Mode to iMON.\n - iMON Display API notifies one of this result values to the caller application after requesting Display Plug-in Mode to iMON.\n - For more information, refer the comment of each line below*/ - public enum DSPNInitResult : int - { - DSPN_SUCCEEDED = 0, //// Display Plug-in Mode is Initialized Successfully - DSPN_ERR_IN_USED = 0x0100, //// Display Plug-in is Already Used by Other Application - DSPN_ERR_HW_DISCONNECTED, //// iMON HW is Not Connected - DSPN_ERR_NOT_SUPPORTED_HW, //// The Connected iMON HW doesn't Support Display Plug-in - DSPN_ERR_PLUGIN_DISABLED, //// Display Plug-in Mode Option is Disabled - DSPN_ERR_IMON_NO_REPLY, //// The Latest iMON is Not Installed or iMON Not Running - DSPN_ERR_UNKNOWN = 0x0200, //// Unknown Failure - }; - - - /**DSPType - @brief These enumeration values represent display type.\n - Currently iMON Display API supports VFD and LCD products.*/ - public enum DSPType : int - { - DSPN_DSP_NONE = 0, - DSPN_DSP_VFD = 0x01, //// VFD products - DSPN_DSP_LCD = 0x02, //// LCD products - }; - - - /**DSPNotifyCode - @brief These enumeration values represent the notification codes.\n - iMON Display API will send or post message to the caller application.\n - The caller application should assign the message and the winodw handle to receivce message with IMON_Display_Init fucntion.\n - These enumeration values are used with WPARAM parameter of the message.\n - For more information, see the explanation of each notification code below*/ - public enum DSPNotifyCode : int - { - /**DSPNM_PLUGIN_SUCCEED - @brief When API succeeds to get the control for the display, API will post caller-specified message with DSPNM_PLUGIN_SUCCEED as WPARAM parameter.\n - LPARAM represents DSPType. This value can be 0x01 (VFD), 0x02 (LCD) or 0x03 (VFD+LCD).*/ - DSPNM_PLUGIN_SUCCEED = 0, - - /**DSPNM_PLUGIN_FAILED - @brief When API fails to get the control for the display, API will post caller-specified message with DSPNM_PLUGIN_FAILED as WPARAM parameter.\n - LPARAM represents error code with DSPNResult.*/ - DSPNM_PLUGIN_FAILED, - - /**DSPNM_IMON_RESTARTED - @brief When iMON starts, API will post caller-specified message with DSPNM_IMON_RESTARTED as WPARAM parameter.\n - LPARAM represents DSPType. This value can be 0 (No Display), 0x01 (VFD), 0x02 (LCD) or 0x03 (VFD+LCD).*/ - DSPNM_IMON_RESTARTED, - - /**DSPNM_IMON_CLOSED - @brief When iMON closed, API will post caller-specified message with DSPNM_IMON_CLOSED as WPARAM parameter.\n - LPARAM is not used.*/ - DSPNM_IMON_CLOSED, - - /**DSPNM_HW_CONNECTED - @brief When iMON HW newly connected, API will post caller-specified message with DSPNM_HW_CONNECTED as WPARAM parameter.\n - LPARAM represents DSPType. This value can be 0 (No Display), 0x01 (VFD), 0x02 (LCD) or 0x03 (VFD+LCD).*/ - DSPNM_HW_CONNECTED, - - /**DSPNM_HW_DISCONNECTED - @brief When iMON HW disconnected, API will post caller-specified message with DSPNM_HW_DISCONNECTED as WPARAM parameter.\n - LPARAM is DSPNResult value, DSPN_ERR_HW_DISCONNECTED.*/ - DSPNM_HW_DISCONNECTED, - - - /**DSPNM_LCD_TEXT_SCROLL_DONE - @brief When iMON LCD finishes scrolling Text, API will post caller-specified message with DSPNM_LCD_TEXT_SCROLL_DONE as WPARAM parameter.\n - The caller application may need to know when text scroll is finished, for sending next text.\n - LPARAM is not used.*/ - DSPNM_LCD_TEXT_SCROLL_DONE = 0x1000, - }; - - /// Functions - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate DSPResult IMON_Display_Uninit(); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate DSPResult IMON_Display_Init(IntPtr hwndNoti, uint uMsgNotification); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate DSPResult IMON_Display_IsInited(); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate DSPResult IMON_Display_IsPluginModeEnabled(); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate DSPResult IMON_Display_SetVfdText( - [MarshalAs(UnmanagedType.LPWStr)] string lpsz1stLine, - [MarshalAs(UnmanagedType.LPWStr)] string lpsz2ndLine); - - //IMONDSPAPI DSPResult IMON_Display_SetVfdText(LPCTSTR lpsz1stLine, LPCTSTR lpsz2ndLine); - -} - namespace OpenHardwareMonitor.GUI @@ -180,14 +34,8 @@ private PersistentSettings settings; private UnitManager unitManager; private List list = new List(); - //private bool mainIconEnabled = false; - //private NotifyIconAdv mainIcon; - IntPtr iSoundGraphDll; - SoundGraph.IMON_Display_Uninit iIMON_Display_Uninit; - SoundGraph.IMON_Display_Init iIMON_Display_Init; - SoundGraph.IMON_Display_IsInited iIMON_Display_IsInited; - SoundGraph.IMON_Display_IsPluginModeEnabled iIMON_Display_IsPluginModeEnabled; - SoundGraph.IMON_Display_SetVfdText iIMON_Display_SetVfdText; + private SoundGraph.Server iServer; + public SoundGraphDisplay(IComputer computer, PersistentSettings settings, UnitManager unitManager) @@ -198,38 +46,23 @@ computer.HardwareAdded += new HardwareEventHandler(HardwareAdded); computer.HardwareRemoved += new HardwareEventHandler(HardwareRemoved); + //Start our client if needed + Process[] processes = Process.GetProcessesByName("SoundGraphAccess"); + if (!(processes.Length > 0)) + { + + //Process client = UserAccountControl.CreateProcessAsStandardUser(@"D:\Dev\SoundGraphAccess\Debug\SoundGraphAccess.exe",""); + /* + Process client = new Process(); + client.StartInfo.FileName = @"D:\Dev\SoundGraphAccess\Debug\SoundGraphAccess.exe"; + client.StartInfo.WorkingDirectory = @"D:\Dev\SoundGraphAccess"; + client.Start();*/ + } + //Try loading SoundGraph iMON Disaply DLL - iSoundGraphDll = NativeMethods.LoadLibraryEx(@"iMONDisplay.dll", IntPtr.Zero, NativeMethods.LoadLibraryFlags.LOAD_WITH_ALTERED_SEARCH_PATH); - int err=Marshal.GetLastWin32Error(); //If you 193 it means you need build for x86 - if (err == 193) - { - Console.Write(@"iMON: Cannot load x86 DLL from x64 process."); - } - else if (err != 0) - { - Console.Write(@"iMON: Error: %i - Failed to load iMONDisplay.dll", err); - } - else - { - Console.Write(@"iMON: DLL loaded."); - //Gather our function pointers - //TODO: Check returned pointers for validity - IntPtr functionPointer = NativeMethods.GetProcAddress(iSoundGraphDll, "IMON_Display_Uninit"); - iIMON_Display_Uninit = (SoundGraph.IMON_Display_Uninit)Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(SoundGraph.IMON_Display_Uninit)); - - functionPointer = NativeMethods.GetProcAddress(iSoundGraphDll, "IMON_Display_Init"); - iIMON_Display_Init = (SoundGraph.IMON_Display_Init)Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(SoundGraph.IMON_Display_Init)); - - functionPointer = NativeMethods.GetProcAddress(iSoundGraphDll, "IMON_Display_IsInited"); - iIMON_Display_IsInited = (SoundGraph.IMON_Display_IsInited)Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(SoundGraph.IMON_Display_IsInited)); - - functionPointer = NativeMethods.GetProcAddress(iSoundGraphDll, "IMON_Display_IsPluginModeEnabled"); - iIMON_Display_IsPluginModeEnabled = (SoundGraph.IMON_Display_IsPluginModeEnabled)Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(SoundGraph.IMON_Display_IsPluginModeEnabled)); - - functionPointer = NativeMethods.GetProcAddress(iSoundGraphDll, "IMON_Display_SetVfdText"); - iIMON_Display_SetVfdText = (SoundGraph.IMON_Display_SetVfdText)Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(SoundGraph.IMON_Display_SetVfdText)); - - } + iServer = new SoundGraph.Server(@"\\.\pipe\sga-receiver", @"\\.\pipe\sga-sender"); + iServer.Start(); + //iServer.SendMessage("init:"); } private void HardwareRemoved(IHardware hardware) @@ -270,11 +103,7 @@ foreach (SensorFrontView icon in list) icon.Dispose(); - //Unload our DLL - if (iSoundGraphDll != IntPtr.Zero) - { - bool result = NativeMethods.FreeLibrary(iSoundGraphDll); - } + iServer.Stop(); } @@ -349,19 +178,19 @@ */ } - public void Init(IntPtr aHWND) + public void Init() { - iIMON_Display_Init(aHWND, SoundGraph.WM_DSP_PLUGIN_NOTIFY); + iServer.SendMessage("init:"); } public void Uninit() { - iIMON_Display_Uninit(); + iServer.SendMessage("uninit:"); } public void SetText(string aUpperLine, string aLowerLine) { - + iServer.SendMessage("set-vfd-text:" + aUpperLine); } /* @@ -378,47 +207,13 @@ } }*/ - + /* public bool IsDllLoaded { get { return iSoundGraphDll!=IntPtr.Zero; } } + */ - public void DisplayPluginMessage(int uErrCode, bool bError) - { - if(bError) - { - switch ((SoundGraph.DSPNInitResult)uErrCode) - { - case SoundGraph.DSPNInitResult.DSPN_ERR_IN_USED: - Console.Write("Display Plug-in is Already Used by Other Application.\n"); break; - case SoundGraph.DSPNInitResult.DSPN_ERR_HW_DISCONNECTED: - Console.Write("iMON HW is Not Connected."); break; - case SoundGraph.DSPNInitResult.DSPN_ERR_NOT_SUPPORTED_HW: - Console.Write("The Connected iMON HW doesn't Support Display Plug-in.\n"); break; - case SoundGraph.DSPNInitResult.DSPN_ERR_PLUGIN_DISABLED: - Console.Write("Display Plug-in Mode Option is Disabled.\n"); break; - case SoundGraph.DSPNInitResult.DSPN_ERR_IMON_NO_REPLY: - Console.Write("The Latest iMON is Not Installed or iMON Not Running.\n"); break; - case SoundGraph.DSPNInitResult.DSPN_ERR_UNKNOWN: - Console.Write("Unknown Failure.\n"); break; - } - } - else - { - switch ((SoundGraph.DSPNotifyCode)uErrCode) - { - case SoundGraph.DSPNotifyCode.DSPNM_PLUGIN_SUCCEED: - Console.Write("Plug-in Mode Inited Successfully.\n"); break; - case SoundGraph.DSPNotifyCode.DSPNM_IMON_RESTARTED: - Console.Write("iMON Started and Plug-in Mode Inited.\n"); break; - case SoundGraph.DSPNotifyCode.DSPNM_HW_CONNECTED: - Console.Write("iMON HW Connected and Plug-in Mode Inited.\n"); break; - } - } - - //GetDlgItem(IDC_STATIC_INFO)->SetWindowText((LPCTSTR)strErrMsg); - } } } diff -r 9ffcd8ed8537 -r e9aefd454d1e GUI/SoundGraphServer.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GUI/SoundGraphServer.cs Mon Feb 02 12:51:06 2015 +0100 @@ -0,0 +1,220 @@ +using System; +using Microsoft.Win32.SafeHandles; +using System.Text; +using System.Runtime.InteropServices; +using System.Threading; +using System.IO; +using System.Diagnostics; + +namespace SoundGraph +{ + public class Server + { + [DllImport("kernel32.dll", SetLastError = true)] + public static extern SafeFileHandle CreateNamedPipe( + String pipeName, + uint dwOpenMode, + uint dwPipeMode, + uint nMaxInstances, + uint nOutBufferSize, + uint nInBufferSize, + uint nDefaultTimeOut, + IntPtr lpSecurityAttributes); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int ConnectNamedPipe( + SafeFileHandle hNamedPipe, + IntPtr lpOverlapped); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int DisconnectNamedPipe( + SafeFileHandle hNamedPipe); + + public const uint PIPE_ACCESS_DUPLEX = (0x00000003); + public const uint FILE_FLAG_OVERLAPPED = (0x40000000); + public const uint PIPE_ACCESS_OUTBOUND = (0x00000002); + public const uint PIPE_ACCESS_INBOUND = (0x00000001); + public const uint PIPE_TYPE_BYTE = (0x00000000); + public const uint PIPE_UNLIMITED_INSTANCES = 255; + + + + + public const int BUFFER_SIZE = 256; + + // + public string iPipeNameOutbound; + Thread iThreadOutbound; + SafeFileHandle iPipeOutbound; + public FileStream iStreamOutbound; + // + public string iPipeNameInbound; + Thread iThreadInbound; + SafeFileHandle iPipeInbound; + public FileStream iStreamInbound; + + + public Server(string aPipeNameOutbound, string aPipeNameInbound) + { + iPipeNameOutbound = aPipeNameOutbound; + iPipeNameInbound = aPipeNameInbound; + } + + /** + * Start our services. + */ + public void Start() + { + //Start outbound thread to send messages + this.iThreadOutbound = new Thread(new ThreadStart(ThreadOutbound)); + this.iThreadOutbound.Start(); + //Start inbound thread to receive messages + this.iThreadInbound = new Thread(new ThreadStart(ThreadInbound)); + this.iThreadInbound.Start(); + } + + /** + * Outbound thread is sending messages to our client. + */ + private void ThreadOutbound() + { + + //Create our outbound named pipe + iPipeOutbound = CreateNamedPipe(this.iPipeNameOutbound, PIPE_ACCESS_OUTBOUND /*| FILE_FLAG_OVERLAPPED*/, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, BUFFER_SIZE, BUFFER_SIZE, 0, IntPtr.Zero); + + //Could not create named pipe + if (iPipeOutbound.IsInvalid) + { + //TODO: error handling + return; + } + + //Will complete once our client connects + int success = ConnectNamedPipe(iPipeOutbound, IntPtr.Zero); + + //could not connect client + if (success == 0) + { + //TODO: error handling + return; + } + + //Client now connected create our stream + iStreamOutbound = new FileStream(iPipeOutbound, FileAccess.Write, BUFFER_SIZE, false); + + } + + /** + * Inbound thread is receiving messages from our client + */ + private void ThreadInbound() + { + //Client client = (Client)clientObj; + //clientse.stream = new FileStream(clientse.handle, FileAccess.ReadWrite, BUFFER_SIZE, true); + + iPipeInbound = CreateNamedPipe(this.iPipeNameInbound, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, BUFFER_SIZE, BUFFER_SIZE, 0, IntPtr.Zero); + + //could not create named pipe + if (iPipeInbound.IsInvalid) + return; + + //Will complete once a client connects + int success = ConnectNamedPipe(iPipeInbound, IntPtr.Zero); + + //could not connect client + if (success == 0) + return; + + //Client now connected create our inbound stream + iStreamInbound = new FileStream(iPipeInbound, FileAccess.Read, BUFFER_SIZE, false); + + + byte[] buffer = null; + ASCIIEncoding encoder = new ASCIIEncoding(); + + while (true) + { + int bytesRead = 0; + + try + { + buffer = new byte[BUFFER_SIZE]; + bytesRead = iStreamInbound.Read(buffer, 0, BUFFER_SIZE); + } + catch + { + //read error has occurred + break; + } + + //client has disconnected + if (bytesRead == 0) + break; + + //fire message received event + //if (this.MessageReceived != null) + // this.MessageReceived(clientse, encoder.GetString(buffer, 0, bytesRead)); + + int ReadLength = 0; + for (int i = 0; i < BUFFER_SIZE; i++) + { + //if (buffer[i].ToString("x2") != "cc") + if (buffer[i] != 0) + { + ReadLength++; + } + else + break; + } + if (ReadLength > 0) + { + byte[] Rc = new byte[ReadLength]; + Buffer.BlockCopy(buffer, 0, Rc, 0, ReadLength); + + Console.WriteLine(encoder.GetString(Rc, 0, ReadLength)); + Trace.WriteLine("Received " + ReadLength + " Bytes: " + encoder.GetString(Rc, 0, ReadLength)); + buffer.Initialize(); + } + + } + + //clean up resources + iStreamInbound.Close(); + iPipeInbound.Close(); + } + + /** + * Send a message to our client. + */ + public void SendMessage(string message) + { + + ASCIIEncoding encoder = new ASCIIEncoding(); + byte[] messageBuffer = encoder.GetBytes(message); + + if (iStreamOutbound.CanWrite) + { + iStreamOutbound.Write(messageBuffer, 0, messageBuffer.Length); + iStreamOutbound.Flush(); + } + + + } + + /** + * + */ + public void Stop() + { + //clean up resources + + DisconnectNamedPipe(this.iPipeOutbound); + + //TODO: more cleanup + + + this.iThreadOutbound.Abort(); + } + + } +} diff -r 9ffcd8ed8537 -r e9aefd454d1e OpenHardwareMonitor.csproj --- a/OpenHardwareMonitor.csproj Mon Feb 04 00:47:01 2013 +0100 +++ b/OpenHardwareMonitor.csproj Mon Feb 02 12:51:06 2015 +0100 @@ -113,6 +113,7 @@ + Component @@ -215,6 +216,10 @@ {B0397530-545A-471D-BB74-027AE456DF1A} OpenHardwareMonitorLib + + {D043A646-FE7A-4334-B23D-E327593C1AE2} + UacHelpers.UserAccountControl + diff -r 9ffcd8ed8537 -r e9aefd454d1e OpenHardwareMonitor.sln --- a/OpenHardwareMonitor.sln Mon Feb 04 00:47:01 2013 +0100 +++ b/OpenHardwareMonitor.sln Mon Feb 02 12:51:06 2015 +0100 @@ -8,20 +8,61 @@ {B0397530-545A-471D-BB74-027AE456DF1A} = {B0397530-545A-471D-BB74-027AE456DF1A} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UacHelpers.UserAccountControl", "UacHelpers.CppLibrary\UacHelpers.CppLibrary.vcxproj", "{D043A646-FE7A-4334-B23D-E327593C1AE2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {B0397530-545A-471D-BB74-027AE456DF1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B0397530-545A-471D-BB74-027AE456DF1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0397530-545A-471D-BB74-027AE456DF1A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B0397530-545A-471D-BB74-027AE456DF1A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B0397530-545A-471D-BB74-027AE456DF1A}.Debug|Win32.ActiveCfg = Debug|Any CPU + {B0397530-545A-471D-BB74-027AE456DF1A}.Debug|Win32.Build.0 = Debug|Any CPU + {B0397530-545A-471D-BB74-027AE456DF1A}.Debug|x64.ActiveCfg = Debug|Any CPU {B0397530-545A-471D-BB74-027AE456DF1A}.Release|Any CPU.ActiveCfg = Release|Any CPU {B0397530-545A-471D-BB74-027AE456DF1A}.Release|Any CPU.Build.0 = Release|Any CPU + {B0397530-545A-471D-BB74-027AE456DF1A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B0397530-545A-471D-BB74-027AE456DF1A}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B0397530-545A-471D-BB74-027AE456DF1A}.Release|Win32.ActiveCfg = Release|Any CPU + {B0397530-545A-471D-BB74-027AE456DF1A}.Release|x64.ActiveCfg = Release|Any CPU {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|Win32.ActiveCfg = Debug|Any CPU + {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|Win32.Build.0 = Debug|Any CPU + {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|x64.ActiveCfg = Debug|Any CPU {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|Any CPU.ActiveCfg = Release|Any CPU {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|Any CPU.Build.0 = Release|Any CPU + {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|Win32.ActiveCfg = Release|Any CPU + {F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|x64.ActiveCfg = Release|Any CPU + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Debug|Any CPU.ActiveCfg = Debug|x64 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Debug|Any CPU.Build.0 = Debug|x64 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Debug|Mixed Platforms.Build.0 = Debug|x64 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Debug|Win32.ActiveCfg = Debug|Win32 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Debug|Win32.Build.0 = Debug|Win32 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Debug|x64.ActiveCfg = Debug|x64 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Debug|x64.Build.0 = Debug|x64 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Release|Any CPU.ActiveCfg = Release|x64 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Release|Mixed Platforms.ActiveCfg = Release|x64 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Release|Mixed Platforms.Build.0 = Release|x64 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Release|Win32.ActiveCfg = Release|Win32 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Release|Win32.Build.0 = Release|Win32 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Release|x64.ActiveCfg = Release|x64 + {D043A646-FE7A-4334-B23D-E327593C1AE2}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff -r 9ffcd8ed8537 -r e9aefd454d1e UacHelpers.CppLibrary/AssemblyInfo.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UacHelpers.CppLibrary/AssemblyInfo.cpp Mon Feb 02 12:51:06 2015 +0100 @@ -0,0 +1,38 @@ +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly:AssemblyTitleAttribute("UAC Helpers")]; +[assembly:AssemblyDescriptionAttribute("User Account Control C++/CLI Library")]; +[assembly:AssemblyConfigurationAttribute("")]; +[assembly:AssemblyCompanyAttribute("Sela Group")]; +[assembly:AssemblyProductAttribute("")]; +[assembly:AssemblyCopyrightAttribute("Copyright (c) Sasha Goldshtein 2008")]; +[assembly:AssemblyTrademarkAttribute("")]; +[assembly:AssemblyCultureAttribute("")]; + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the value or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly:AssemblyVersionAttribute("1.0.0.0")]; + +[assembly:ComVisible(false)]; + +[assembly:CLSCompliantAttribute(true)]; + +[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)]; diff -r 9ffcd8ed8537 -r e9aefd454d1e UacHelpers.CppLibrary/UacHelpers.CppLibrary.vcxproj --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UacHelpers.CppLibrary/UacHelpers.CppLibrary.vcxproj Mon Feb 02 12:51:06 2015 +0100 @@ -0,0 +1,216 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + v2.0 + UacHelpers.UserAccountControl + {D043A646-FE7A-4334-B23D-E327593C1AE2} + UacHelpersCppLibrary + + + + + + + + + ManagedCProj + + + + DynamicLibrary + Unicode + true + true + + + DynamicLibrary + Unicode + true + + + DynamicLibrary + Unicode + true + true + + + DynamicLibrary + Unicode + true + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + true + Level3 + ProgramDatabase + + + + + true + true + false + + + MachineX86 + + + + + X64 + + + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + true + Level3 + ProgramDatabase + + + + + true + true + false + + + MachineX64 + + + + + WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreadedDLL + + + true + Level3 + ProgramDatabase + + + + + true + false + + + MachineX86 + + + + + X64 + + + WIN32;NDEBUG;%(PreprocessorDefinitions) + MultiThreadedDLL + + + true + Level3 + ProgramDatabase + + + + + true + false + + + MachineX64 + + + + + true + true + + + true + true + + + true + true + + + + + + + + + + + + + \ No newline at end of file diff -r 9ffcd8ed8537 -r e9aefd454d1e UacHelpers.CppLibrary/UserAccountControl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UacHelpers.CppLibrary/UserAccountControl.cpp Mon Feb 02 12:51:06 2015 +0100 @@ -0,0 +1,226 @@ +#include "UserAccountControl.h" + +#include +#pragma comment (lib, "kernel32.lib") +#pragma comment (lib, "advapi32.lib") + +#include +#include +#include + +using namespace msclr::interop; + +using namespace System::ComponentModel; +using namespace Microsoft::Win32; + +namespace UacHelpers { + + Process^ UserAccountControl::CreateProcessAsAdmin(System::String^ exePath, System::String^ arguments) + { + ProcessStartInfo^ psi = gcnew ProcessStartInfo(exePath, arguments); + psi->UseShellExecute = true; + psi->Verb = "runas"; + return Process::Start(psi); + } + + Process^ UserAccountControl::CreateProcessAsStandardUser(System::String^ exePath, System::String^ arguments) + { + marshal_context context; + + //If the current process is not elevated, then there's no reason to go through the hassle -- + //just use the standard System.Diagnostics.Process facilities. + if (!IsCurrentProcessElevated) + { + return Process::Start(exePath, arguments); + } + + //The following implementation is roughly based on Aaron Margosis' post: + //http://blogs.msdn.com/aaron_margosis/archive/2009/06/06/faq-how-do-i-start-a-program-as-the-desktop-user-from-an-elevated-app.aspx + + //Enable SeIncreaseQuotaPrivilege in this process. (This requires administrative privileges.) + HANDLE hProcessToken = NULL; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken)) + { + throw gcnew Win32Exception(GetLastError()); + } + else + { + TOKEN_PRIVILEGES tkp; + tkp.PrivilegeCount = 1; + LookupPrivilegeValueW(NULL, SE_INCREASE_QUOTA_NAME, &tkp.Privileges[0].Luid); + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + AdjustTokenPrivileges(hProcessToken, FALSE, &tkp, 0, NULL, NULL); + DWORD dwLastErr = GetLastError(); + CloseHandle(hProcessToken); + if (ERROR_SUCCESS != dwLastErr) + { + throw gcnew Win32Exception(dwLastErr); + } + } + + //Get window handle representing the desktop shell. This might not work if there is no shell window, or when + //using a custom shell. Also note that we're assuming that the shell is not running elevated. + HWND hShellWnd = GetShellWindow(); + if (hShellWnd == NULL) + { + throw gcnew System::InvalidOperationException("Unable to locate shell window; you might be using a custom shell"); + } + + //Get the ID of the desktop shell process. + DWORD dwShellPID; + GetWindowThreadProcessId(hShellWnd, &dwShellPID); + if (dwShellPID == 0) + { + throw gcnew Win32Exception(GetLastError()); + } + + //Open the desktop shell process in order to get the process token. + HANDLE hShellProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwShellPID); + if (hShellProcess == NULL) + { + throw gcnew Win32Exception(GetLastError()); + } + + HANDLE hShellProcessToken = NULL; + HANDLE hPrimaryToken = NULL; + try + { + //Get the process token of the desktop shell. + if (!OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, &hShellProcessToken)) + { + throw gcnew Win32Exception(GetLastError()); + } + + //Duplicate the shell's process token to get a primary token. + const DWORD dwTokenRights = TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID; + if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, NULL, SecurityImpersonation, TokenPrimary, &hPrimaryToken)) + { + throw gcnew Win32Exception(GetLastError()); + } + + //Start the target process with the new token. + STARTUPINFO si = {0}; si.cb = sizeof(si); + PROCESS_INFORMATION pi = {0}; + if (!CreateProcessWithTokenW(hPrimaryToken, 0, + context.marshal_as(exePath), context.marshal_as(exePath + " " + arguments), + 0, NULL, NULL, &si, &pi)) + { + throw gcnew Win32Exception(GetLastError()); + } + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return Process::GetProcessById(pi.dwProcessId); + } + finally + { + if (hShellProcessToken != NULL) + CloseHandle(hShellProcessToken); + + if (hPrimaryToken != NULL) + CloseHandle(hPrimaryToken); + + if (hShellProcess != NULL) + CloseHandle(hShellProcess); + } + } + + bool UserAccountControl::IsUserAdmin::get() + { + if (UserAccountControl::IsUacEnabled) + return GetProcessTokenElevationType() != TokenElevationTypeDefault; //split token + + //If UAC is off, we can't rely on the token; check for Admin group. + return WindowsPrincipal(WindowsIdentity::GetCurrent()).IsInRole("Administrators"); + } + + bool UserAccountControl::IsUacEnabled::get() + { + //Check the HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA registry value. + RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, false); + return key->GetValue(UacRegistryValue)->Equals(1); + } + + void UserAccountControl::DisableUac() + { + SetUacRegistryValue(false); + } + + void UserAccountControl::DisableUacAndRestartWindows() + { + DisableUac(); + RestartWindows(); + } + + void UserAccountControl::EnableUac() + { + SetUacRegistryValue(true); + } + + void UserAccountControl::EnableUacAndRestartWindows() + { + EnableUac(); + RestartWindows(); + } + + bool UserAccountControl::IsCurrentProcessElevated::get() + { + return GetProcessTokenElevationType() == TokenElevationTypeFull; //elevated + } + + bool UserAccountControl::IsCurrentProcessVirtualized::get() + { + HANDLE hToken; + try + { + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + throw gcnew Win32Exception(GetLastError()); + + DWORD virtualizationEnabled; + DWORD dwSize; + if (!GetTokenInformation(hToken, TokenVirtualizationEnabled, &virtualizationEnabled, sizeof(virtualizationEnabled), &dwSize)) + throw gcnew Win32Exception(GetLastError()); + + return virtualizationEnabled != 0; + } + finally + { + CloseHandle(hToken); + } + } + + int UserAccountControl::GetProcessTokenElevationType() + { + HANDLE hToken; + try + { + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + throw gcnew Win32Exception(GetLastError()); + + TOKEN_ELEVATION_TYPE elevationType; + DWORD dwSize; + if (!GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(elevationType), &dwSize)) + throw gcnew Win32Exception(GetLastError()); + + return elevationType; + } + finally + { + CloseHandle(hToken); + } + } + + void UserAccountControl::SetUacRegistryValue(bool enabled) + { + RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, true); + key->SetValue(UacRegistryValue, enabled ? 1 : 0); + } + + void UserAccountControl::RestartWindows() + { + InitiateSystemShutdownEx(NULL, NULL, 0/*Timeout*/, + TRUE/*ForceAppsClosed*/, TRUE/*RebootAfterShutdown*/, + SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG | SHTDN_REASON_FLAG_PLANNED); + //This shutdown flag corresponds to: "Operating System: Reconfiguration (Planned)". + } +} \ No newline at end of file diff -r 9ffcd8ed8537 -r e9aefd454d1e UacHelpers.CppLibrary/UserAccountControl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UacHelpers.CppLibrary/UserAccountControl.h Mon Feb 02 12:51:06 2015 +0100 @@ -0,0 +1,133 @@ +// UacHelpers.CppLibrary.h + +#pragma once + +using namespace System::Diagnostics; +using namespace System::Security::Principal; + +namespace UacHelpers { + + /// + ///Provides facilities for enabling and disabling User Account Control (UAC), + ///determining elevation and virtualization status, and launching a process + ///under elevated credentials. + /// + /// + ///Note that there's a delicate scenario where the registry key has already been + ///changed, but the user has not logged off yet so the token hasn't been filtered. + ///In that case, we will think that UAC is on but the user is not an admin (because + ///the token is not a split token). + /// + public ref class UserAccountControl abstract sealed + { + public: + /// + ///Returns true if the current user has administrator privileges. + /// + /// + ///If UAC is on, then this property will return true even if the + ///current process is not running elevated. If UAC is off, then this + ///property will return true if the user is part of the built-in + ///Administrators group. + /// + static property bool IsUserAdmin + { + bool get(); + } + + /// + ///Returns true if User Account Control (UAC) is enabled on + ///this machine. + /// + /// + ///This value is obtained by checking the LUA registry key. It is possible + ///that the user has not restarted the machine after enabling/disabling UAC. + ///In that case, the value of the registry key does not reflect the true state + ///of affairs. It is possible to devise a custom solution that would provide + ///a mechanism for tracking whether a restart occurred since UAC settings were + ///changed (using the RunOnce mechanism, temporary files, or volatile registry keys). + /// + static property bool IsUacEnabled + { + bool get(); + } + + /// + ///Returns true if the current process is using UAC virtualization. + /// + /// + ///Under UAC virtualization, file system and registry accesses to specific + ///locations performed by an application are redirected to provide backwards- + ///compatibility. 64-bit applications or applications that have an associated + ///manifest do not enjoy UAC virtualization because they are assumed to be + ///compatible with Vista and UAC. + /// + static property bool IsCurrentProcessVirtualized + { + bool get(); + } + + /// + ///Returns true if the current process is elevated, i.e. if the process + ///went through an elevation consent phase. + /// + /// + ///This property will return false if UAC is disabled and the process + ///is running as admin. It only determines whether the process went through + ///the elevation procedure. + /// + static property bool IsCurrentProcessElevated + { + bool get(); + } + + /// + ///Disables User Account Control by changing the LUA registry key. + ///The changes do not have effect until the system is restarted. + /// + static void DisableUac(); + + /// + ///Disables User Account Control and restarts the system. + /// + static void DisableUacAndRestartWindows(); + + /// + ///Enables User Account Control by changing the LUA registry key. + ///The changes do not have effect until the system is restarted. + /// + static void EnableUac(); + + /// + ///Enables User Account Control and restarts the system. + /// + static void EnableUacAndRestartWindows(); + + /// + ///Creates a process under the elevated token, regardless of UAC settings + ///or the manifest associated with that process. + /// + ///The path to the executable file. + ///The command-line arguments to pass to the process. + ///A object representing the newly created process. + static Process^ CreateProcessAsAdmin(System::String^ exePath, System::String^ arguments); + + /// + ///Creates a process under the standard user if the current process is elevated. The identity + ///of the standard user is determined by retrieving the user token of the currently running Explorer + //(shell) process. If the current process is not elevated, the standard user is used. + /// + ///The path to the executable file. + ///The command-line arguments to pass to the process. + ///A object representing the newly created process. + static Process^ CreateProcessAsStandardUser(System::String^ exePath, System::String^ arguments); + + private: + static int GetProcessTokenElevationType(); + static void SetUacRegistryValue(bool enable); + static void RestartWindows(); + + static System::String^ UacRegistryKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"; + static System::String^ UacRegistryValue = "EnableLUA"; + }; +} // end namespace UacHelpers \ No newline at end of file