Getting Windows OS version programmatically - c#

I am trying to fetch Windows version with C# on my Windows 10 machine.
I always get those values (with C#\C++):
Major: 6
Minor: 2
Which is Windows 8 OS, accordingly to MSDN
C# code:
var major = OperatingSystem.Version.Major
var minor = OperatingSystem.Version.Minor
C++ code
void print_os_info()
{
//http://stackoverflow.com/questions/1963992/check-windows-version
OSVERSIONINFOW info;
ZeroMemory(&info, sizeof(OSVERSIONINFOW));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
LPOSVERSIONINFOW lp_info = &info;
GetVersionEx(lp_info);
printf("Windows version: %u.%u\n", info.dwMajorVersion, info.dwMinorVersion);
}
Windows 10 suppose to be with those:
Major: 10
Minor: 0*
(When I am taking a dump file from running process I can see that the OS version of that file is set to 10.0)
built by: 10.0.10586.0 (th2_release.151029-1700)
What am I missing here?

In my scenario I needed my application to capture computer info for possible bug-reports and statistics.
I did not find the solutions where an application manifest had to be added satisfactory. Most of the suggestions I found while googling this suggested just that, unfortunately.
Thing is, when using a manifest, each OS version has to be added manually to it in order for that particular OS version to be able to report itself at runtime.
In other words, this becomes a race condition: A user of my app may very well be using a version of my app that pre-dates the OS in use. I would have to upgrade the app immediately when a new OS version was launched by Microsoft. I would also have to force the users to upgrade the app at the same time as they updated the OS.
In other words, not very feasible.
After browsing through the options I found some references (surprisingly few compared to the app manifest) that instead suggested using registry lookups.
My (chopped down) ComputerInfo class with only WinMajorVersion, WinMinorVersion and IsServer properties looks like this:
using Microsoft.Win32;
namespace Inspection
{
/// <summary>
/// Static class that adds convenient methods for getting information on the running computers basic hardware and os setup.
/// </summary>
public static class ComputerInfo
{
/// <summary>
/// Returns the Windows major version number for this computer.
/// </summary>
public static uint WinMajorVersion
{
get
{
dynamic major;
// The 'CurrentMajorVersionNumber' string value in the CurrentVersion key is new for Windows 10,
// and will most likely (hopefully) be there for some time before MS decides to change this - again...
if (TryGetRegistryKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", out major))
{
return (uint) major;
}
// When the 'CurrentMajorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion'
dynamic version;
if (!TryGetRegistryKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out version))
return 0;
var versionParts = ((string) version).Split('.');
if (versionParts.Length != 2) return 0;
uint majorAsUInt;
return uint.TryParse(versionParts[0], out majorAsUInt) ? majorAsUInt : 0;
}
}
/// <summary>
/// Returns the Windows minor version number for this computer.
/// </summary>
public static uint WinMinorVersion
{
get
{
dynamic minor;
// The 'CurrentMinorVersionNumber' string value in the CurrentVersion key is new for Windows 10,
// and will most likely (hopefully) be there for some time before MS decides to change this - again...
if (TryGetRegistryKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMinorVersionNumber",
out minor))
{
return (uint) minor;
}
// When the 'CurrentMinorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion'
dynamic version;
if (!TryGetRegistryKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out version))
return 0;
var versionParts = ((string) version).Split('.');
if (versionParts.Length != 2) return 0;
uint minorAsUInt;
return uint.TryParse(versionParts[1], out minorAsUInt) ? minorAsUInt : 0;
}
}
/// <summary>
/// Returns whether or not the current computer is a server or not.
/// </summary>
public static uint IsServer
{
get
{
dynamic installationType;
if (TryGetRegistryKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "InstallationType",
out installationType))
{
return (uint) (installationType.Equals("Client") ? 0 : 1);
}
return 0;
}
}
private static bool TryGetRegistryKey(string path, string key, out dynamic value)
{
value = null;
try
{
using(var rk = Registry.LocalMachine.OpenSubKey(path))
{
if (rk == null) return false;
value = rk.GetValue(key);
return value != null;
}
}
catch
{
return false;
}
}
}
}

You'll need to add an app.manifest to your application:
then uncomment the following line:
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />

