How to write an localized on-screen-keyboard - c#

I have to write an on screen keyboard for our company's program, which is mostly used on industry's PCs with touch capability.
We can't use the windows default keyboard because we don't need all keys on the keyboard. So I was asked to write a custom one in C#.
I already found this blog as reference, but I'm not sure how to start.
I created a small prototype GUI and assign for each key a scancode, and translate these scancodes to the related character. And send them to the active control. But I'm not sure what scancodes I should use.
So my question is, is that the correct way to write a OSK like this and if yes which scancodes should I use? Any links?
I'm also not sure how to handle the shift states...
Edit:
Okay I did a bit more research and came up with a osk which reads the current keyboard layout and even handles the easy shift states (Shift and Alt Gr). I wrote a KeyButton class which inherits from Button, this KeyButton has a ScanCode property of type byte and if you assign a valid scancode to it, the KeyButton will call the related functions to get the correct text. I used the functions from Michael Kaplan blogs with some small changes. In the end it turned out that I just had to do the same as he did.
So the answer to my question is: Yes, you have to use scancodes on your buttons and then get the virtualkey and the unicode from the keyboard layout. Use these scancodes.
Now I get the characters the only thing left is to send these around.

I think this is fairly simple, just make a series of buttons and assign each button a letter, and inside the buttons Click method you can do a simple.
SendKeys.Send("A");
Changing key based on button etc

I wrote mapping classes that map key code to character for WPF application.
May be this can help.
public class KeyMapper
{
/// <summary>
/// Map key code to character.
/// If key code cannot be mapped returns empty char.
/// </summary>
public static char MapKey(Key key, bool shiftPressed, string culture)
{
CheckCulture(culture);
int englishVirtuaCode = KeyInterop.VirtualKeyFromKey(key);
return EnglishVirtualCodeToChar(englishVirtuaCode, shiftPressed, culture);
}
private static void CheckCulture(string culture)
{
InputLanguage language = InputLanguage.FromCulture(new CultureInfo(culture));
if (language == null)
throw new ArgumentException(string.Format("culture {0} does not exist.", culture));
}
private static char EnglishVirtualCodeToChar(int enlishVirtualCode, bool shiftPressed, string culture)
{
var scanCode = KeyMappingWinApi.MapVirtualKeyEx((uint)enlishVirtualCode, 0, EnglishCultureHandle);
var vitualKeyCode = KeyMappingWinApi.MapVirtualKeyEx(scanCode, 1, GetCultureHandle(culture));
byte[] keyStates = GetKeyStates(vitualKeyCode, shiftPressed);
const int keyInformationSize = 5;
var stringBuilder = new StringBuilder(keyInformationSize);
KeyMappingWinApi.ToUnicodeEx(vitualKeyCode, scanCode, keyStates, stringBuilder, stringBuilder.Capacity, 0, GetCultureHandle(culture));
if (stringBuilder.Length == 0)
return ' ';
return stringBuilder[0];
}
private static IntPtr EnglishCultureHandle
{
get { return GetCultureHandle("en-US"); }
}
private static IntPtr GetCultureHandle(string culture)
{
return InputLanguage.FromCulture(new CultureInfo(culture)).Handle;
}
/// <summary>
/// Gets key states for ToUnicodeEx function
/// </summary>
private static byte[] GetKeyStates(uint keyCode, bool shiftPressed)
{
const byte keyPressFlag = 0x80;
const byte shifPosition = 16; // position of Shift key in keys array
var keyStatses = new byte[256];
keyStatses[keyCode] = keyPressFlag;
keyStatses[shifPosition] = shiftPressed ? keyPressFlag : (byte)0;
return keyStatses;
}
}
public class KeyMappingWinApi
{
[DllImport("user32.dll")]
public static extern uint MapVirtualKeyEx(uint uCode, uint uMapType, IntPtr dwhkl);
[DllImport("user32.dll")]
public static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState,
[Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
[DllImport("user32.dll")]
public static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
}

You might want to check out these guys:
http://cnt.lakefolks.com/
Might be cheaper just to buy it rather than develop it, ya never know ;-)

Related

C# Screen size without references in interactive

