SendKeys with C# PostMessage - Underscore - c#

I'm trying to send an underscore with PostMessage but the best I can get is -, can anyone help here I cannot find the answer anywhere.
I send a string to a character loop that gets each one and uses PostMessage to send the key, which works fine for almost everything but I cannot figure out _.
public bool SendKeys(string message)
{
var success = false;
uint wparam = 0 << 29 | 0;
foreach (var child in GetChildWindows(Window.Windowhandle))
{
var sb = new StringBuilder(100);
GetClassName(child, sb, sb.Capacity);
//(IntPtr)Keys.H
if (sb.ToString() != Window.TargetWindow) continue;
foreach (var c in message)
{
var k = ConvertFromString(c.ToString());
if (String.Equals(c.ToString(), c.ToString().ToUpper(), StringComparison.Ordinal))
{
PostMessage(child, WM_CHAR, (IntPtr) k, (IntPtr) wparam);
}
else
{
PostMessage(child, WM_KEYDOWN, (IntPtr)k, (IntPtr)wparam);
}
success = true;
Thread.Sleep(200);
}
}
return success;
}
ConvertFromString is just (Keys)Enum.Parse(typeof (Keys), keystr); really, getting the key from System.Form.Keys
Can anyone identify how to send an underscore??

ConvertFromString is just (Keys)Enum.Parse(typeof (Keys), keystr);
That is the fundamental flaw, there is no Keys enumeration value that represent an underscore. Keys are virtual keys, they are the same anywhere in the world. But the characters they produce are not the same, it depends the modifier keys and the active keyboard layout that the user selected.
An American user must first press the Shift key, then press Keys.OemMinus. A German user must press Shift, then Keys.OemQuestion. A French user only presses Keys.D8. A Spanish user presses AltGr, then Keys.D7. Etcetera.
You'll need to get ahead by dropping your dependency on Keys if you want a predictable result. Use WM_CHAR and pass the actual character you want.

Related

The description for Event ID 'X' in Source 'Microsoft-Windows-Kernel-Power' cannot be found

I am trying to read the System Event Logs in C# .NET 3.5 with the following method EventLog.GetEventLogs. This seems to be working perfectly for all kinds of Event Logs that I want to take a look at. There is but one exception: Microsoft-Windows-Kernel-Power Event Logs which can be read but produces the following Message:
Microsoft-Windows-Kernel-Power The description for Event ID 'X' in
Source 'Microsoft-Windows-Kernel-Power' cannot be found. The local
computer may not have the necessary registry information or message
DLL files to display the message, or you may not have permission to
access them. The following information is part of the event:'Y', 'Z'
instead of the correct Message displayed in the Windows Event Viewer.
Code looks like this
var myEventLogs = new List<myModels.EventLogEntry>();
foreach (var eventLog in EventLog.GetEventLogs())
{
foreach (var entry in eventLog.Entries)
{
if (entry.Source.IndexOf("kernel-power", StringComparison.OrdinalIgnoreCase) == -1 &&
entry.Message.IndexOf("kernel-power", StringComparison.OrdinalIgnoreCase) == -1)
continue;
myEventLogs.Add(new myModels.EventLogEntry(entry.Source, entry.Message))
}
}
This is happening even if I run the application as administrator. I am at a loss here. I have searched far and wide all over the internet and found a couple posts that seem to have similar issues but most are for writing Event Logs instead of having problems reading them. Is anyone familiar with that kind of problem or can point me in the right direction? All my registry keys etc seem to be set up correctly (and it is also showing the same results on a different PC).
EDIT: Windows Version 10.0.18363 Build 18363 but it is happening on multiple PCs (I am not sure what Windows version the others are using). In fact, I have not found a single one which is working (tested 5 so far).
I couldn't find any specification neither, but I got a hack which shows plausible messages here. The issue looks like either a bug in EventLogEntry from CLR or a non-uniform message handling of kernel-power events.
I reproduced the issue, and what was happening under the hood on my machine:
E.g. there is a 109 (0x6D) event in the event log:
EventLogEntry gets a path to a dll with string resources from HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Application\Microsoft-Windows-Kernel-Power\EventMessageFile (in CLR sources of EventLogEntry as here and here)
Dll path on my machine is: %systemroot%\system32\microsoft-windows-kernel-power-events.dll, it's successfully found and loaded, but there is no 0x6D string id inside. However there is a string with the same text but with 0x02 00 00 00 prefix in its id (string resources from native dll were enumerated using this article)
ID 0x0200006d (33554541) Language: 0409
The kernel power manager has initiated a shutdown transition.
So if set 0x02 00 00 00 bit manually in event id, it might produce meaningful messages. Code below does this, but again this is an ugly hack, it should not be used in real life software, as it's based solely on assumptions and not tested in all cases, and it manipulates private state of EventLogEntry:
var myEventLogs = new List<myModels.EventLogEntry>();
foreach (var eventLog in EventLog.GetEventLogs())
{
foreach (EventLogEntry entry in eventLog.Entries)
{
if (entry.Source.IndexOf("kernel-power", StringComparison.OrdinalIgnoreCase) == -1 &&
entry.Message.IndexOf("kernel-power", StringComparison.OrdinalIgnoreCase) == -1)
continue;
var dataBuf = entry.GetPrivateField<byte[]>("dataBuf");
var bufOffset = entry.GetPrivateField<int>("bufOffset");
byte previousMagicByte = dataBuf[bufOffset + EVENTID + 3];
try
{
dataBuf[bufOffset + EVENTID + 3] |= 0x02; //as strings in microsoft-windows-kernel-power-events.dll have 0x02****** ids
myEventLogs.Add(new myModels.EventLogEntry(entry.Source, entry.Message))
}
finally
{
dataBuf[bufOffset + EVENTID + 3] = previousMagicByte;
}
}
}
...
internal const int EVENTID = 20;
public static T GetPrivateField<T>(this object obj, string fieldName)
{
if (fieldName == null)
throw new ArgumentNullException(nameof(fieldName));
var fieldInfo = obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo == null)
throw new ArgumentException($"Type {obj.GetType().FullName} doesn't have {fieldName} private instance field");
object result = fieldInfo.GetValue(obj);
return (T)result;
}