Registry.GetValue(#"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentBuildNumber", string.Empty).ToString()
same code for all OSes from XP till current 10.16299,
over scenarios not properly work from windows 8
The OSVersion property reports the same version number (6.2.0.0) for both Windows 8 and Windows 8.1 and the same major and minor version number for Windows 10.
https://msdn.microsoft.com/library/system.environment.osversion.aspx

As the accepted answer is only for C#, here is a solution for C++.
It uses the RtlGetVersion in the ntdll.dll that uses the same structure as GetVersionEx (name is different, but the elements are the same) and gives you the correct version.
As this function is normally used for driver development, the function is declared in the DDK and not in the SDK. So I used a dynamic solution to call the function.
Please be aware that the ntdll.dll is loaded and released in every call. So if you need the function more often, keep the library loaded.
The structure pOSversion is pointing to must be initialized like for GetVersionEx.
BOOL GetTrueWindowsVersion(OSVERSIONINFOEX* pOSversion)
{
// Function pointer to driver function
NTSTATUS (WINAPI *pRtlGetVersion)(
PRTL_OSVERSIONINFOW lpVersionInformation) = NULL;
// load the System-DLL
HINSTANCE hNTdllDll = LoadLibrary("ntdll.dll");
// successfully loaded?
if (hNTdllDll != NULL)
{
// get the function pointer to RtlGetVersion
pRtlGetVersion = (NTSTATUS (WINAPI *)(PRTL_OSVERSIONINFOW))
GetProcAddress (hNTdllDll, "RtlGetVersion");
// if successfull then read the function
if (pRtlGetVersion != NULL)
pRtlGetVersion((PRTL_OSVERSIONINFOW)pOSversion);
// free the library
FreeLibrary(hNTdllDll);
} // if (hNTdllDll != NULL)
// if function failed, use fallback to old version
if (pRtlGetVersion == NULL)
GetVersionEx((OSVERSIONINFO*)pOSversion);
// always true ...
return (TRUE);
} // GetTrueWindowsVersion

You can do this in C# the same way C++ answer has it
[SecurityCritical]
[DllImport("ntdll.dll", SetLastError = true)]
internal static extern bool RtlGetVersion(ref OSVERSIONINFOEX versionInfo);
[StructLayout(LayoutKind.Sequential)]
internal struct OSVERSIONINFOEX
{
// The OSVersionInfoSize field must be set to Marshal.SizeOf(typeof(OSVERSIONINFOEX))
internal int OSVersionInfoSize;
internal int MajorVersion;
internal int MinorVersion;
internal int BuildNumber;
internal int PlatformId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
internal string CSDVersion;
internal ushort ServicePackMajor;
internal ushort ServicePackMinor;
internal short SuiteMask;
internal byte ProductType;
internal byte Reserved;
}
...
var osVersionInfo = new OSVERSIONINFOEX { OSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)) };
if (!RtlGetVersion(ref osVersionInfo))
{
// TODO: Error handling, call GetVersionEx, etc.
}

You can read from registry through code and do specific action what you intended.
Say for example:
Registry key is located at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion and then look for "ProductName".
You can open registry information by giving regedit.exe in run (Windows+r)
var reg =Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\WindowsNT\CurrentVersion");
string productName = (string)reg.GetValue("ProductName");
if (productName.StartsWith("Windows 10"))
{
}
else
{
}

Related

AccessibleObjectFromPoint is returning client object instead of check box