I want to get the screen size of the primary screen, without adding any references (e.g. WinForms or Presentation). I found a similar question here, however there is no solution which doesn't include downloading or something like that.
But I want to make a method, which can be executed in the C# interactive on any other pc. Therefore I need a solution that doesn't reference other stuff than the standard (E.g. System, System.Core, ... is allowed).
I do know this is possible with
System.Windows.Forms.Screen.PrimaryScreen.Bounds;
but as this requires the System.Windows.Forms reference, it's not suitable for me. But basically the result of this snippet is what I want to get without references.
Here's an example I came up with.
I have noticed that it does not work correctly on High-DPI Screens. It will report the apparent resolution, not the actual resolution.
static void Main(string[] args)
{
var size = GetScreenSize();
Console.WriteLine(size.Length + " x " + size.Width);
Console.ReadLine();
}
static Size GetScreenSize()
{
return new Size(GetSystemMetrics(0), GetSystemMetrics(1));
}
struct Size
{
public Size(int l, int w)
{
Length = l;
Width = w;
}
public int Length { get; set; }
public int Width { get; set; }
}
[DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern int GetSystemMetrics(int nIndex);
If you don't want to use those libraries, You'll probably need to use native methods. I can't think of a way around this, as you'll need to communicate with the system any way you go.
A good place to start would be the source of System.Windows.Forms.Screen
Here's a link to the source. I bet you could strip down their code, and get the bare minimum.
I actually found a solution to this:
using System;
using System.Runtime.InteropServices;
static void Main()
{
EnumWindows(E, IntPtr.Zero);
Console.Write($"{_.Item1}x{_.Item2}");
}
struct R
{
int l;
int t;
public int r;
public int b;
public override string ToString() => $"{l},{t},{r},{b}";
public bool i() => l == 0 && r != 00;
}
static (int, int) _;
static bool E(IntPtr w, IntPtr l)
{
var r = new R();
GetWindowRect(w, ref r);
if (r.i() && _.Item1 == 0)
_ = (r.r, r.b);
return true;
}
delegate bool P(IntPtr w, IntPtr l);
[DllImport("user32.dll")]
static extern bool EnumWindows(P e, IntPtr l);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr w, ref R r);
Main()
Paste this into your interactive and it should output the screen resolution - at least it does for me.
Don't ask me how it works, it's stumped together from different tutorials and pressed into the interactive. Therefore I can't guarantee this will work on every pc.
Could somebody else please test this?

Does the value of APPCOMMAND_VOLUME_UP and APPCOMMAND_VOLUME_DOWN really matter as long as it starts with 0xA and 0x9?

I'm using the below code to modify system volume programmatically. I would be honest with you that I got the same code from the internet from a lot of sources.
//private const int APPCOMMAND_VOLUME_UP = 0xA0000;
private const int APPCOMMAND_VOLUME_UP = 0xAFFFF;
private const int APPCOMMAND_VOLUME_DOWN = 0x90000;
private const int WM_APPCOMMAND = 0x319;
[DllImport("user32.dll")]
public static extern IntPtr SendMessageW(IntPtr hWnd, int Msg,
IntPtr wParam, IntPtr lParam);
private void btnVolumeUp_Click(object sender, RoutedEventArgs e)
{
SendMessageW(new WindowInteropHelper(this).Handle, WM_APPCOMMAND, new WindowInteropHelper(this).Handle,
(IntPtr)APPCOMMAND_VOLUME_UP);
}
I've observed that the values of the two variables APPCOMMAND_VOLUME_UP and APPCOMMAND_VOLUME_DOWN don't really matter as long as the up value starts with 0xA and the down value starts with 0x9. I have tried with many different values between 0000 and FFFF for both UP and DOWN scenarios. Is it really true that the values don't matter?
I don't have much knowledge about the interop calls. Can someone please explain the significance of those values?
Thank you in advance!
There is no "value". The LPARAM argument encodes three distinct properties:
The command, for example APPCOMMAND_VOLUME_UP is 10 (0x0A).
The device, indicates how the command was generated and can be key, mouse or oem.
The "keys", indicates which modifiers were in effect when the command was generated. Like shift/ctrl for a keyboard and the clicked mouse button for a mouse
These three properties are encoded the way bitfields work in the C language. The bit pattern in hex is 0xDCCCKKKK where C is command, D is device, K is keys.
Since you synthesize the message yourself, you have no meaningful way to report the device or the keys. Nor does it matter, you should simply use 0 (device = keyboard, no modifier keys). Do note that the value you use now is not correct, 0xAFFFF does not use a correct keys value and you are saying that the CTRL and SHIFT keys are down. Probably not enough to terminally confuse the shell, these modifier keys don't affect the way that particular command works.
So a sane implementation would look like:
public enum AppCommand {
VolumeDown = 9
VolumeUp = 10,
// etc..
}
private void SendAppCommand(AppCommand cmd) {
var hwnd = new WindowInteropHelper(this).Handle;
SendMessageW(hwnd, WM_APPCOMMAND, hwnd, (int)cmd << 16);
}