How can I integrate complexity verification while using a PasswordBox?

I have an application that needs to have the user create a passphrase. As such, there are various rules that need to be tested against the passphrase as it's being entered.
These rules are pretty typical-- the value must be a certain length, have one upper, one lower, include some special characters, etc.
However, since this is a WPF application using a PasswordBox control, and the resulting value is very sensitive, how can I setup a complexity comparison system that dynamically checks the passphrase as the user is typing?
To be clear, I have a list of all requirements in a text label below the passphrase creation PasswordBox element. As the user types, a validator shows which requirements are met and which ones are still needed. I also have an entropy calculator which give a typical "OK, Good, Strong, Very Strong" indicator once requirements are met. This is why I need to find a way to securely validate the value as the user is typing.
How can I accomplish this without emitting insecure .Net strings?
You can subscribe to PasswordChanged but don't use Password property if you care about securely storing your sensetive value. Instead, do this:
private void OnPasswordChanged(object sender, RoutedEventArgs e) {
using (var pwd = ((PasswordBox) sender).SecurePassword) {
int length = pwd.Length;
if (length == 0) {
// string empty, do something
return;
}
bool hasSpecial = false;
bool hasUpper = false;
bool hasLower = false;
bool hasDigit = false;
// etc
// allocate unmanaged memory and copy string there
IntPtr ptr = Marshal.SecureStringToBSTR(pwd);
try {
// each char in that string is 2 bytes, not one (it's UTF-16 string)
for (int i = 0; i < length * 2; i += 2) {
// so use ReadInt16 and convert resulting "short" to char
var ch = Convert.ToChar(Marshal.ReadInt16(ptr + i));
// run your checks
hasSpecial |= IsSpecialChar(ch);
hasUpper |= Char.IsUpper(ch);
hasLower |= Char.IsLower(ch);
hasDigit |= Char.IsDigit(ch);
}
}
finally {
// don't forget to zero memory to remove password from it
Marshal.ZeroFreeBSTR(ptr);
}
}
}
That way you never build .NET string during your validation, and every trace of password is cleared from memory when you finish.

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;
}

Get Safe sender list in Outlook 2007 C# Add in

