Keyboard layout change not detected in universal windows apps with C# - c#

I wanna detect every keyboard layout change with C#. It doesn't matter whether it is switched by win + space or alt + shift or with mouse...
I wrote a code that works considerably well (see below) for desktop apps. But it doesn't work for UWP apps. If I switch layout while within the UWP app, it is not detected, if I switch to desktop app the change is detected right away... How can I do it to detect any change in the layout? Is there any other way how to find out what layout is active at any given moment no matter what window is active?
My code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Globalization;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string lastLang = "";
while (true)
{
string langSuffix = GetKeyboardLayoutIdAtTime();
if (!langSuffix.Equals(lastLang))
{
// do something
Console.WriteLine(getCurrentTimeStamp() + ": Changing '" + lastLang + "' to '" + langSuffix + "'.");
lastLang = langSuffix;
}
System.Threading.Thread.Sleep(1000);
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern int GetWindowThreadProcessId(IntPtr handleWindow, out int lpdwProcessID);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetKeyboardLayout(int WindowsThreadProcessID);
public static string GetKeyboardLayoutIdAtTime()
{
IntPtr hWnd = GetForegroundWindow();
int lpdwProcessId;
InputLanguageCollection installedInputLanguages = InputLanguage.InstalledInputLanguages;
CultureInfo currentInputLanguage = null;
int WinThreadProcId = GetWindowThreadProcessId(hWnd, out lpdwProcessId);
IntPtr KeybLayout = GetKeyboardLayout(WinThreadProcId);
// this remain unchanged when I switch layouts in UWP
Console.WriteLine("KL IntPtr: " + KeybLayout);
for (int i = 0; i < installedInputLanguages.Count; i++)
{
if (KeybLayout == installedInputLanguages[i].Handle) currentInputLanguage = installedInputLanguages[i].Culture;
}
if(currentInputLanguage == null)
{
Console.WriteLine(getCurrentTimeStamp() + "current input language is null...");
}
return currentInputLanguage.TwoLetterISOLanguageName;
}
private static string getCurrentTimeStamp()
{
return DateTime.Now.ToString("yyyyMMddHHmmssffff");
}
}
}

In Desktop, we use the Input Method Manager to communicate with an input method editor (IME), which runs as a service. In UWP, we should be able to use the Text Services Framework. There is a document about Alternatives to Windows APIs in Universal Windows Platform (UWP) apps.
So we should be able to use the Windows​.UI​.Text​.Core namespace, it provides types for accessing the Windows core text APIs and the text input server. Windows core text is a client-server system that centralizes the processing of keyboard input into a single server.
We can find the InputLanguageChanged event in the CoreTextServicesManager class. It occurs when the current input language has changed. When we switched the input method by win + space or alt + shift or with mouse, the InputLanguageChanged will be fired.
For example:
public MainPage()
{
this.InitializeComponent();
CoreTextServicesManager textServiceManager = CoreTextServicesManager.GetForCurrentView();
textServiceManager.InputLanguageChanged += TextServiceManager_InputLanguageChanged;
}
private void TextServiceManager_InputLanguageChanged(CoreTextServicesManager sender, object args)
{
Debug.WriteLine("Keyboard layout is changed!");
}

Related

c# trouble displaying unicode card suits between different versions of Windows