TabTip click &123 key programmatically

I have a C# winForms application that makes use of the Windows 8 keyboard.
I open the keyboard by launching tabtip.exe.
I am able to close the keyboard using a PostMessage command like this:
public static void HideOnScreenKeyboard()
{
uint WM_SYSCOMMAND = 274;
uint SC_CLOSE = 61536;
IntPtr KeyboardWnd = FindWindow("IPTip_Main_Window", null);
PostMessage(KeyboardWnd.ToInt32(), WM_SYSCOMMAND, (int)SC_CLOSE, 0);
}
I think using PostMessage it should be possible to simulate almost anything programmatically if you just pass the correct values.
The values used for closing the keyboard (274 and 61536) I just found on the internet.
It looks that it is possible to grab these values using Spy++, or some other tools but I am unable how to do this.
Can anybody tell me the values needed to simulate a press on the &123 key, so the keyboard switches to the numeric keyboard?
Or, does anybody know how to get these values?
I have tried Spy++, but so many messages are passing constantly that I don't know where to look.
Look at the image of the OnScreenKeyboard to see what key I mean
You could try to use SendInput to simulate a mouse click event on the &123 button of the virtual keyboard window.
Below is an example of how to use SendInput to send a mouse click (left_down + left_up) to the button but I haven't included the code to programatically find the window and get the window size.
[StructLayout(LayoutKind.Sequential)]
public struct MINPUT
{
internal uint type;
internal short dx;
internal short dy;
internal ushort mouseData;
internal ushort dwFlags;
internal ushort time;
internal IntPtr dwExtraInfo;
internal static int Size
{
get { return Marshal.SizeOf(typeof(INPUT)); }
}
}
const ushort MOUSEEVENTF_ABSOLUTE = 0x8000;
const ushort MOUSEEVENTF_LEFTDOWN = 0x0002;
const ushort MOUSEEVENTF_LEFTUP = 0x0004;
// programatically determine the position and size of the TabTip window
// compute the location of the center of the &123 key
int coordinateX = ...
int coordinateY = ...
var pInputs = new[] {
new MINPUT() {
type = 0×01; //INPUT_KEYBOARD
dx = coordinateX,
dy = coordinateY;
dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN;
time = 0;
dwExtraInfo = IntPtr.Zero;
},
new MINPUT() {
type = 0×01; //INPUT_KEYBOARD
dx = coordinateX,
dy = coordinateY;
dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP;
time = 0;
dwExtraInfo = IntPtr.Zero;
}
};
SendInput((uint)pInputs.Length, pInputs, MINPUT.Size);
Why don't you set the input scope of the edit control to numeric? New built-in edit controls with the correct properties automatically trigger the numeric mode when touched.
Rather than hacking the Touch Input Panel which appears differently in different locales, etc. set InputScope of the text box to Number and let Windows do the magic.

Send multimedia commands