I have created an Outlook 2007 add-in in C#.NET 4.0.
I want to read the safe sender list in my C# code.
if (oBoxItem is Outlook.MailItem)
{
Outlook.MailItem miEmail = (Outlook.MailItem)oBoxItem;
OlDefaultFolders f = Outlook.OlDefaultFolders.olFolderContacts;
if (miEmail != null)
{
string body = miEmail.Body;
double score = spamFilterObject.CalculateSpamScore(body);
if (score <= 0.9)
{
miEmail.Move(mfJunkEmail);
}
}
}
So, the above code moves all email to spam, even though they are present in the safe sender list. Thus I want to get the safe sender list so that I can avoid this spam checking.
Could anybody please help me on this?
The Outlook object model doesn't expose these lists (for more or less obvious reasons). The safe sender list can be read straight from the registry at:
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\[PROFILE NAME]\0a0d020000000000c000000000000046\001f0418
This binary registry key contains double-byte characters, separated by a semicolon (;).
The MAPI property mapping onto this registry key is
PR_SPAM_TRUSTED_SENDERS_W, documented here.
Chavan, I assume since this hasn't been updated in over 4 years, you don't need any more information, but this question and the answer helped me find what I was looking for (it was very hard to find) and enabled me to write the code below that may help if you're still looking for an answer.
This code runs in LINQPad, so if you aren't a LINQPad user, remove the .Dump() methods and replace with Console.WriteLine or Debug.WriteLine.
Cheers!
const string valueNameBlocked = "001f0426";
const string valueNameSafe = "001f0418";
// Note: I'm using Office 2013 (15.0) and my profile name is "Outlook"
// You may need to replace the 15.0 or the "Outlook" at the end of your string as needed.
string keyPath = #"Software\Microsoft\Office\15.0\Outlook\Profiles\Outlook";
string subKey = null;
var emptyBytes = new byte[] { };
var semi = new[] { ';' };
string blocked = null, safe = null;
// I found that my subkey under the profile was not the same on different machines,
// so I wrote this block to look for it.
using (var key = Registry.CurrentUser.OpenSubKey(keyPath))
{
var match =
// Get the subkeys and all of their value names
key.GetSubKeyNames().SelectMany(sk =>
{
using (var subkey = key.OpenSubKey(sk))
return subkey.GetValueNames().Select(valueName => new { subkey = sk, valueName });
})
// But only the one that matches Blocked Senders
.FirstOrDefault(sk => valueNameBlocked == sk.valueName);
// If we got one, get the data from the values
if (match != null)
{
// Simultaneously setting subKey string for later while opening the registry key
using (var subkey = key.OpenSubKey(subKey = match.subkey))
{
blocked = Encoding.Unicode.GetString((byte[])subkey.GetValue(valueNameBlocked, emptyBytes));
safe = Encoding.Unicode.GetString((byte[])subkey.GetValue(valueNameSafe, emptyBytes));
}
}
}
// Remove empty items and the null-terminator (sometimes there is one, but not always)
Func<string, List<string>> cleanList = s => s.Split(semi, StringSplitOptions.RemoveEmptyEntries).Where(e => e != "\0").ToList();
// Convert strings to lists (dictionaries might be preferred)
var blockedList = cleanList(blocked).Dump("Blocked Senders");
var safeList = cleanList(safe).Dump("Safe Senders");
byte[] bytes;
// To convert a modified list back to a string for saving:
blocked = string.Join(";", blockedList) + ";\0";
bytes = Encoding.Unicode.GetBytes(blocked);
// Write to the registry
using (var key = Registry.CurrentUser.OpenSubKey(keyPath + '\\' + subKey, true))
key.SetValue(valueNameBlocked, bytes, RegistryValueKind.Binary);
// In LINQPad, this is what I used to view my binary data
string.Join("", bytes.Select(b => b.ToString("x2"))).Dump("Blocked Senders: binary data");
safe = string.Join(";", safeList) + ";\0"; bytes = Encoding.Unicode.GetBytes(safe);
string.Join("", bytes.Select(b => b.ToString("x2"))).Dump("Safe Senders: binary data");
PST and IMAP4 (ost) stores keep the list in the profile section in the registry. Profile section guid is {00020D0A-0000-0000-C000-000000000046}. To access the data directly, you will need to know the Outlook version and the profile name.
Exchange store keeps this data as a part of the server side rule that processes incoming messages on the server side. You can see the rule data in OutlookSpy (I am its author) - go to the Inbox folder, "Associated Contents" tab, find the entry named (PR_RuleMsgName) == "Junk E-mail Rule", double click on it, take a look at the PR_EXTENDED_RULE_CONDITION property.
Outlook Object Model does not expose Junk mail settings. If using Redemption (I am also its author) is an option, it exposes the RDOJunkEmailOptions.TrustedSenders collection (works both for the PST and Exchange stores):
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set Store = Session.Stores.DefaultStore
set TrustedSenders = Store.JunkEmailOptions.TrustedSenders
for each v in TrustedSenders
debug.print v
next

Simulating Keyboard with SendInput API in DirectInput applications