Using c# .net 4.7.1, I'm trying to make a console app Blackjack game and I'm having trouble displaying the card suits to the console output with different versions of Windows. For Windows 7, this is the Main method that displays the suits correctly:
static void Main(string[] args)
{
string[] Suits = new string[] { "♠", "♣", "♥", "♦" };
Methods.Print(Suits[0] + " " + Suits[1] + " " + Suits[2] + " " + Suits[3]);
Console.ReadLine();
....
}
the suits display as I want them, like this:
But if I run my program with this Main method in it on my Windows 10 machine they display like this:
I've found that if I include this line in my Main method on my Windows 10 machine then the suits display as I want them to:
Console.OutputEncoding = System.Text.Encoding.UTF8;
But then that makes it so the suits don't display correctly on my Windows 7 machine. Can anyone help me out with how I can have these card suits display properly regardless of what Windows OS the program is run on? Thanks in advance.
If you want it to work reliably in the console then here is my solution:
static void Main(string[] args)
{
Console.WriteLine("D, C, H, S");
Console.ReadLine();
}
Here are 2 other options:
Check windows versions and test all scenarios using ♠, ♣, ♥, ♦ and Encoding.UTF8;
Gui application.
Windows 7
The problem with Windows7 console and
Console.OutputEncoding = Encoding.UTF8;
Console.WriteLine("♠, ♣, ♥, ♦");
Console.ReadLine();
is most likely the font in console app.
From Console.OutputEncoding Property:
Note that successfully displaying Unicode characters to the console requires the following:
The console must use a TrueType font, such as Lucida Console or Consolas, to display characters.
You can change the font in the Console app's properties:
Thanks to #tymtam for the insight on the issue I'm having. I looked into the possibility of changing the console font as a solution. I found this article that shows how to programmatically change the console font to Lucida Console which is a true type font. Here's my formatted code from that link:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
namespace BlackJack
{
class BlackJack
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetCurrentConsoleFontEx(IntPtr consoleOutput, bool maximumWindow, ref CONSOLE_FONT_INFO_EX consoleCurrentFontEx);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int dwType);
[DllImport("kernel32.dll", SetLastError = true)]
static extern int SetConsoleFont(IntPtr hOut, uint dwFontNum);
private const int STD_OUTPUT_HANDLE = -11;
private const int TMPF_TRUETYPE = 4;
private const int LF_FACESIZE = 32;
private static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal unsafe struct CONSOLE_FONT_INFO_EX
{
internal uint cbSize;
internal uint nFont;
internal COORD dwFontSize;
internal int FontFamily;
internal int FontWeight;
internal fixed char FaceName[LF_FACESIZE];
}
[StructLayout(LayoutKind.Sequential)]
internal struct COORD
{
internal short X;
internal short Y;
internal COORD(short x, short y)
{
X = x;
Y = y;
}
}
public static void SetConsoleFont(string fontName = "Lucida Console")
{
unsafe
{
IntPtr hnd = GetStdHandle(STD_OUTPUT_HANDLE);
if (hnd != INVALID_HANDLE_VALUE)
{
CONSOLE_FONT_INFO_EX info = new CONSOLE_FONT_INFO_EX();
info.cbSize = (uint)Marshal.SizeOf(info);
// Set console font to Lucida Console.
CONSOLE_FONT_INFO_EX newInfo = new CONSOLE_FONT_INFO_EX();
newInfo.cbSize = (uint)Marshal.SizeOf(newInfo);
newInfo.FontFamily = TMPF_TRUETYPE;
IntPtr ptr = new IntPtr(newInfo.FaceName);
Marshal.Copy(fontName.ToCharArray(), 0, ptr, fontName.Length);
// Get some settings from current font.
newInfo.dwFontSize = new COORD(info.dwFontSize.X, info.dwFontSize.Y);
newInfo.FontWeight = info.FontWeight;
SetCurrentConsoleFontEx(hnd, false, ref newInfo);
}
}
}
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
SetConsoleFont();
....
}
Two things to note
I had to add this using statement for it to work:
using System.Runtime.InteropServices;
I had to check the Allow unsafe code checkbox located in the Project>Properties>Build screen as shown below:
After making these changes the program runs on both Windows 7 and Windows 10 and it displays the card suits as I want them to. Like I said before, I don't have access to machines that have other versions of Windows on them, so I can only say that this runs on Windows 7 and Windows 10 for sure.

Extract icon from DLL with transparency