Is there some way that I can send multimedia control commands like next song, pause, play, vol up, etc. to the operating system?
Commands that are sent when pressing Fn + some mapped ..key.
I am making a remote control for PC and sending those commands is essential.
You can use keybd_event to simulate keys presses, you have to simulate key down and then key up in order to recognize correctly
[DllImport("user32.dll", SetLastError = true)]
public static extern void keybd_event(byte virtualKey, byte scanCode, uint flags, IntPtr extraInfo);
public const int VK_MEDIA_NEXT_TRACK = 0xB0;
public const int VK_MEDIA_PLAY_PAUSE = 0xB3;
public const int VK_MEDIA_PREV_TRACK = 0xB1;
public const int KEYEVENTF_EXTENDEDKEY = 0x0001; //Key down flag
public const int KEYEVENTF_KEYUP = 0x0002; //Key up flag
private void ButtonClick(object sender, EventArgs e)
keybd_event(VK_MEDIA_PREV_TRACK, 0, KEYEVENTF_EXTENDEDKEY, IntPtr.Zero);
keybd_event(VK_MEDIA_PREV_TRACK, 0, KEYEVENTF_KEYUP, IntPtr.Zero);
}`
Actually, the answer of dxramax gives me erratic behavior. I'm posting this answer that gives me consistent behavior, and also has some more details.
To send multimedia keys, including Play/Pause, NextTrack, PrevTrack, etc, you can use keybd_event:
public class Program
{
public const int KEYEVENTF_EXTENTEDKEY = 1;
public const int KEYEVENTF_KEYUP = 0;
public const int VK_MEDIA_NEXT_TRACK = 0xB0;
public const int VK_MEDIA_PLAY_PAUSE = 0xB3;
public const int VK_MEDIA_PREV_TRACK = 0xB1;
[DllImport("user32.dll")]
public static extern void keybd_event(byte virtualKey, byte scanCode, uint flags, IntPtr extraInfo);
public static void Main(string[] args)
{
keybd_event(VK_MEDIA_PLAY_PAUSE, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); // Play/Pause
//keybd_event(VK_MEDIA_PREV_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); // PrevTrack
//keybd_event(VK_MEDIA_NEXT_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); // NextTrack
}
Here is a list to the supported key codes that this windows api can handle:
https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
The SendKeys class is very nice, but it's also limited. The approach above sends the key command directly to Windows OS.
Unfortunately, in most cases, keys Fn can't be sent using Windows API and as a result - using .NET classes. It depends on how the manufacturer has done this functionality. Probably it is supported by additional driver or even go over operation system.
You can check if it's possible to send Fn commands from the code by trying to hook them using Windows API code or some application like AutoHotKey. For instance, on my laptop, I can't hook multimedia commands.
Otherwise, if you are lucky, use SendKeys as mentioned in the comments.
If anyone wonders on this page like me. All posts above do not work, you also need bScan which is second parameter, you can get it with MapVirutalKey(VKCode,0).
Ex:
int keyValue = VK_MEDIA_NEXT_TRACK;
keybd_event(keyValue, MapVirtualKey(keyValue, 0), KEYEVENTF_KEYUP, 0);
keybd_event(keyValue, MapVirtualKey(keyValue, 0), KEYEVENTF_KEYUP, 0);

Get ListView item text from other window

I want to make a little application that changes the default playback device in windows 7. The only solution was to interact with the Sound Applet. I succeeded to get the handle to the SysListView32 window that has the devices name but i cant get the text from the ListView.
This is the code used:
IntPtr sListView = (window handle received from another function)
LVITEM lvi = new LVITEM();
lvi.mask = LVIF_TEXT;
lvi.cchTextMax = 1024;
lvi.iItem = 0; // i tried with a loop trought all the items
lvi.iSubItem = 0;
lvi.pszText = Marshal.AllocHGlobal(1024);
IntPtr ptrLvi = Marshal.AllocHGlobal(Marshal.SizeOf(lvi));
Marshal.StructureToPtr(lvi, ptrLvi, false);
SendMessage(sListView, (int)WinMesages.LVM_GETITEMW, IntPtr.Zero, ptrLvi);
string strLvi = Marshal.PtrToStringAuto(lvi.pszText);
The result (strLvi) are some chinese letters. What is wrong in the script?
UPDATE: LVITEM struct is this:
private struct LVITEM
{
public uint mask;
public int iItem;
public int iSubItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public IntPtr lParam;
}
The sLIstView handle is correct... a checked in spy++.
What test do i need to perform to check where is the problem? I could give you all the script if that would help.
Have you tried using LWM_GETITEMTEXTW instead?

Categories