I'm trying to simulate keyboard commands for a custom game controller application. Because I'll need to simulate commands in a DirectInput environment most of the usual methods don't work. I know that using a hook would work 100% but I'm trying to find an easier implementation.
I've done quite a bit of searching and found that by using the SendInput API with Scancodes instead of virtual keys should work, but it seems to behave like the key's are "sticking". I've sent both the KEYDOWN and KEYUP events but when I attempt to send the message in a DirectInput environment, the game acts as if the key is being held down.
For instance, if I simulate a "W" keypress and have that key mapped in a First Person Shooter to the "move forward" action, once I'm in game, the function below will cause the character to move forward. However, just by issuing the command once, it will move the character forward indefinitely.
Here is a code snippet (in C#) for the SendInput function I'm calling:
[DllImport("user32.dll")]
static extern UInt32 SendInput(UInt32 nInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] INPUT[] pInputs, Int32 cbSize);
public static void Test_KeyDown()
{
INPUT[] InputData = new INPUT[2];
Key ScanCode = Microsoft.DirectX.DirectInput.Key.W;
InputData[0].type = 1; //INPUT_KEYBOARD
InputData[0].wScan = (ushort)ScanCode;
InputData[0].dwFlags = (uint)SendInputFlags.KEYEVENTF_SCANCODE;
InputData[1].type = 1; //INPUT_KEYBOARD
InputData[1].wScan = (ushort)ScanCode;
InputData[1].dwFlags = (uint)(SendInputFlags.KEYEVENTF_KEYUP | SendInputFlags.KEYEVENTF_UNICODE);
// send keydown
if (SendInput(2, InputData, Marshal.SizeOf(InputData[1])) == 0)
{
System.Diagnostics.Debug.WriteLine("SendInput failed with code: " +
Marshal.GetLastWin32Error().ToString());
}
}
I'm not sure if this method is a lost cause, or if there is just something silly I'm missing. I hate to over complicate my code if I don't have to by using hooks, but this is also new territory for me.
Any help anyone can give is much appreciated.
Thanks!
I've found the solution to my own problem. So, I thought I'd post here to help anyone who may have similar issues in the future.
The keyup command was not working properly because when only sending the scan code, the keyup must be OR'ed with the scan code flag (effectively enabling both flags) to tell the SendInput() API that this is a both a KEYUP and a SCANCODE command.
For instance, the following code will properly issue a scan code key-up:
INPUT[] InputData = new INPUT[1];
InputData[0].Type = (UInt32)InputType.KEYBOARD;
//InputData[0].Vk = (ushort)DirectInputKeyScanCode; //Virtual key is ignored when sending scan code
InputData[0].Scan = (ushort)DirectInputKeyScanCode;
InputData[0].Flags = (uint)KeyboardFlag.KEYUP | (uint)KeyboardFlag.SCANCODE;
InputData[0].Time = 0;
InputData[0].ExtraInfo = IntPtr.Zero;
// Send Keyup flag "OR"ed with Scancode flag for keyup to work properly
SendInput(1, InputData, Marshal.SizeOf(typeof(INPUT)))
Thanks to Hans for the response. I did some investigating and sending two messages back to back like the original example does indeed simulate a "keypress" but it's very fast. It would not work well for a movement command, but would be ideal when action keys are to be "tapped" and not held down.
Also, the virtual key field is ignored when sending a scan code. MSDN had the following to say on the subject:
"Set the KEYEVENTF_SCANCODE flag to define keyboard input in terms of the scan code. This is useful to simulate a physical keystroke regardless of which keyboard is currently being used. The virtual key value of a key may alter depending on the current keyboard layout or what other keys were pressed, but the scan code will always be the same."
http://msdn.microsoft.com/en-us/library/ms646271%28v=VS.85%29.aspx
I was trying to simulate keyboards for a presentation in Flash, and was also having the same problem. It did work for apps like Notepad, but not for flash. After hours of googling I finally made it work:
public static void GenerateKey(int vk, bool bExtended)
{
INPUT[] inputs = new INPUT[1];
inputs[0].type = INPUT_KEYBOARD;
KEYBDINPUT kb = new KEYBDINPUT(); //{0};
// generate down
if ( bExtended )
kb.dwFlags = KEYEVENTF_EXTENDEDKEY;
kb.wVk = (ushort)vk;
inputs[0].ki = kb;
SendInput(1, inputs, System.Runtime.InteropServices.Marshal.SizeOf(inputs[0]));
// generate up
//ZeroMemory(&kb, sizeof(KEYBDINPUT));
//ZeroMemory(&inputs,sizeof(inputs));
kb.dwFlags = KEYEVENTF_KEYUP;
if ( bExtended )
kb.dwFlags |= KEYEVENTF_EXTENDEDKEY;
kb.wVk = (ushort)vk;
inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki = kb;
SendInput(1, inputs, System.Runtime.InteropServices.Marshal.SizeOf(inputs[0]));
}

Categories

Resources