I have the following C# code to extract an icon with a specific index from a specific DLL:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
public class ExtractIcon
{
public static Icon Extract(string file, int number, bool largeIcon)
{
IntPtr large;
IntPtr small;
ExtractIconEx(file, number, out large, out small, 1);
try
{
return Icon.FromHandle(largeIcon ? large : small);
}
catch
{
return null;
}
}
[DllImport("Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons);
}
This works fine. Sort of. Because it doesn't handle transparency.
Take a look:
How can I fix this?
EDIT
Credits to #rbmm
The problem was not the code above but rather the code I was using to convert from Icon to Bitmap. I was using Bitmap.FromHIcon, which apparently discards the transparency. I now use a custom method to convert between these two and it works flawlessly.

Why does my C# console application magically halt but resume upon pressing a key? [duplicate]

I have a very simple C# console application that displays some text and loops waiting for input until the escape key is pressed or the timeout period is served.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
namespace SampleApp
{
public static class Program
{
public static void Main (string [] args)
{
var key = new ConsoleKeyInfo();
var watch = Stopwatch.StartNew();
var timeout = TimeSpan.FromSeconds(5);
Console.WriteLine("Press escape to return to the previous screen...");
Console.WriteLine();
do
{
Console.WriteLine("This screen will automatically close in " + ((timeout + TimeSpan.FromSeconds(1)) - watch.Elapsed).ToString(#"hh\:mm\:ss") + ".");
if (Console.KeyAvailable) { key = Console.ReadKey(true); }
else { Thread.Sleep(TimeSpan.FromSeconds(0.10D)); }
}
while ((key.Key != ConsoleKey.Escape) && (timeout > (watch.Elapsed - TimeSpan.FromSeconds(0.5D))));
watch.Stop();
}
}
}
This works fine but if I click on the console app with the mouse (to gain focus for example), all activity on the screen freezes until I right click or press escape. During this time, the title of the console also changes to "Select AppName" assuming "AppName" was the title before.
If I right-click ion the console first, the do {...} while (); loop seems to go crazy and prints a lot of extra lines.
Since I am not aware of this behavior of the console, not sure what to ask. Is this to be expected? If so, can I change this behavior? If not, any suggestions for workarounds would be appreciated.
The issue was solved using Hans' comment above.
Sounds to me that you've activated the console window's Mark and Paste commands somehow. Normally activate through the system menu (Alt + Space, Edit, Mark/Paste). That doesn't have anything to do with this code of course.
Apparently, Quick Edit Mode was set in the console defaults (Alt + Space, Defaults, Options, Edit Options, Quick Edit Mode) for some reason. Unchecking that resolved the issue.
//call this class to disable quick edit mode.
public static void Main()
{
//disable console quick edit mode
DisableConsoleQuickEdit.Go();
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
static class DisableConsoleQuickEdit
{
const uint ENABLE_QUICK_EDIT = 0x0040;
// STD_INPUT_HANDLE (DWORD): -10 is the standard input device.
const int STD_INPUT_HANDLE = -10;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll")]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
internal static bool Go()
{
IntPtr consoleHandle = GetStdHandle(STD_INPUT_HANDLE);
// get current console mode
uint consoleMode;
if (!GetConsoleMode(consoleHandle, out consoleMode))
{
// ERROR: Unable to get console mode.
return false;
}
// Clear the quick edit bit in the mode flags
consoleMode &= ~ENABLE_QUICK_EDIT;
// set the new mode
if (!SetConsoleMode(consoleHandle, consoleMode))
{
// ERROR: Unable to set console mode
return false;
}
return true;
}
}

Getting the exact display name of the metro style apps

I am using PackageManager class to list the installed metro style applications in the system.
PackageId.Name does not return the actual name of the package, Neither is Package.DisplayName. Package.DisplayName returns empty string. It does return display name only for Package.Current.
When I tried to use AppxManifest.xml, I could not get the display name from
Package->Properties->Displayname too.
<Properties>
<DisplayName>ms-resource:///Resources/AppStoreName</DisplayName>
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
<Logo>Assets\WindowsIcons\StoreLogo.png</Logo>
</Properties>
Where can I get the exact display name of metro style applications ?
If you already have a reference to a Package instance, you can get the Display Name using an interop method and the registry. Here's a complete example:
using Microsoft.Win32;
using System;
using System.Runtime.InteropServices;
using System.Text;
using Windows.ApplicationModel;
public class PackageUtil
{
[DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode,
ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)]
private static extern int SHLoadIndirectString(string pszSource,
StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved);
private static string GetPackageDisplayName(Package package)
{
using (var key = Registry.ClassesRoot.OpenSubKey(
#"Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel" +
$#"\Repository\Packages\{package.Id.FullName}\App\Capabilities"))
{
var sb = new StringBuilder(256);
Shlwapi.SHLoadIndirectString((string)key.GetValue("ApplicationName"),
sb, sb.Capacity, IntPtr.Zero);
return sb.ToString();
}
}
}

How to access a window?

I'm trying to access a specific window using its handle (that is System.IntPtr value):
// Getting the process of Visual Studio program
var process = Process.GetProcessesByName("devenv")[0];
// Showing the handle we've got, we've no problem
MessageBox.Show(this, process.MainWindowHandle.ToString());
// Attempting to get the main window object by its handle
var wnd = NativeWindow.FromHandle(process.MainWindowHandle);
// always fails
if (wnd == null)
MessageBox.Show("Failed");
else
MessageBox.Show(wnd.ToString(), "Yeeeeees!!");
I have tried also to access another demo .net winforms application's main window, that I have made for this purpose, (i.e. I run the demo application, and attempted to access its main window by this application), and failed, too, although both the demo and this application are .NET applications. However, this successes:
var process2 = Process.GetCurrentProcess();
MessageBox.Show(this, process2.MainWindowHandle.ToString());
var wnd2 = NativeWindow.FromHandle(process2.MainWindowHandle);
if (wnd2 == null)
MessageBox.Show("Failed");
else
MessageBox.Show(wnd2.ToString(), "Yes");
I think this works because it is invoked from the same application. So, how can I access some another program's window object by its handle?
I thought it can work using C\C++ by using header file <windows.h> and then using a P\invoke.
If I can't, is there another way to access a window (i.e. rather than using handles)?
===================
EDIT
I want to deal with the entire window object and its own controls
Then, as Raymond suggested, why don't you try with Automation? Add a console project with references to UIAutomationClient and UIAutomationTypes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Automation;
using System.Diagnostics;
using System.Threading;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var pInfo = new ProcessStartInfo("notepad");
var p = Process.Start(pInfo);
p.WaitForInputIdle();
AutomationElement installerEditorForm = AutomationElement.FromHandle(p.MainWindowHandle);
// menus
AutomationElementCollection menuBars = installerEditorForm.FindAll(TreeScope.Children, new PropertyCondition(
AutomationElement.ControlTypeProperty, ControlType.MenuBar));
var mainMenuItem = menuBars[0];
AutomationElementCollection menus = mainMenuItem.FindAll(TreeScope.Children, new PropertyCondition(
AutomationElement.ControlTypeProperty, ControlType.MenuItem));
var fileMenuItem = menus[0];
ExpandCollapsePattern fileMenuItemOpenPattern = (ExpandCollapsePattern)fileMenuItem.GetCurrentPattern(
ExpandCollapsePattern.Pattern);
fileMenuItemOpenPattern.Expand();
AutomationElement fileMenuItemNew = fileMenuItem.FindFirst(TreeScope.Children,
new AndCondition(
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.MenuItem),
new PropertyCondition(AutomationElement.NameProperty, "New")));
Console.Read();
}
}
}
reference
The documentation for NativeWindow.FromHandle explains why that function always returns null for you:
The handle must already be owned by another NativeWindow in the
current process; otherwise, null is returned.
But the window that you are targeting is in a different process. So you simply cannot use NativeWindow here. You will have to make do with the window handle as an IntPtr.
In your edit you state:
I want to deal with the entire window object and its own controls
That changes nothing. You can't use NativeWindow. You will have to deal with the raw Win32 API.
What do you want to access? You can get the title and text of windows in Windows. But you cant get a NativeWindow object of another application. You need to use the windows API to interact with other applications. I once hijacked an object in another app, but did so by knowing its class and discovering a hack to find its Idispatch pointer, you can look into it here. Below is how to get the title of the foreground window, hope this helps.
using System.Runtime.InteropServices;
using System.Text;
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private string GetActiveWindowTitle()
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
Thought I might add, if you're trying to subclass a window of another application, you should take a look at my link above. I believe the only way to do it is using DLL injection and windows hooks, exemplified in my example here.
Didn't get what you're really trying to do but maybe if you try...
public class ApiUtils
{
[DllImport("user32")]
public static extern int SetForegroundWindow(IntPtr hwnd);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow);
[DllImport("user32.dll")]
public static extern int GetForegroundWindow();
public static void ActiveWindow(IntPtr hwnd)
{
if ((IntPtr)GetForegroundWindow() != hwnd)
{
ShowWindow(hwnd, ShowWindowCommand.ShowMaximized);
}
}
}
now calling it...
Process p = Process.Start(new ProcessStartInfo() { FileName = "someApp.exe"});
ApiUtils.ShowWindow(p.MainWindowHandle, WindowShowStyle.ShowNormal);
If not sorry, didn't get the question really well.

Categories

Resources