I am using Visual Studio 2017. I added two projects in a solution. One project is in C# with WPF. The other is in VC++ with ATL.
From C#, I call a function in the VC++ project, which sets low level mouse hook. A part of the code in the low level mouse proc is as follows:
MSLLHOOKSTRUCT stMouse = *(MSLLHOOKSTRUCT*)(lParam);
POINT pt = stMouse.pt;
IAccessible* pAcc;
VARIANT varChild;
HRESULT hr = AccessibleObjectFromPoint(pt, &pAcc, &varChild);
VARIANT varRole;
hr = pAcc->get_accRole(varChild, &varRole);
When I test by clicking on a check box under View tab in MS Word 2013, then I get the role as client for WM_LBUTTONDOWN and WM_LBUTTONUP messages. But I should get the role as check box.
I checked with Inspect.exe that comes with Windows 10 SDK. Inspect.exe shows role correctly as check box. Inspect.exe has two options - one to see UI Automation properties and the other to see MSAA properties. I am seeing the MSAA properties.
How can I get the correct role? What method deos Inspect.exe uses?
Microsoft Active Accessibility / MSAA (based on the IAccessible interface) is a legacy API. You should now use Windows Automation. UIA is based on COM, and uses interfaces, the most important one being IUIAutomationElement. Inspect.exe uses UIA or MSAA.
Note .NET is compatible with UIA and WPF can expose its UI Elements to UIA pretty easily, through the AutomationPeer class and UIElement.OnCreateAutomationPeer Method method. WPF provides a default implementation that can be tweaked if needed.
Here is a similar example to yours in C++, using the UIA API, instead of MSAA (we could write the same using C#):
#include "stdafx.h" // needs <uiautomation.h>
class CCoInitialize { // https://blogs.msdn.microsoft.com/oldnewthing/20040520-00/?p=39243
public:
CCoInitialize() : m_hr(CoInitialize(NULL)) { }
~CCoInitialize() { if (SUCCEEDED(m_hr)) CoUninitialize(); }
operator HRESULT() const { return m_hr; }
HRESULT m_hr;
};
// this is a poor-man COM object class
class CHandler : public IUIAutomationEventHandler
{
public:
HRESULT QueryInterface(REFIID riid, LPVOID * ppv)
{
if (!ppv) return E_INVALIDARG;
*ppv = NULL;
if (riid == IID_IUnknown || riid == IID_IUIAutomationEventHandler)
{
*ppv = this;
return NOERROR;
}
return E_NOINTERFACE;
}
ULONG AddRef() { return 1; }
ULONG Release() { return 1; }
// this will be called by UIA
HRESULT HandleAutomationEvent(IUIAutomationElement *sender, EVENTID eventId)
{
wprintf(L"Event id: %u\n", eventId);
return S_OK;
}
};
int main()
{
CCoInitialize init;
// this sample uses Visual Studio's ATL smart pointers, but it's not mandatory at all
CComPtr<IUIAutomation> uia;
if (SUCCEEDED(uia.CoCreateInstance(CLSID_CUIAutomation)))
{
// get mouse pos now
POINT pt;
GetCursorPos(&pt);
// find what type of "control" was under the mouse
CComPtr<IUIAutomationElement> element;
if (SUCCEEDED(uia->ElementFromPoint(pt, &element)))
{
CComBSTR type;
element->get_CurrentLocalizedControlType(&type);
wprintf(L"type at %u,%u: %s\n", pt.x, pt.y, type.m_str);
}
// get root
CComPtr<IUIAutomationElement> root;
if (SUCCEEDED(uia->GetRootElement(&root)))
{
// add a handler that will trigger every time you open any window on the desktop
// in the real world, you'll need to delete this pointer to avoid memory leaks of course
CHandler *pHandler = new CHandler();
if (SUCCEEDED(uia->AddAutomationEventHandler(UIA_Window_WindowOpenedEventId, root, TreeScope::TreeScope_Children, NULL, pHandler)))
{
// since this is a console app, we need to run the Windows message loop otherwise, you'll never get any UIA events
// for this sample, just press CTRL-C to stop the program. in the real world, you'll need to get out properly
// if you have a standard windows app, the loop will be already there
while (TRUE)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
}
return 0;
}

SHIFT Shortcut being displayed as MAJ for culture "nl-BE" when it should be SHIFT (C#.NET)

After a long discussion with Infragistics it appears that ShortCuts with SHIFT are displayed as MAJ in my culture "nl-BE". First of all, the culture "nl-BE" and AZERTY is somewhat of a strange thing. Read http://en.wikipedia.org/wiki/AZERTY if want to know more. The important quote is:
The other keys are identical, even though traditionally the names of
special keys are printed on them in English. This is because Belgium
is predominantly bilingual (French-Dutch) and officially trilingual (a
third language, German, is spoken in the East Cantons).
So MAJ is printed as SHIFT. In Office for instance, Shortcuts with SHIFT are displayed as SHIFT. In the Infragistics controls however they are displayed as MAJ. And this frustrates our customers.
So, after a discussion with Infragistics they claim that it's a Windows Api call that is returning the MAJ instead of SHIFT. I have gotten a sample project from them which shows the behavior. So now my question is why the Windows Api call doesn't return SHIFT, and if it's normal, then how does Office do it to display it correct?
The code to get the text of the key is :
NativeWindowMethods.GetKeyNameText((int)scanCode, sb, 256);
and
class NativeWindowMethods
{
#region MapVirtualKey
[DllImport("user32.dll")]
internal static extern int MapVirtualKey(uint uCode, uint uMapType);
#endregion //MapVirtualKey
#region GetKeyNameText
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern int GetKeyNameText(
int lParam,
[MarshalAs(UnmanagedType.LPWStr), Out]System.Text.StringBuilder str,
int size);
#endregion //GetKeyNameText
}
In case of the Shiftkey, the scancode is 2752512 (2a) and MAJ is returned.
So, what are my questions?
Is it normal that MAJ is returned for the culture "nl-BE"? Or is it a bug in user32.dll?
If Office gets it right, isn't it up to Infragistics to also get it right?
Does Infragistics use the correct user32.dll api call?
For completeness I'll paste the full code for the Utilities class. From the Form next call is done:
systemLocalizedString = Utilities.GetLocalizedShortcutString(shortcut);
With shortcut = ShiftF12. After the call, systemLocalizedString is equal to "MAJ+F12".
UPDATE: With the help of Hans Passant I downloaded the Microsoft Keyboard Layout Creator and exported my current Keyboard Layout. In the .klc file there's no MAJ to be found, only Shift (2a Shift for instance). So why does the user32.dll return MAJ? Even weirder is that when I make a copy of the .klc file and install it as a new keyboard, then suddenly the user32.dll does return Shift for that newly installed keyboard (while it's an exact copy).
Utilities.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
class Utilities
{
#region GetLocalizedShortcutString
/// <summary>
/// Returns the localized string for the specified <b>Shortcut</b>
/// </summary>
/// <param name="shortcut">Shortcut to localize</param>
/// <param name="separator">Character used to separate multiple keys in the shortcut</param>
/// <returns>A string containing the localized description of the shortcut based on the currently mapped keyboard layout</returns>
public static string GetLocalizedShortcutString(Shortcut shortcut, char separator = '+')
{
if (shortcut == Shortcut.None)
return string.Empty;
return GetLocalizedKeyString((Keys)shortcut, separator);
}
#endregion //GetLocalizedShortcutString
#region GetLocalizedKeyString
/// <summary>
/// Returns the localized string for the specified <b>Keys</b>
/// </summary>
/// <param name="keys">Keys to localize</param>
/// <param name="separator">Character used to separate multiple keys</param>
/// <returns>A string containing the localized description of the keys based on the currently mapped keyboard layout</returns>
public static string GetLocalizedKeyString(Keys keys, char separator)
{
bool alt = ((long)keys & (long)Keys.Alt) != 0;
bool ctrl = ((long)keys & (long)Keys.Control) != 0;
bool shift = ((long)keys & (long)Keys.Shift) != 0;
// get the key involved
long value = (long)keys & 0xffff;
Keys key = (Keys)Enum.ToObject(typeof(Keys), value);
System.Text.StringBuilder sb = new System.Text.StringBuilder();
if (alt && key != Keys.Menu)
{
sb.Append(GetLocalizedKeyStringHelper(Keys.Menu));
sb.Append(separator);
}
if (ctrl && key != Keys.ControlKey)
{
sb.Append(GetLocalizedKeyStringHelper(Keys.ControlKey));
sb.Append(separator);
}
if (shift && key != Keys.ShiftKey)
{
sb.Append(GetLocalizedKeyStringHelper(Keys.ShiftKey));
sb.Append(separator);
}
sb.Append(GetLocalizedKeyStringHelper(key));
return sb.ToString();
}
#endregion //GetLocalizedKeyString
#region GetLocalizedKeyStringHelper
private static string GetLocalizedKeyStringHelper(Keys key)
{
string localizedKey = GetLocalizedKeyStringUnsafe(key);
if (localizedKey == null || localizedKey.Length == 0)
return key.ToString();
return localizedKey;
}
#endregion //GetLocalizedKeyStringHelper
#region GetLocalizedKeyStringUnsafe
private static string GetLocalizedKeyStringUnsafe(Keys key)
{
// strip any modifier keys
long keyCode = ((int)key) & 0xffff;
System.Text.StringBuilder sb = new System.Text.StringBuilder(256);
long scanCode = NativeWindowMethods.MapVirtualKey((uint)keyCode, (uint)0);
// shift the scancode to the high word
scanCode = (scanCode << 16);
if (keyCode == 45 ||
keyCode == 46 ||
keyCode == 144 ||
(33 <= keyCode && keyCode <= 40))
{
// add the extended key flag
scanCode |= 0x1000000;
}
NativeWindowMethods.GetKeyNameText((int)scanCode, sb, 256);
return sb.ToString();
}
#endregion //GetLocalizedKeyStringUnsafe
}
class NativeWindowMethods
{
#region MapVirtualKey
[DllImport("user32.dll")]
internal static extern int MapVirtualKey(uint uCode, uint uMapType);
#endregion //MapVirtualKey
#region GetKeyNameText
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern int GetKeyNameText(int lParam, [MarshalAs(UnmanagedType.LPWStr), Out]System.Text.StringBuilder str, int size);
#endregion //GetKeyNameText
}
}
but even I am using AZERTY, that's what is used in schools to learn how to type so...
So yes, that's the problem. The only way you can get an AZERTY layout is to select keyboard layout titled "français (Belgique)" in Control Panel + Language + Add a language. As opposed to the "Nederlands (België)" layout, it has a QWERTY arrangement. The GetKeyNameText() winapi function returns strings that are encoded in the keyboard layout file. Which are of course in French for a keyboard layout named français so MAJ is the expected result. Windows does not have a keyboard layout available that speaks Dutch with an AZERTY arrangement.
All is not lost, Windows users often ask for custom keyboard layouts. So Microsoft made a tool available to create your own, the Microsoft Keyboard Layout Creator. It was primarily designed to re-arrange keys, a little extra elbow grease is required to make it do what you want. The tool doesn't let you directly edit the key descriptions and defaults to English names. You'll want to start with File + Load Existing Keyboard. Then File + Save Layout to save the layout to a .klc file. Open it in a text editor, Notepad is fine, locate the sections named KEYNAME and KEYNAME_EXT and edit the key names the way you want them.
Restart the utility (don't skip) and reload the .klc file. And build the setup package with Project + Build DLL.
Assuming that you're using Windows Forms have you tried using the Windows.Forms.KeysConverter class for your conversion? Looking at the source code for the normal winforms MenuItem control, the string for shortcut keys is obtained by calling the KeysConverter.ConvertToString(Object value) method. The KeysConverter in-turn gets the string from System.Windows.Forms.resources ("toStringShift" resource key for Shift). Those resource strings are all localized depending on which .Net language packs are installed on the computer and the culture used to run the app. So from what I can see Windows Forms doesn't actually use user32.dll.
Try the following code to test and try both nl-BE and fr-BE (you'll of course need both .Net language packs installed):
static void Main()
{
var culture = new CultureInfo("nl-BE");
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
var keys = Keys.Shift | Keys.N;
var keysstring = new KeysConverter().ConvertToString(keys);
}
If this does work correctly then I'd say that Infragistics is using the wrong API. I'd expect them to use the normal .Net KeysConverter rather than the call to user32.dll.
Update: I couldn't install the language packs on my computer but I extracted the files from the installers and can confirm that the System.Windows.Forms.resources.dll assemblies for each language do contain the correct key names which you're looking for.
System.Windows.Forms.fr.resx:
<data name="toStringShift" xml:space="preserve">
<value>Maj</value>
</data>
System.Windows.Forms.nl.resx:
<data name="toStringShift" xml:space="preserve">
<value>Shift</value>
</data>
If you are looking for solution of a problem rather then answer to question who should be blamed? Microsoft Kernel developers, Microsoft Office developers, Infragistics developers.. and you are using WPF then this
KeyGesture gesture;
MenuItem menuItem;
...
menuItem.InputGestureText = gesture.GetDisplayStringForCulture(System.Threading.Thread.CurrentThread.CurrentUICulture);
Works for me to display shortcuts the way I need them. I did not test it with esoteric or Van-Damme cultures but this code should be language neutral.
How exactly does the code work can be analyzed using Visual Studio debugger, source code of the KeyGesture.GetDisplayStringForCulture is also browsable online → http://referencesource.microsoft.com/#PresentationCore/src/Core/CSharp/System/Windows/Input/Command/KeyGesture.cs#fbe5780461e3961d
EDIT BTW: the WPF code seems to end in ModifierKeyConverters
internal static string MatchModifiers(ModifierKeys modifierKeys)
{
string modifiers = String.Empty;
switch (modifierKeys)
{
case ModifierKeys.Control: modifiers="Ctrl";break;
case ModifierKeys.Shift : modifiers="Shift";break;
case ModifierKeys.Alt : modifiers="Alt";break;
case ModifierKeys.Windows: modifiers="Windows";break;
}
return modifiers;
}

RegLoadAppKey working fine on 32-bit OS, failing on 64-bit OS, even if both processes are 32-bit

I'm using .NET 4 and the new RegistryKey.FromHandle call so I can take the hKey I get from opening a software registry file with RegLoadAppKey and operate on it with the existing managed API.
I thought at first it was just a matter of a busted DllImport and my call had an invalid type in the params or a missing MarshalAs or whatever, but looking at other registry functions and their DllImport declarations (for instance, on pinvoke.net), I don't see what else to try (I've had hKey returned as both int and IntPtr, both worked on 32-bit OS and fail on 64-bit OS)
I've got it down to as simple a repro case as I can - it just tries to create a 'random' subkey then write a value to it. It works fine on my Win7 x86 box and fails on Win7 x64 and 2008 R2 x64, even when it's still a 32-bit process, even run from a 32-bit cmd prompt. EDIT: It also fails in the same way if it's a 64-bit process.
EDIT: it works fine if the file passed in is empty - the problem case is for the existing software registry hive. I extracted 'bare' software registry hive files from 2008 r2 (x64) and WHS v1 (x86) iso's and both have the same problem.
on Win7 x86:
INFO: Running as Admin in 32-bit process on 32-bit OS
Was able to create Microsoft\Windows\CurrentVersion\RunOnceEx\a95b1bbf-7a04-4707-bcca-6aee6afbfab7 and write a value under it
on Win7 x64, as 32-bit:
INFO: Running as Admin in 32-bit process on 64-bit OS
Unhandled Exception: System.UnauthorizedAccessException: Access to the registry key '\Microsoft\Windows\CurrentVersion\RunOnceEx\ce6d5ff6-c3af-47f7-b3dc-c5a1b9a3cd22' is denied.
at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
at Microsoft.Win32.RegistryKey.CreateSubKeyInternal(String subkey, RegistryKeyPermissionCheck permissionCheck, Object registrySecurityObj, RegistryOptions registryOptions)
at Microsoft.Win32.RegistryKey.CreateSubKey(String subkey)
at LoadAppKeyAndModify.Program.Main(String[] args)
on Win7 x64, as 64-bit:
INFO: Running as Admin in 64-bit process on 64-bit OS
Unhandled Exception: System.UnauthorizedAccessException: Access to the registry key '\Microsoft\Windows\CurrentVersion\RunOnceEx\43bc857d-7d07-499c-8070-574d6732c130' is denied.
at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
at Microsoft.Win32.RegistryKey.CreateSubKeyInternal(String subkey, RegistryKeyPermissionCheck permissionCheck, Object registrySecurityObj, RegistryOptions registryOptions)
at Microsoft.Win32.RegistryKey.CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck)
at LoadAppKeyAndModify.Program.Main(String[] args)
source:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("INFO: Running as {0} in {1}-bit process on {2}-bit OS",
new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator) ? "Admin" : "Normal User",
Environment.Is64BitProcess ? 64 : 32,
Environment.Is64BitOperatingSystem ? 64 : 32);
if (args.Length != 1)
{
throw new ApplicationException("Need 1 argument - path to the software hive file on disk");
}
string softwareHiveFile = Path.GetFullPath(args[0]);
if (File.Exists(softwareHiveFile) == false)
{
throw new ApplicationException("Specified file does not exist: " + softwareHiveFile);
}
// pick a random subkey so it doesn't already exist
var existingKeyPath = #"Microsoft\Windows\CurrentVersion";
var keyPathToCreate = #"RunOnceEx\" + Guid.NewGuid();
var completeKeyPath = Path.Combine(existingKeyPath, keyPathToCreate);
var hKey = RegistryNativeMethods.RegLoadAppKey(softwareHiveFile);
using (var safeRegistryHandle = new SafeRegistryHandle(new IntPtr(hKey), true))
using (var appKey = RegistryKey.FromHandle(safeRegistryHandle))
using (var currentVersionKey = appKey.OpenSubKey(existingKeyPath, true))
{
if (currentVersionKey == null)
{
throw new ApplicationException("Specified file is not a well-formed software registry hive: " + softwareHiveFile);
}
using (var randomSubKey = currentVersionKey.CreateSubKey(keyPathToCreate))
{
randomSubKey.SetValue("foo", "bar");
Console.WriteLine("Was able to create {0} and write a value under it", completeKeyPath);
}
}
}
}
internal static class RegistryNativeMethods
{
[Flags]
public enum RegSAM
{
AllAccess = 0x000f003f
}
private const int REG_PROCESS_APPKEY = 0x00000001;
// approximated from pinvoke.net's RegLoadKey and RegOpenKey
// NOTE: changed return from long to int so we could do Win32Exception on it
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegLoadAppKey(String hiveFile, out int hKey, RegSAM samDesired, int options, int reserved);
public static int RegLoadAppKey(String hiveFile)
{
int hKey;
int rc = RegLoadAppKey(hiveFile, out hKey, RegSAM.AllAccess, REG_PROCESS_APPKEY, 0);
if (rc != 0)
{
throw new Win32Exception(rc, "Failed during RegLoadAppKey of file " + hiveFile);
}
return hKey;
}
}
Ended up with opening a support case with Microsoft Support - the problem is specific to 1) the hives that ship on the install media for recent versions of Windows and 2) RegLoadAppKey as an API. Switching over to RegLoadKey/RegUnLoadKey instead worked fine for the exact same files (in the same process, even), and since the bug in RegLoadAppKey is unlikely to get fixed (let alone soon) to deal with those specific files, I just switched to RegLoadKey/RegUnLoadKey instead.
Following link should be helpful in this scenario:
http://msdn.microsoft.com/en-us/library/ms973190.aspx#64mig_topic5

