I'm working with an on-screen keyboard that needs to send key strokes to a third party application. They are running on Windows XP. A small set of characters that are not available on the US English keyboard need to be supported (such as "å" or ñ). After reviewing SendInput it seemed like the safest thing would be to send the hex unicode value of the character as a key stroke sequence. I wrote code that sends an "Alt" and "Add" key down event, followed by key down and up events for the four character unicode sequence with the Alt key ORed, then finally "Add" and "Alt" key up events. In my C# test app. I am using KeyPreview and sure enough, all of the events are coming through however all I get is a beep, no character. I have captured the same information from entering the key strokes manually, the KeyPreview information is identical, and the character appears.
Is it possible to use SendInput in this way? I haven't used a hook to examine the data but I've seen posts that indicate SendInput events have some sort of "injected" flag attached, maybe this causes the sequence to fail?
This demo code successfully sends key events, but a sequence of key events intended to generate a unicode character fails.
private const uint KEYEVENTF_KEYDOWN = 0x0000;
private const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
private const uint KEYEVENTF_KEYUP = 0x0002;
private const int INPUT_KEYBOARD = 1;
[DllImport ("user32.dll", SetLastError = false)]
static extern IntPtr GetMessageExtraInfo ();
[DllImport ("user32.dll", SetLastError = true)]
static extern uint SendInput (uint nInputs, [MarshalAs (UnmanagedType.LPArray, SizeConst = 1)] INPUT[] pInputs, int cbSize);
[StructLayout (LayoutKind.Sequential, Size = 24)]
private struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout (LayoutKind.Explicit)]
private struct INPUT
{
[FieldOffset (0)]
public int type;
[FieldOffset (4)]
public KEYBDINPUT ki;
}
private void PressKey (Keys k)
{
PressKeyDown (k);
PressKeyUp (k);
}
private void PressKeyDown (Keys k)
{
INPUT input = new INPUT ();
input.type = INPUT_KEYBOARD;
input.ki.wVk = (byte)k;
input.ki.wScan = 0;
input.ki.time = 0;
uint flags = KEYEVENTF_KEYDOWN;
if ((33 <= (byte)k && (byte)k <= 46) || (91 <= (byte)k) && (byte)k <= 93)
flags |= KEYEVENTF_EXTENDEDKEY;
input.ki.dwFlags = flags;
input.ki.dwExtraInfo = GetMessageExtraInfo ();
Output ("Sending key down {0}. Flags:{1}", k, flags);
INPUT[] inputs = new INPUT[] { input };
uint result = SendInput ((uint)inputs.Length, inputs, Marshal.SizeOf (typeof (INPUT)));
if ((uint)inputs.Length != result)
MessageBox.Show ("PressKeyDown result = " + Marshal.GetLastWin32Error ());
}
private void PressKeyUp (Keys k)
{
INPUT input = new INPUT ();
input.type = INPUT_KEYBOARD;
input.ki.wVk = (byte)k;
input.ki.wScan = 0;
input.ki.time = 0;
uint flags = KEYEVENTF_KEYUP;
if ((33 <= (byte)k && (byte)k <= 46) || (91 <= (byte)k) && (byte)k <= 93)
flags |= KEYEVENTF_EXTENDEDKEY;
input.ki.dwFlags = flags;
input.ki.dwExtraInfo = GetMessageExtraInfo ();
Output ("Sending key up {0}", k);
INPUT[] inputs = new INPUT[] { input };
uint result = SendInput ((uint)inputs.Length, inputs, Marshal.SizeOf (typeof (INPUT)));
if ((uint)inputs.Length != result)
MessageBox.Show ("PressKeyUp result = " + Marshal.GetLastWin32Error ());
}
private void TestSend ()
{
System.Threading.Thread.CurrentThread.Join (1000);
Keys k = Keys.Menu;
PressKeyDown (k);
System.Threading.Thread.Sleep (100);
k = Keys.Add;
k |= Keys.Alt;
PressKeyDown (k);
System.Threading.Thread.Sleep (100);
k = Keys.NumPad0;
k |= Keys.Alt;
PressKey (k);
System.Threading.Thread.Sleep (100);
k = Keys.NumPad0;
k |= Keys.Alt;
PressKey (k);
System.Threading.Thread.Sleep (100);
k = Keys.E;
k |= Keys.Alt;
PressKey (k);
System.Threading.Thread.Sleep (100);
k = Keys.NumPad5;
k |= Keys.Alt;
PressKey (k);
System.Threading.Thread.Sleep (100);
PressKeyUp (Keys.Add);
PressKeyUp (Keys.Menu);
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace Simulate
{
public class Simulate
{
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
static extern UInt32 SendInput(UInt32 numberOfInputs, INPUT[] input, Int32 sizeOfInputStructure);
[StructLayout(LayoutKind.Sequential, Size = 24)]
struct KEYBDINPUT
{
public UInt16 Vk;
public UInt16 Scan;
public UInt32 Flags;
public UInt32 Time;
public UInt32 ExtraInfo;
}
[StructLayout(LayoutKind.Explicit)]
private struct INPUT
{
[FieldOffset(0)]
public int Type;
[FieldOffset(4)]
public KEYBDINPUT ki;
}
public static void TextInput(string text)
{
char[] chars = text.ToCharArray();
for (int i = 0; i < chars.Length; i++)
{
UInt16 unicode = chars[i];
INPUT down = new INPUT();
down.Type = 1; //INPUT_KEYBOARD
down.ki.Vk = 0;
down.ki.Scan = unicode;
down.ki.Time = 0;
down.ki.Flags = 0x0004; //KEYEVENTF_UNICODE
down.ki.ExtraInfo = 0;
INPUT up = new INPUT();
up.Type = 1; //INPUT_KEYBOARD
up.ki.Vk = 0;
up.ki.Scan = unicode;
up.ki.Time = 0;
up.ki.Flags = 0x0004; //KEYEVENTF_UNICODE
up.ki.ExtraInfo = 0;
INPUT[] input = new INPUT[2];
input[0] = down;
input[1] = up;
SendInput(1, input, Marshal.SizeOf(typeof(INPUT)));
}
}
}
}
// Call the API :
Simulate.TextInput("AbCçDeFgĞhİiJkLmNoÖpQrSşTuÜvXyZ - äÄß_0123456789");
You can generate them by holding down the Alt key and typing in the 4 digit Unicode codepoint on the numeric keypad. å = Alt + 0229, ñ = Alt + 0241. Find other codes with the Charmap.exe applet.
Apparently the processing of a sequence of key presses to represent a unicode character is done at a level that is not accessible through SendInput. I changed my code to set the unicode flag on dwFlags and set the unicode value on the wScan data parameter. I've managed to convince myself after testing with several European and Asian languages that this creates the same results as the multiple keystroke method.
Related
I'm trying to get text which user type from keyboard. Everything seems working fine when I have English Keyboard Layout, bus when I change it into russian it does not work. I have used
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if ((Keys)vkCode == Keys.Enter || (Keys)vkCode == Keys.Tab || (Keys)vkCode == Keys.LButton || (Keys)vkCode == Keys.RButton)
WriteNewLine(_text);
else
if (ShiftKey)
_text += GetCharsFromKeys((Keys)vkCode, true, false);
else
_text += GetCharsFromKeys((Keys)vkCode, false, false);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
This function gets key code. As you can see I call next function
static string GetCharsFromKeys(Keys keys, bool shift, bool altGr)
{
var buf = new StringBuilder(256);
var keyboardState = new byte[256];
if (shift)
keyboardState[(int)Keys.ShiftKey] = 0xff;
if (altGr)
{
keyboardState[(int)Keys.ControlKey] = 0xff;
keyboardState[(int)Keys.Menu] = 0xff;
}
ToUnicode((uint)keys, 0, keyboardState, buf, 256, 0);
return buf.ToString();
}
It returns actual key what user type. But it work only for ENG keyboard layout.
If you know how to add International keyboard layout handling, pls let me know.
P.S. When I enter :
Руский
I get :
Hecrbq
You need to use InstalledInputLanguages and CurrentInputLanguage. There is an example here that you can follow. I will copy the code here in case the link dies. This code is looking to recognize # symbol entry in the European keyboard so you would need to tweak it to meet your needs.
DllImport("user32.dll")]
public static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
public static void GetKeyboardShortcutForChar(char c, InputLanguage lang, out Keys key, out bool shift)
{
var keyCode = VkKeyScanEx(c, lang.Handle);
key = (Keys) (keyCode & 0xFF);
shift = (keyCode & 0x100) == 0x100;
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int ToUnicodeEx(int wVirtKey, uint wScanCode, byte[] lpKeyState, StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
public static char? FromKeys(int keys, bool shift, bool capsLock)
{
var keyStates = new byte[256];
if (shift)
keyStates[16] = 0x80;
if (capsLock)
keyStates[20] = 0x80;
var sb = new StringBuilder(10);
int ret = User32.ToUnicodeEx(keys, 0, keyStates, sb, sb.Capacity, 0, InputLanguage.CurrentInputLanguage.Handle);
return ret == 1 ? (char?)sb[0] : null;
}
I am having the most difficult time marshaling this struct between C# and C++.
What makes it very hard to troubleshoot is that SOMETIMES the strings are populated with data (wtf), but most of the time they are not.
I've tried sending over an Array of structs as well as a IntPtr, but the results are similar, the strings in the struct are almost always empty and I can't figure out what I'm doing wrong in the marshaling. The code is posted below. Any help would be appreciated.
Edit***
Turns out the problem was on the C++ side and all the marshaling stuff was correct. Thanks for the tip Hans. ***
C++:
#pragma pack (push, 1)
typedef struct
{
char FirmwareVers[FS_MAX_FIRMWARE_VER];
char SerialNum[FS_MAX_SERIAL_NUM];
char HardwareVers[FS_MAX_HW_VER];
ULONG StatusFlags;
int LMIndex;
} FS_LMON_STATUS, *PFS_LMON_STATUS;
DllExport int _stdcall FS_GetLMs(PFS_LMON_STATUS pLaunchMonInfo, int MaxLaunchMons, int *pNumLaunchMons)
{
int Cnt;
FS_LMON_STATUS LMStatus;
if(!g_IsInitalized)
return FS_NOT_INITALIZED;
*pNumLaunchMons = 0;
if(MaxLaunchMons == 0)
return FS_ERROR;
for(Cnt = 0; Cnt < MAX_LM_CONNECTIONS; Cnt++)
{
if(g_CreatedClasses.pLMList->GetLMStatus(Cnt, &LMStatus) != FS_SUCCESS)
continue;
if(LMStatus.LMIndex != INVALID_LM_INDEX)
{
memcpy(pLaunchMonInfo, &LMStatus, sizeof(LMStatus));
pLaunchMonInfo++;
(*pNumLaunchMons)++;
MaxLaunchMons--;
if(MaxLaunchMons == 0)
{
return FS_SUCCESS;
}
}
}
return FS_SUCCESS;
}
C#:
[DllImport("FSADLL", SetLastError = false)]
private static extern int FS_GetLMs([Out] IntPtr pLaunchMonInfo, int MaxLaunchMons, ref int pNumLaunchMons);
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] //, Size = 38)]
public struct FS_LMON_STATUS
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FS_MAX_FIRMWARE_VER)] //10 bytes
public string FirmwareVers;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FS_MAX_SERIAL_NUM)] // 15 bytes
public string SerialNum;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FS_MAX_HW_VER)] // 5 bytes
public string HardwareVers;
public uint StatusFlags; //4 bytes
public int LMIndex; // identifies which index //4 bytes
}
const int max_launch_monitors = 8;
FS_LMON_STATUS[] dev_info = new FS_LMON_STATUS[max_launch_monitors];
int num_launch_monitors = 0;
IntPtr pAddr = Marshal.AllocHGlobal(max_launch_monitors * Marshal.SizeOf(typeof(FS_LMON_STATUS)));
Marshal.StructureToPtr(dev_info, pAddr, false);
int result = FS_GetLMs(pAddr, max_launch_monitors, ref num_launch_monitors);
UnityEngine.Debug.Log("Result of FS_GetLMs: " + result);
FS_LMON_STATUS[] device_info = new FS_LMON_STATUS[max_launch_monitors];
//Marshal.Copy(pAddr, device_info, (int)0, num_launch_monitors * (int)Marshal.SizeOf(typeof(FS_LMON_STATUS)));
//Marshal.ReadIntPtr(pAddr, 0);
//device_info = (FS_LMON_STATUS[]) Marshal.PtrToStructure(Marshal.AllocHGlobal(max_launch_monitors * Marshal.SizeOf(typeof(FS_LMON_STATUS[]))), typeof(FS_LMON_STATUS[]));
if (num_launch_monitors > 0)
UnityEngine.Debug.Log("GC2 Device Found.");
else // If there is no devices found, remove the previous device from the holder variable
GC2Device = null;
for (int i = 0; i < num_launch_monitors; i++)
{
device_info[i] = (FS_LMON_STATUS)Marshal.PtrToStructure(pAddr, typeof(FS_LMON_STATUS));
pAddr = new IntPtr(Marshal.SizeOf(typeof(FS_LMON_STATUS)) + pAddr.ToInt64());
}
//*** There will only ever be 1 device in the list until the old SDK is fixed ***
for (int lm_index = 0; lm_index < num_launch_monitors; lm_index++)
{
if (device_info[lm_index].StatusFlags != LM_STATUS_DISCONNECTED)
{
UnityEngine.Debug.Log("device_info.SerialNum: " + device_info[lm_index].SerialNum);
//assign each LM to a LM data structure
LaunchMonitor logical_device = new LaunchMonitor(inst);
logical_device.mLaunchMonitorType = LaunchMonitorType.LAUNCH_MONITOR_TYPE_GC2;
logical_device.mConnectionType = ConnectionType.USB_CONNECTION;
IntPtr pnt = Marshal.AllocHGlobal(Marshal.SizeOf(device_info[lm_index]));
Marshal.StructureToPtr(device_info[lm_index], pnt, false);
//Marshal.Copy(device_info[lm_index], dv_info, 0, (uint)Marshal.SizeOf(typeof(FS_LMON_STATUS)));
logical_device.mConnectionToken = pnt;
//GC2Devices.Add(logical_device);
logical_device.Serial = logical_device.GetSerialNumber();
GC2Device = logical_device;
}
}
Turns out the problem was on the C++ side and all the marshaling stuff was correct. Thanks for the tip Hans.
Im currently using the InputSimulator v0.1.0.0 to simulate keypresses and/or mouse events over Remote Desktop. Basic keypresses (for example pressing 'a') works, but special characters, like 'tab', 'enter' dont.
I simulate entering texts with:
InputSimulator.SimulateTextEntry("blabla");
but the following only works locally:
InputSimulator.SimulateKeyPress(VirtualKeyCode.TAB);
or
InputSimulator.SimulateKeyPress(VirtualKeyCode.RETURN);
I searched over the net for working examples but i havent found anything useful. Anyone has idea how to make it work?
Thanks in advance!
-------------------------OWN ANSWER----------------------------------
After googling some more, i have found this article:
http://www.pinvoke.net/default.aspx/user32.keybd_event
in which there is a good code, that does not solve the InputSimulator problem, but does exactly that i need. Here is the code, and how i used that:
[StructLayout(LayoutKind.Sequential)]
public struct KEYBOARD_INPUT
{
public const uint Type = 1;
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
};
[StructLayout(LayoutKind.Explicit)]
struct KEYBDINPUT
{
[FieldOffset(0)]
public ushort wVk;
[FieldOffset(2)]
public ushort wScan;
[FieldOffset(4)]
public uint dwFlags;
[FieldOffset(8)]
public uint time;
[FieldOffset(12)]
public IntPtr dwExtraInfo;
};
[StructLayout(LayoutKind.Sequential)]
struct HARDWAREINPUT
{
public uint uMsg;
public ushort wParamL;
public ushort wParamH;
};
[StructLayout(LayoutKind.Explicit)]
struct INPUT
{
[FieldOffset(0)]
public int type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
};
[DllImport("user32.dll", SetLastError = true)]
static extern uint SendInput(uint nInputs, IntPtr pInput, int cbSize);
And this is how i called the 'press TAB' event:
keybd_event(0x09, 0x0f, 0, 0); // Tab Press
keybd_event(0x09, 0x0f, 0x0002, 0);
As suggested i copy my solution as an answer. I hope these will help for anyone working on similar problems. :) The solution is a bit long, but the problem was not only 'how-to-press a button programatically', but also 'how-to-make it work via remote desktop' and 'how-to-make a general solution for different keyboards'. Well, im not 100% sure that the last problem is completely solved, but the solution below may be used for further developing. I also know that the code is not optimal and sometimes ugly, but im still testing and developing it! :)
//m_text is the whole text i want to write. It may contain special characters,
//like 'enter', 'tab', lower/upper-case chars, and chars with shit/alt is
//pressed, like ';'.
//Test with this string, its difficult-enough. :)
string m_text = "123qweQWE;{tab}!{eNTer}*|";
IntPtr keyboardLayout = GetKeyboardLayout(0);
while (!string.IsNullOrWhiteSpace(m_text))
{
int m_Index = 0;
//Enter, tab and similar keys are in {} brackets
//(for example {tab}). We get the 'tab' from the
//string and pass this to our method. Key combinations
//are separated by a '+' like {alt+tab+tab}, from this
//we will get 'press the alt, then press the tab, then
//press the tab again'.
if (m_text[m_Index] == '{')
{
#region [ Special chars ]
string m_SubString = m_text.Substring(
m_Index + 1, m_text.IndexOf("}") - 1);
string[] m_Splitted = m_SubString.Split(new char[] { '+' });
for (int i = 0; i < m_Splitted.Length; i++)
{
//If the string is longer than 1 char it means we are processing a tab-like key.
if (m_Splitted[i].Length > 1)
PressSpecial(m_Splitted[i]);
else
{
//If the char is 1-char-long, it means we previously pressed a tab-like key,
//and now we press a simple key, like in the case of {altgr+w}.
//Get the virtual key of the char.
short vKey = VkKeyScanEx(
char.Parse(m_Splitted[i]), keyboardLayout);
//Get the low byte from the virtual key.
byte m_LOWBYTE = (Byte)(vKey & 0xFF);
//Get the scan code of the key.
byte sScan = (byte)MapVirtualKey(m_LOWBYTE, 0);
//Press the key.
//Key down event, as indicated by the 3rd parameter that is 0.
keybd_event(m_LOWBYTE, sScan, 0, 0);
}
}
Application.DoEvents();
//We have pressed all the keys we wanted, now release them in backward-order
//when pressing alt+tab we beed to release them in tab-alt order! The logic is the same.
for (int i = m_Splitted.Length - 1; i > -1; i--)
{
if (m_Splitted[i].Length > 1)
ReleaseSpecial(m_Splitted[i]);
else
{
short vKey = VkKeyScanEx(
char.Parse(m_Splitted[i]), keyboardLayout);
byte m_LOWBYTE = (Byte)(vKey & 0xFF);
byte sScan = (byte)MapVirtualKey(m_LOWBYTE, 0);
//Key up event, as indicated by the 3rd parameter that is 0x0002.
keybd_event(m_LOWBYTE, sScan, 0x0002, 0); //Key up
}
}
Application.DoEvents();
#endregion
//We do not use the '{' and '}' brackets, thats why the '+2'. :)
m_Index = m_SubString.Length + 2;
}
else
{
#region [ One char ]
short vKey = VkKeyScanEx(m_text[m_Index], keyboardLayout);
//Hi-byte indicates if we need to press shift, alt or other similar keys.
byte m_HIBYTE = (Byte)(vKey >> 8);
byte m_LOWBYTE = (Byte)(vKey & 0xFF);
byte sScan = (byte)MapVirtualKey(m_LOWBYTE, 0);
//Press the special key if needed.
if ((m_HIBYTE == 1))
PressShift();
else if ((m_HIBYTE == 2))
PressControl();
else if ((m_HIBYTE == 4))
PressAlt();
else if ((m_HIBYTE == 6))
PressAltGr();
//Press, then release the key.
keybd_event(m_LOWBYTE, sScan, 0, 0); //Key down
keybd_event(m_LOWBYTE, sScan, 0x0002, 0); //Key up
//Release the special key if needed.
if ((m_HIBYTE == 1))
ReleaseShift();
else if ((m_HIBYTE == 2))
ReleaseControl();
else if ((m_HIBYTE == 4))
ReleaseAlt();
else if ((m_HIBYTE == 6))
ReleaseAltGr();
#endregion
//Get the next char from the string.
m_Index++;
}
//Remove the already processed chars from the string.
if (m_Index < m_text.Length)
m_text = m_text.Substring(m_Index);
else
m_text = string.Empty;
}
So, this was the logic that processes a string. Lets see the helper methods that will handle the events:
Press and release special keys are the same, only the first two parameters are different.
Check msdn to get the virtual and scan codes for enter, tab, alt, altgr, etc...
#region [ Press shift ]
private void PressShift()
{
//0xA0 is the virtual key of 'shift'.
//0x2A is the scan code of 'shift'.
keybd_event(0xA0, 0x2A, 0, 0);
}
#endregion
#region [ Release shift ]
private void ReleaseShift()
{
keybd_event(0xA0, 0x2A, 0x0002, 0);
}
#endregion
PressSpecial is similar to the code above, so it can be used for shift as well. I separated some of them into different methods as for me its easier to see what i use in the code (its easier for me to use 'PressShift();' instead of 'PressSpecial("shift")';). :)
private void PressSpecial(string p_Special)
{
switch (p_Special.ToLower()) //<-- use lower version!
{
case "home":
keybd_event(0x24, 0x47, 0, 0);
break;
case "end":
keybd_event(0x23, 0x4F, 0, 0);
break;
//Do the same for any key you need (enter, tab, page up, etc...).
//Remember to get the proper virtual- and scan codes for each keys!
}
}
ReleaseSpecial is the same as PressSpecial, but the 3rd parameter is 0x0002.
private void ReleaseSpecial(string p_Special)
{
switch (p_Special.ToLower())
{
case "home":
keybd_event(0x24, 0x47, 0x0002, 0);
break;
case "end":
keybd_event(0x23, 0x4F, 0x0002, 0);
break;
}
}
And finally, here are the dll import methods. You can put them into a static class, if you wish:
[DllImport("user32.dll", EntryPoint = "keybd_event", CharSet = CharSet.Auto,
ExactSpelling = true)]
public static extern void keybd_event(byte vk, byte scan, int flags, int extrainfo);
[DllImport("user32.dll")]
public static extern IntPtr GetKeyboardLayout(uint idThread);
[DllImport("user32.dll")]
public static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MapVirtualKey(int uCode, int uMapType);
Is there a way via .NET/C# to find out the number of CPU cores?
PS This is a straight code question, not a "Should I use multi-threading?" question! :-)
There are several different pieces of information relating to processors that you could get:
Number of physical processors
Number of cores
Number of logical processors.
These can all be different; in the case of a machine with 2 dual-core hyper-threading-enabled processors, there are 2 physical processors, 4 cores, and 8 logical processors.
The number of logical processors is available through the Environment class, but the other information is only available through WMI (and you may have to install some hotfixes or service packs to get it on some systems):
Make sure to add a reference in your project to System.Management.dll
In .NET Core, this is available (for Windows only) as a NuGet package.
Physical Processors:
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem").Get())
{
Console.WriteLine("Number Of Physical Processors: {0} ", item["NumberOfProcessors"]);
}
Cores:
int coreCount = 0;
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())
{
coreCount += int.Parse(item["NumberOfCores"].ToString());
}
Console.WriteLine("Number Of Cores: {0}", coreCount);
Logical Processors:
Console.WriteLine("Number Of Logical Processors: {0}", Environment.ProcessorCount);
OR
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem").Get())
{
Console.WriteLine("Number Of Logical Processors: {0}", item["NumberOfLogicalProcessors"]);
}
Processors excluded from Windows:
You can also use Windows API calls in setupapi.dll to discover processors that have been excluded from Windows (e.g. through boot settings) and aren't detectable using the above means. The code below gives the total number of logical processors (I haven't been able to figure out how to differentiate physical from logical processors) that exist, including those that have been excluded from Windows:
static void Main(string[] args)
{
int deviceCount = 0;
IntPtr deviceList = IntPtr.Zero;
// GUID for processor classid
Guid processorGuid = new Guid("{50127dc3-0f36-415e-a6cc-4cb3be910b65}");
try
{
// get a list of all processor devices
deviceList = SetupDiGetClassDevs(ref processorGuid, "ACPI", IntPtr.Zero, (int)DIGCF.PRESENT);
// attempt to process each item in the list
for (int deviceNumber = 0; ; deviceNumber++)
{
SP_DEVINFO_DATA deviceInfo = new SP_DEVINFO_DATA();
deviceInfo.cbSize = Marshal.SizeOf(deviceInfo);
// attempt to read the device info from the list, if this fails, we're at the end of the list
if (!SetupDiEnumDeviceInfo(deviceList, deviceNumber, ref deviceInfo))
{
deviceCount = deviceNumber;
break;
}
}
}
finally
{
if (deviceList != IntPtr.Zero) { SetupDiDestroyDeviceInfoList(deviceList); }
}
Console.WriteLine("Number of cores: {0}", deviceCount);
}
[DllImport("setupapi.dll", SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid,
[MarshalAs(UnmanagedType.LPStr)]String enumerator,
IntPtr hwndParent,
Int32 Flags);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet,
Int32 MemberIndex,
ref SP_DEVINFO_DATA DeviceInterfaceData);
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
public int cbSize;
public Guid ClassGuid;
public uint DevInst;
public IntPtr Reserved;
}
private enum DIGCF
{
DEFAULT = 0x1,
PRESENT = 0x2,
ALLCLASSES = 0x4,
PROFILE = 0x8,
DEVICEINTERFACE = 0x10,
}
Environment.ProcessorCount
[Documentation]
WMI queries are slow, so try to Select only the desired members instead of using Select *.
The following query takes 3.4s:
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())
While this one takes 0.122s:
foreach (var item in new System.Management.ManagementObjectSearcher("Select NumberOfCores from Win32_Processor").Get())
Environment.ProcessorCount should give you the number of cores on the local machine.
The the easyest way = Environment.ProcessorCount
Exemple from Environment.ProcessorCount Property
using System;
class Sample
{
public static void Main()
{
Console.WriteLine("The number of processors " +
"on this computer is {0}.",
Environment.ProcessorCount);
}
}
It's rather interesting to see how .NET get this internally to say the least... It's as "simple" as below:
namespace System.Threading
{
using System;
using System.Runtime.CompilerServices;
internal static class PlatformHelper
{
private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 0x7530;
private static volatile int s_lastProcessorCountRefreshTicks;
private static volatile int s_processorCount;
internal static bool IsSingleProcessor
{
get
{
return (ProcessorCount == 1);
}
}
internal static int ProcessorCount
{
get
{
int tickCount = Environment.TickCount;
int num2 = s_processorCount;
if ((num2 == 0) || ((tickCount - s_lastProcessorCountRefreshTicks) >= 0x7530))
{
s_processorCount = num2 = Environment.ProcessorCount;
s_lastProcessorCountRefreshTicks = tickCount;
}
return num2;
}
}
}
}
From .NET Framework source
You can also get it with PInvoke on Kernel32.dll
The following code is coming more or less from SystemInfo.cs from System.Web source located here:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SYSTEM_INFO
{
public ushort wProcessorArchitecture;
public ushort wReserved;
public uint dwPageSize;
public IntPtr lpMinimumApplicationAddress;
public IntPtr lpMaximumApplicationAddress;
public IntPtr dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public ushort wProcessorLevel;
public ushort wProcessorRevision;
}
internal static class SystemInfo
{
static int _trueNumberOfProcessors;
internal static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern void GetSystemInfo(out SYSTEM_INFO si);
[DllImport("kernel32.dll")]
internal static extern int GetProcessAffinityMask(IntPtr handle, out IntPtr processAffinityMask, out IntPtr systemAffinityMask);
internal static int GetNumProcessCPUs()
{
if (SystemInfo._trueNumberOfProcessors == 0)
{
SYSTEM_INFO si;
GetSystemInfo(out si);
if ((int) si.dwNumberOfProcessors == 1)
{
SystemInfo._trueNumberOfProcessors = 1;
}
else
{
IntPtr processAffinityMask;
IntPtr systemAffinityMask;
if (GetProcessAffinityMask(INVALID_HANDLE_VALUE, out processAffinityMask, out systemAffinityMask) == 0)
{
SystemInfo._trueNumberOfProcessors = 1;
}
else
{
int num1 = 0;
if (IntPtr.Size == 4)
{
uint num2 = (uint) (int) processAffinityMask;
while ((int) num2 != 0)
{
if (((int) num2 & 1) == 1)
++num1;
num2 >>= 1;
}
}
else
{
ulong num2 = (ulong) (long) processAffinityMask;
while ((long) num2 != 0L)
{
if (((long) num2 & 1L) == 1L)
++num1;
num2 >>= 1;
}
}
SystemInfo._trueNumberOfProcessors = num1;
}
}
}
return SystemInfo._trueNumberOfProcessors;
}
}
There are many answers here already, but some have heavy upvotes and are incorrect.
The .NET Environment.ProcessorCount WILL return incorrect values and can fail critically if your system WMI is configured incorrectly.
If you want a RELIABLE way to count the cores, the only way is Win32 API.
Here is a C++ snippet:
#include <Windows.h>
#include <vector>
int num_physical_cores()
{
static int num_cores = []
{
DWORD bytes = 0;
GetLogicalProcessorInformation(nullptr, &bytes);
std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> coreInfo(bytes / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
GetLogicalProcessorInformation(coreInfo.data(), &bytes);
int cores = 0;
for (auto& info : coreInfo)
{
if (info.Relationship == RelationProcessorCore)
++cores;
}
return cores > 0 ? cores : 1;
}();
return num_cores;
}
And since this is a .NET C# Question, here's the ported version:
[StructLayout(LayoutKind.Sequential)]
struct CACHE_DESCRIPTOR
{
public byte Level;
public byte Associativity;
public ushort LineSize;
public uint Size;
public uint Type;
}
[StructLayout(LayoutKind.Explicit)]
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION
{
[FieldOffset(0)] public byte ProcessorCore;
[FieldOffset(0)] public uint NumaNode;
[FieldOffset(0)] public CACHE_DESCRIPTOR Cache;
[FieldOffset(0)] private UInt64 Reserved1;
[FieldOffset(8)] private UInt64 Reserved2;
}
public enum LOGICAL_PROCESSOR_RELATIONSHIP
{
RelationProcessorCore,
RelationNumaNode,
RelationCache,
RelationProcessorPackage,
RelationGroup,
RelationAll = 0xffff
}
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
{
public UIntPtr ProcessorMask;
public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
public SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION ProcessorInformation;
}
[DllImport("kernel32.dll")]
static extern unsafe bool GetLogicalProcessorInformation(SYSTEM_LOGICAL_PROCESSOR_INFORMATION* buffer, out int bufferSize);
static unsafe int GetProcessorCoreCount()
{
GetLogicalProcessorInformation(null, out int bufferSize);
int numEntries = bufferSize / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
var coreInfo = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[numEntries];
fixed (SYSTEM_LOGICAL_PROCESSOR_INFORMATION* pCoreInfo = coreInfo)
{
GetLogicalProcessorInformation(pCoreInfo, out bufferSize);
int cores = 0;
for (int i = 0; i < numEntries; ++i)
{
ref SYSTEM_LOGICAL_PROCESSOR_INFORMATION info = ref pCoreInfo[i];
if (info.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore)
++cores;
}
return cores > 0 ? cores : 1;
}
}
public static readonly int NumPhysicalCores = GetProcessorCoreCount();
One option would be to read the data from the registry.
MSDN Article On The Topic: http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.localmachine(v=vs.71).aspx)
The processors, I believe can be located here, HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor
private void determineNumberOfProcessCores()
{
RegistryKey rk = Registry.LocalMachine;
String[] subKeys = rk.OpenSubKey("HARDWARE").OpenSubKey("DESCRIPTION").OpenSubKey("System").OpenSubKey("CentralProcessor").GetSubKeyNames();
textBox1.Text = "Total number of cores:" + subKeys.Length.ToString();
}
I am reasonably sure the registry entry will be there on most systems.
Thought I would throw my $0.02 in.
You can use this class:
public static class CpuCores
{
private static int cores = 0;
public static int Number
{
get
{
if (cores > 0) return cores;
RegistryKey key = Registry.LocalMachine.OpenSubKey(#"SYSTEM\CurrentControlSet\Control\Class\" +
"{50127dc3-0f36-415e-a6cc-4cb3be910b65}");
if (key == null)
{
cores = Environment.ProcessorCount;
return cores;
}
string[] subkeys = key.GetSubKeyNames();
key.Close();
cores = 0;
if (subkeys != null && subkeys.Length > 0) foreach (string s in subkeys)
{
if (s.Length != 4) continue;
int n;
if (int.TryParse(s, out n) && ++n > cores) cores = n;
}
if (cores <= 0) cores = Environment.ProcessorCount;
return cores;
}
}
}
I was looking for the same thing but I don't want to install any nuget or servicepack, so I found this solution, it is pretty simple and straight forward,
using this discussion, I thought it would be so easy to run that WMIC command and get that value, here is the C# code. You only need to use System.Management namespace (and couple more standard namespaces for process and so on).
string fileName = Path.Combine(Environment.SystemDirectory, "wbem", "wmic.exe");
string arguments = #"cpu get NumberOfCores";
Process process = new Process
{
StartInfo =
{
FileName = fileName,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
process.Start();
StreamReader output = process.StandardOutput;
Console.WriteLine(output.ReadToEnd());
process.WaitForExit();
int exitCode = process.ExitCode;
process.Close();
I would like to programmatically check the value of, and be able to toggle num-lock. What's the simplest way to do that in C#?
The reason is that I want to verify num-lock is "ON" at program start.
Thanks
Check How to programmatically turn on the Numlock Key
using System;
using System.Runtime.InteropServices;
class SetNumlockKeyOn
{
[StructLayout(LayoutKind.Sequential)]
public struct INPUT
{
internal int type;
internal short wVk;
internal short wScan;
internal int dwFlags;
internal int time;
internal IntPtr dwExtraInfo;
int dummy1;
int dummy2;
internal int type1;
internal short wVk1;
internal short wScan1;
internal int dwFlags1;
internal int time1;
internal IntPtr dwExtraInfo1;
int dummy3;
int dummy4;
}
[DllImport(“user32.dll”)]
static extern int SendInput(uint nInputs, IntPtr pInputs, int cbSize);
public static void SetNumlockOn()
{
const int mouseInpSize = 28;//Hardcoded size of the MOUSEINPUT tag !!!
INPUT input = new INPUT();
input.type = 0x01; //INPUT_KEYBOARD
input.wVk = 0x90; //VK_NUMLOCK
input.wScan = 0;
input.dwFlags = 0; //key-down
input.time = 0;
input.dwExtraInfo = IntPtr.Zero;
input.type1 = 0x01;
input.wVk1 = 0x90;
input.wScan1 = 0;
input.dwFlags1 = 2; //key-up
input.time1 = 0;
input.dwExtraInfo1 = IntPtr.Zero;
IntPtr pI = Marshal.AllocHGlobal(mouseInpSize * 2);
Marshal.StructureToPtr(input, pI, false);
int result = SendInput(2, pI, mouseInpSize); //Hardcoded size of the MOUSEINPUT tag !!!
//if (result == 0 || Marshal.GetLastWin32Error() != 0)
// Console.WriteLine(Marshal.GetLastWin32Error());
Marshal.FreeHGlobal(pI);
}
You can do this via P/Invoke with GetKeyboardState and keybd_event.
The MSDN page for keybd_event shows exactly how to toggle num-lock, as well as get it's state (in C++).
There are P/Invoke signitures available on pinvoke.net for keybd_event and GetKeyboardState.
In addition to the answer given by Arsen:
There are problems with heap corruption in 64-bit builds. Programs using this code may crash at any point. To see this, enable the debug option "Enable Windows debug heap allocator". The debugger stops on calling FreeHGlobal.
It helps to calculate the size of the INPUT structure as follows.
int mouseInpSize = Marshal.SizeOf(input);
IntPtr pI = Marshal.AllocHGlobal(mouseInpSize);
Marshal.StructureToPtr(input, pI, false);
int result = SendInput(2, pI, mouseInpSize);
Marshal.FreeHGlobal(pI);