How can a C# Windows Console application tell if it is run interactively

How can a Windows console application written in C# determine whether it is invoked in a non-interactive environment (e.g. from a service or as a scheduled task) or from an environment capable of user-interaction (e.g. Command Prompt or PowerShell)?
[EDIT: 4/2021 - new answer...]
Due to a recent change in the Visual Studio debugger, my original answer stopped working correctly when debugging. To remedy this, I'm providing an entirely different approach. The text of the original answer is included at the bottom.
1. Just the code, please...
To determine if a .NET application is running in GUI mode:
[DllImport("kernel32.dll")] static extern IntPtr GetModuleHandleW(IntPtr _);
public static bool IsGui
{
get
{
var p = GetModuleHandleW(default);
return Marshal.ReadInt16(p, Marshal.ReadInt32(p, 0x3C) + 0x5C) == 2;
}
}
This checks the Subsystem value in the PE header. For a console application, the value will be 3 instead of 2.
2. Discussion
As noted in a related question, the most reliable indicator of GUI vs. console is the "Subsystem" field in the PE header of the executable image. The following C# enum lists the allowable (documented) values:
public enum Subsystem : ushort
{
Unknown /**/ = 0x0000,
Native /**/ = 0x0001,
WindowsGui /**/ = 0x0002,
WindowsCui /**/ = 0x0003,
OS2Cui /**/ = 0x0005,
PosixCui /**/ = 0x0007,
NativeWindows /**/ = 0x0008,
WindowsCEGui /**/ = 0x0009,
EfiApplication /**/ = 0x000A,
EfiBootServiceDriver /**/ = 0x000B,
EfiRuntimeDriver /**/ = 0x000C,
EfiRom /**/ = 0x000D,
Xbox /**/ = 0x000E,
WindowsBootApplication /**/ = 0x0010,
};
As easy as that code (in that other answer) is, our case here can be vastly simplified. Since we are only specifically interested in our own running process (which is necessarily loaded), you don't have to open any file or read from the disk to obtain the subsystem value. Our executable image is guaranteed to be already mapped into memory. And it is simple to retrieve the base address for any loaded file image by calling the GetModuleHandleW function:
[DllImport("kernel32.dll")]
static extern IntPtr GetModuleHandleW(IntPtr lpModuleName);
Although we might provide a filename to this function, again things are easier and we don't have to. Passing null, or in this case, default(IntPtr.Zero) (which is the same as IntPtr.Zero), returns the base address of the virtual memory image for the current process. This eliminates the extra steps (alluded to earlier) of having to fetch the entry assembly and its Location property, etc. Without further ado, here is the new and simplified code:
static Subsystem GetSubsystem()
{
var p = GetModuleHandleW(default); // VM base address of mapped PE image
p += Marshal.ReadInt32(p, 0x3C); // RVA of COFF/PE within DOS header
return (Subsystem)Marshal.ReadInt16(p + 0x5C); // PE offset to 'Subsystem' word
}
public static bool IsGui => GetSubsystem() == Subsystem.WindowsGui;
public static bool IsConsole => GetSubsystem() == Subsystem.WindowsCui;
[official end of the new answer]
3. Bonus Discussion
For the purposes of .NET, Subsystem is perhaps the most—or only—useful piece of information in the PE Header. But depending on your tolerance for minutiae, there could be other invaluable tidbits, and it's easy to use the technique just described to retrieve additional interesting data.
Obviously, by changing the final field offset (0x5C) used earlier, you can access other fields in the COFF or PE header. The next snippet illustrates this for Subsystem (as above) plus three additional fields with their respective offsets.
NOTE: To reduce clutter, the enum declarations used in the following can be found here
var p = GetModuleHandleW(default); // PE image VM mapped base address
p += Marshal.ReadInt32(p, 0x3C); // RVA of COFF/PE within DOS header
var subsys = (Subsystem)Marshal.ReadInt16(p + 0x005C); // (same as before)
var machine = (ImageFileMachine)Marshal.ReadInt16(p + 0x0004); // new
var imgType = (ImageFileCharacteristics)Marshal.ReadInt16(p + 0x0016); // new
var dllFlags = (DllCharacteristics)Marshal.ReadInt16(p + 0x005E); // new
// ... etc.
To improve things when accessing multiple fields in unmanaged memory, it's essential to define an overlaying struct. This allows for direct and natural managed access using C#. For the running example, I merged the adjacent COFF and PE headers together into the following C# struct definition, and only included the four fields we deemed interesting:
[StructLayout(LayoutKind.Explicit)]
struct COFF_PE
{
[FieldOffset(0x04)] public ImageFileMachine MachineType;
[FieldOffset(0x16)] public ImageFileCharacteristics Characteristics;
[FieldOffset(0x5C)] public Subsystem Subsystem;
[FieldOffset(0x5E)] public DllCharacteristics DllCharacteristics;
};
NOTE: A fuller version of this struct, without the omitted fields, can be found here
Any interop struct such as this has to be properly setup at runtime, and there are many options for doing so. Ideally, its generally better to impose the struct overlay "in-situ" directly on the unmanaged memory, so that no memory copying needs to occur. To avoid prolonging the discussion here even further however, I will instead show an easier method that does involve copying.
var p = GetModuleHandleW(default);
var _pe = Marshal.PtrToStructure<COFF_PE>(p + Marshal.ReadInt32(p, 0x3C));
Trace.WriteLine($#"
MachineType: {_pe.MachineType}
Characteristics: {_pe.Characteristics}
Subsystem: {_pe.Subsystem}
DllCharacteristics: {_pe.DllCharacteristics}");
4. Output of the demo code
Here is the output when a console program is running...
MachineType: Amd64
Characteristics: ExecutableImage, LargeAddressAware
Subsystem: WindowsCui (3)
DllCharacteristics: HighEntropyVA, DynamicBase, NxCompatible, NoSeh, TSAware
...compared to GUI (WPF) application:
MachineType: Amd64
Characteristics: ExecutableImage, LargeAddressAware
Subsystem: WindowsGui (2)
DllCharacteristics: HighEntropyVA, DynamicBase, NxCompatible, NoSeh, TSAware
[OLD: original answer from 2012...]
To determine if a .NET application is running in GUI mode:
bool is_console_app = Console.OpenStandardInput(1) != Stream.Null;
Environment.UserInteractive Property
If all you're trying to do is to determine whether the console will continue to exist after your program exits (so that you can, for example, prompt the user to hit Enter before the program exits), then all you have to do is to check if your process is the only one attached to the console. If it is, then the console will be destroyed when your process exits. If there are other processes attached to the console, then the console will continue to exist (because your program won't be the last one).
For example*:
using System;
using System.Runtime.InteropServices;
namespace CheckIfConsoleWillBeDestroyedAtTheEnd
{
internal class Program
{
private static void Main(string[] args)
{
// ...
if (ConsoleWillBeDestroyedAtTheEnd())
{
Console.WriteLine("Press any key to continue . . .");
Console.ReadKey();
}
}
private static bool ConsoleWillBeDestroyedAtTheEnd()
{
var processList = new uint[1];
var processCount = GetConsoleProcessList(processList, 1);
return processCount == 1;
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern uint GetConsoleProcessList(uint[] processList, uint processCount);
}
}
(*) Adapted from code found here.
I haven't tested it, but Environment.UserInteractive looks promising.
A possible improvement of Glenn Slayden's solution:
bool isConsoleApplication = Console.In != StreamReader.Null;
To prompt for user input in an interactive console, but do nothing when run without a console or when input has been redirected:
if (Environment.UserInteractive && !Console.IsInputRedirected)
{
Console.ReadKey();
}

How to get the "friendly" OS Version Name?

I am looking for an elegant way to get the OS version like: "Windows XP Professional Service Pack 1" or "Windows Server 2008 Standard Edition" etc.
Is there an elegant way of doing that?
I am also interested in the processor architecture (like x86 or x64).
You can use WMI to get the product name ("Microsoft® Windows Server® 2008 Enterprise "):
using System.Management;
var name = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().Cast<ManagementObject>()
select x.GetPropertyValue("Caption")).FirstOrDefault();
return name != null ? name.ToString() : "Unknown";
You should really try to avoid WMI for local use. It is very convenient but you pay dearly for it in terms of performance. This is quick and simple:
public string HKLM_GetString(string path, string key)
{
try
{
RegistryKey rk = Registry.LocalMachine.OpenSubKey(path);
if (rk == null) return "";
return (string)rk.GetValue(key);
}
catch { return ""; }
}
public string FriendlyName()
{
string ProductName = HKLM_GetString(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ProductName");
string CSDVersion = HKLM_GetString(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CSDVersion");
if (ProductName != "")
{
return (ProductName.StartsWith("Microsoft") ? "" : "Microsoft ") + ProductName +
(CSDVersion != "" ? " " + CSDVersion : "");
}
return "";
}
Why not use Environment.OSVersion? It will also tell you what operating this is - Windows, Mac OS X, Unix, etc. To find out if you are running in 64bit or 32bit, use IntPtr.Size - this will return 4 bytes for 32bit and 8 bytes for 64bit.
Try:
new ComputerInfo().OSVersion;
Output:
Microsoft Windows 10 Enterprise
Note:
Add reference to Microsoft.VisualBasic.Devices;
For me below line works which gives me output like:
Microsoft Windows 10.0.18362
System.Runtime.InteropServices.RuntimeInformation.OSDescription
It can be used to get information like architecture as well
https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.runtimeinformation?view=netframework-4.8
Properties
FrameworkDescription: Returns a string that indicates the name of the .NET installation on which an app is running.
OSArchitecture: Gets the platform architecture on which the current app is running.
OSDescription: Gets a string that describes the operating system on which the app is running.
ProcessArchitecture: Gets the process architecture of the currently running app.
Little late, but this is how I did it. Might help someone in the future.
using Microsoft.Win32;
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion");
string pathName = (string)registryKey.GetValue("productName");
Sample output:
Name = Windows Vista
Edition = Home Premium
Service Pack = Service Pack 1
Version = 6.0.6001.65536
Bits = 64
Sample class:
class Program
{
static void Main( string[] args )
{
Console.WriteLine( "Operation System Information" );
Console.WriteLine( "----------------------------" );
Console.WriteLine( "Name = {0}", OSInfo.Name );
Console.WriteLine( "Edition = {0}", OSInfo.Edition );
Console.WriteLine( "Service Pack = {0}", OSInfo.ServicePack );
Console.WriteLine( "Version = {0}", OSInfo.VersionString );
Console.WriteLine( "Bits = {0}", OSInfo.Bits );
Console.ReadLine();
}
}
Source code for OSInfo class: http://www.csharp411.com/determine-windows-version-and-edition-with-c/ However there is an error in the code, you will need to replace the "case 6" statement (it's just before #endregion NAME) with this:
case 6:
switch (minorVersion)
{
case 0:
switch (productType)
{
case 1:
name = "Windows Vista";
break;
case 3:
name = "Windows Server 2008";
break;
}
break;
case 1:
switch (productType)
{
case 1:
name = "Windows 7";
break;
case 3:
name = "Windows Server 2008 R2";
break;
}
break;
}
break;
And if you want to go a step further and see if your program is running in 64 or 32 bit:
public static class Wow
{
public static bool Is64BitProcess
{
get { return IntPtr.Size == 8; }
}
public static bool Is64BitOperatingSystem
{
get
{
// Clearly if this is a 64-bit process we must be on a 64-bit OS.
if (Is64BitProcess)
return true;
// Ok, so we are a 32-bit process, but is the OS 64-bit?
// If we are running under Wow64 than the OS is 64-bit.
bool isWow64;
return ModuleContainsFunction("kernel32.dll", "IsWow64Process") && IsWow64Process(GetCurrentProcess(), out isWow64) && isWow64;
}
}
static bool ModuleContainsFunction(string moduleName, string methodName)
{
IntPtr hModule = GetModuleHandle(moduleName);
if (hModule != IntPtr.Zero)
return GetProcAddress(hModule, methodName) != IntPtr.Zero;
return false;
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
extern static bool IsWow64Process(IntPtr hProcess, [MarshalAs(UnmanagedType.Bool)] out bool isWow64);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
extern static IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
extern static IntPtr GetModuleHandle(string moduleName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
extern static IntPtr GetProcAddress(IntPtr hModule, string methodName);
}
One thing to be careful of is this information is usually localized and will report differently depending on the language of the OS.
You can get a lot of info from WMI look for the Win32_OperatingSystem class
Note that the processor architecture question is complex:
do you mean (higher numers require lower numbers to be true):
The CPU is capable for handling 64bit (in the sense that it supports AMD/intel x64 or Itanium)
The Operating system is 64bit
GPR and pointers are 64bits, i.e. XP 64, Vista 64, a 64 bit server release or a 64bit OS for mono
The currently executing process is a 64 bit process (not executing under Wow64 for example)
if you are happy that all 3 must be true then
IntPtr.Size == 8
Indicates that all three are true
You can use Visual Basic Devices to get version information.
Code:
using Microsoft.VisualBasic.Devices;
var versionID = new ComputerInfo().OSVersion;//6.1.7601.65536
var versionName = new ComputerInfo().OSFullName;//Microsoft Windows 7 Ultimate
var verionPlatform = new ComputerInfo().OSPlatform;//WinNT
Console.WriteLine(versionID);
Console.WriteLine(versionName);
Console.WriteLine(verionPlatform);
Output:
6.1.7601.65536
Microsoft Windows 10 Enterprise
WinNT
Note:
You will need to add a reference to Microsoft.VisualBasic;
I know it is no direct answer to the question and it's also a little bit late, but for those who are only looking for a way to determine whether the OS is a Client OS or a server there is a way to use the following: (you need to include the System.Management reference)
using System;
using System.Management;
ManagementClass osClass = new ManagementClass("Win32_OperatingSystem");
foreach (ManagementObject queryObj in osClass.GetInstances())
{
foreach (PropertyData prop in queryObj.Properties)
{
if (prop.Name == "ProductType")
{
ProdType = int.Parse(prop.Value.ToString());
}
}
}
while the variable ProdType is an integer that was initialized before. It will contain a value between 1 and 3 while 1 stands for Workstation, 2 for Domain Controller and 3 for a server.
This was taken from Accessing the properties of Win32_OperatingSystem and changed a little bit...
Disclosure: After posting this, I realized that I am depending on a Nuget extension method library called Z.ExntensionMethods which contains IndexOf()
using Microsoft.VisualBasic.Devices;
string SimpleOSName()
{
var name = new ComputerInfo().OSFullName;
var parts = name.Split(' ').ToArray();
var take = name.Contains("Server")?3:2;
return string.Join(" ", parts.Skip(parts.IndexOf("Windows")).Take(take));
}
Faster performance using System.Management;
string SimpleOSName()
{
var name = new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem")
.Get().Cast<ManagementObject>()
.Select(x => x.GetPropertyValue("Caption").ToString())
.First();
var parts = name.Split(' ').ToArray();
var take = name.Contains("Server")?3:2;
return string.Join(" ", parts.Skip(parts.IndexOf("Windows")).Take(take));
}
output example:
Windows 7
Windows Server 2008

Categories

Resources