I'd like to invoke the user's screen saver if such is defined, in a Windows environment.
I know it can be done using pure C++ code (and then the wrapping in C# is pretty simple), as suggested here.
Still, for curiosity, I'd like to know if such task can be accomplished by purely managed code using the dot net framework (version 2.0 and above), without p/invoke and without visiting the C++ side (which, in turn, can use windows API pretty easily).
I've an idea, I'm not sure how consistently this would work, so you'd need to research a bit I think, but hopefully it's enough to get you started.
A screen saver is just an executable, and the registry stores the location of this executable in HKCU\Control Panel\Desktop\SCRNSAVE.EXE
On my copy of Vista, this worked for me:
RegistryKey screenSaverKey = Registry.CurrentUser.OpenSubKey(#"Control Panel\Desktop");
if (screenSaverKey != null)
{
string screenSaverFilePath = screenSaverKey.GetValue("SCRNSAVE.EXE", string.Empty).ToString();
if (!string.IsNullOrEmpty(screenSaverFilePath) && File.Exists(screenSaverFilePath))
{
Process screenSaverProcess = Process.Start(new ProcessStartInfo(screenSaverFilePath, "/s")); // "/s" for full-screen mode
screenSaverProcess.WaitForExit(); // Wait for the screensaver to be dismissed by the user
}
}
I think having a .Net library function that does this is highly unlikely - I'm not aware of any. A quick search returned this Code Project tutorial which contains an example of a managed wrapper which you mentioned in your question.
P/invoke exists so that you're able to access OS-specific features, of which screen savers are an example.
I'm not sure you can use completely managed code to do this.
This uses Windows API but is still very simple: Launch System Screensaver from C# Windows Form
Working on any version of windows...
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace HQ.Util.Unmanaged
{
public class ScreenSaverHelper
{
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
private static extern IntPtr GetDesktopWindow();
// Signatures for unmanaged calls
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SystemParametersInfo(int uAction, int uParam, ref int lpvParam, int flags);
// Constants
private const int SPI_GETSCREENSAVERACTIVE = 16;
private const int SPI_SETSCREENSAVERACTIVE = 17;
private const int SPI_GETSCREENSAVERTIMEOUT = 14;
private const int SPI_SETSCREENSAVERTIMEOUT = 15;
private const int SPI_GETSCREENSAVERRUNNING = 114;
private const int SPIF_SENDWININICHANGE = 2;
private const uint DESKTOP_WRITEOBJECTS = 0x0080;
private const uint DESKTOP_READOBJECTS = 0x0001;
private const int WM_CLOSE = 16;
public const uint WM_SYSCOMMAND = 0x112;
public const uint SC_SCREENSAVE = 0xF140;
public enum SpecialHandles
{
HWND_DESKTOP = 0x0,
HWND_BROADCAST = 0xFFFF
}
public static void TurnScreenSaver(bool turnOn = true)
{
// Does not work on Windows 7
// int nullVar = 0;
// SystemParametersInfo(SPI_SETSCREENSAVERACTIVE, 1, ref nullVar, SPIF_SENDWININICHANGE);
// Does not work on Windows 7, can't broadcast. Also not needed.
// SendMessage(new IntPtr((int) SpecialHandles.HWND_BROADCAST), WM_SYSCOMMAND, SC_SCREENSAVE, 0);
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, (IntPtr)SC_SCREENSAVE, (IntPtr)0);
}
}
}
Related
I have a C# console application that modifies an environment variable
RegistryKey key = Registry.CurrentUser.OpenSubKey("Environment", true);
key.SetValue("BMKTARGET", targetLocation.Path, RegistryValueKind.String);
The problem is that after running it in cmd, I have to close cmd and restart cmd to recognize the change.... since cmd needs to be told that it is modified, so I tried to run this code I found online:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SendMessageTimeout(
IntPtr hWnd,
int Msg,
int wParam,
string lParam,
int fuFlags,
int uTimeout,
out int lpdwResult
);
public const int HWND_BROADCAST = 0xffff;
public const int WM_SETTINGCHANGE = 0x001A;
public const int SMTO_NORMAL = 0x0000;
public const int SMTO_BLOCK = 0x0001;
public const int SMTO_ABORTIFHUNG = 0x0002;
public const int SMTO_NOTIMEOUTIFNOTHUNG = 0x0008;
//Run this function after modification
static void BroadcastEnvironment()
{
int result;
SendMessageTimeout((IntPtr)HWND_BROADCAST, WM_SETTINGCHANGE, 0, "Environment",
SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 5000, out result);
}
Somehow it still doesn't work, how can I fix this?
Note:
I'm trying to pass information to a .bat script that is running this exe... if there is a better way to do this, I'll be happy with that answer.
PS
someone in the comments made what looks to be a good suggestion, but without sufficient information to test it, If some one could elaborate that would be appreciated.
No need for all that registry mucking about.
Environment.SetEnvironmentVariable("BMKTARGET", targetLocation.Path,
EnvironmentVariableTarget.User);
and
var value = Environment.GetEnvironmentVariable("BMKTARGET",
EnvironmentVariableTarget.User)
additionally, this batch script posted by #grawity on SuperUser may be of some utility to you.
#eryksun says that instead of the script all you need is this one-liner for your batch file:
for /f "tokens=2*" %%a in ('reg query HKCU\Environment /v BMKTARGET') do set "BMKTARGET=%%b"
I want to start the run dialog (Windows+R) from Windows within my C# code.
I assume this can be done using explorer.exe but I'm not sure how.
Use RunFileDlg:
[DllImport("shell32.dll", EntryPoint = "#61", CharSet = CharSet.Unicode)]
public static extern int RunFileDlg(
[In] IntPtr hWnd,
[In] IntPtr icon,
[In] string path,
[In] string title,
[In] string prompt,
[In] uint flags);
private static void Main(string[] args)
{
// You might also want to add title, window handle...etc.
RunFileDlg(IntPtr.Zero, IntPtr.Zero, null, null, null, 0);
}
Possible values for flags:
RFF_NOBROWSE = 1; //Removes the browse button.
RFF_NODEFAULT = 2; // No default item selected.
RFF_CALCDIRECTORY = 4; // Calculates the working directory from the file name.
RFF_NOLABEL = 8; // Removes the edit box label.
RFF_NOSEPARATEMEM = 14; // Removes the Separate Memory Space check box (Windows NT only).
See also How to programmatically open Run c++?
The RunFileDlg API is unsupported and may be removed by Microsoft from future versions of Windows (I'll grant that MS's commitment to backwards compatibility and the fact that this API, though undocumented, appears to be fairly widely known makes this unlikely, but it's still a possibility).
The supported way to launch the run dialog is using the IShellDispatch::FileRun method.
In C#, you can access this method by going to Add Reference, select the COM tab, and select "Microsoft Shell Controls and Automation". After doing this you can launch the dialog as follows:
Shell32.Shell shell = new Shell32.Shell();
shell.FileRun();
Yes, the RunFileDlg API offers more customizability, but this has the advantage of being documented, supported, and therefore unlikely to break in the future.
Note that Shell32 must be run on an STA thread. If you get an exception in your code, add [STAThread] above your method declaration like this, for example:
[STAThread]
private static void OpenRun() {
//Shell32 code here
}
Any method calling a method that uses Shell32 should also be run on an STA thread.
Another method would be to emulate the Windows+R key combination.
using System.Runtime.InteropServices;
using System.Windows.Forms;
static class KeyboardSend
{
[DllImport("user32.dll")]
private static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
private const int KEYEVENTF_EXTENDEDKEY = 1;
private const int KEYEVENTF_KEYUP = 2;
public static void KeyDown(Keys vKey)
{
keybd_event((byte)vKey, 0, KEYEVENTF_EXTENDEDKEY, 0);
}
public static void KeyUp(Keys vKey)
{
keybd_event((byte)vKey, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
}
}
and call:
KeyboardSend.KeyDown(Keys.LWin);
KeyboardSend.KeyDown(Keys.R);
KeyboardSend.KeyUp(Keys.R);
KeyboardSend.KeyUp(Keys.LWin);
I am trying to find the COM port assigned to a USB device, through the registry using Silverlight, and have tried the following:
dynamic WshShell = AutomationFactory.CreateObject("WScript.Shell");
string strRegKeyUSB = #"HKLM\HARDWARE\DEVICEMAP\SERIALCOMM\\Device\USB_COM";
string strCOMValue = WshShell.RegRead(strRegKeyUSB);
This approach usually works 100%, but all Value names under the DEVICEMAP Key is "\Device\XXX"
This causes the the "Path" to not be found, as the "\\" between SERIALCOMM and Device is not seen as valid (Throws Error: "Cannot find File Specified")
This, as far as I can see, only really leaves me with one option - P/Invoke, in Silverlight 5
I am using P/Invoke already for a SerialWrapper Class, to Open, Read, Write the COM Ports, and would like to include only the minimal needed to only read this one Key Value from the Registry - I have tried following some examples I have found, but not being strong in Interop, P/Invoke, etc. I am struggling to find only the portions I need.
If someone could please just give me a basic example, to only accomplish this (I do NOT need to write to the registry, or read QWORDS, or anything else - Only read this string value from only this specific key)
I have tried following the following post (Marshal.PtrToStructure in Silverlight) and it's answer, in relation to this (http://www.pinvoke.net/default.aspx/winspool.enumports), but have not been able to get this working, Most likely form a lack of REALLY understanding ;-)
Here's a simple desktop application that reads a REG_SZ value. It's crude and simple. It will read the value that you want. You may have to adapt it to Silverlight. I cannot help you there!
I hope this is useful:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApplication1
{
internal static class NativeMethods
{
public const int ERROR_SUCCESS = 0;
public const uint HKEY_LOCAL_MACHINE = 0x80000002;
public const int KEY_READ = 0x20019;
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
public static extern int RegOpenKeyEx(
UIntPtr hKey,
string subKey,
int ulOptions,
int samDesired,
out UIntPtr hkResult
);
[DllImport("advapi32.dll")]
public static extern int RegCloseKey(
UIntPtr hKey
);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
public static extern int RegQueryValueEx(
UIntPtr hKey,
string lpValueName,
int lpReserved,
IntPtr type,
IntPtr lpData,
ref int lpcbData
);
}
internal static class RegistryWrapper
{
private static void checkErrorCode(int errorCode)
{
if (errorCode != NativeMethods.ERROR_SUCCESS)
throw new Win32Exception(errorCode);
}
public static string ReadRegString(UIntPtr rootKey, string subKey, string name)
{
UIntPtr hkey;
checkErrorCode(NativeMethods.RegOpenKeyEx(rootKey, subKey, 0, NativeMethods.KEY_READ, out hkey));
try
{
int cbData = 0;
checkErrorCode(NativeMethods.RegQueryValueEx(hkey, name, 0, IntPtr.Zero, IntPtr.Zero, ref cbData));
IntPtr ptr = Marshal.AllocHGlobal(cbData);
try
{
checkErrorCode(NativeMethods.RegQueryValueEx(hkey, name, 0, IntPtr.Zero, ptr, ref cbData));
return Marshal.PtrToStringUni(ptr, cbData / sizeof(char)).TrimEnd('\0');
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
finally
{
checkErrorCode(NativeMethods.RegCloseKey(hkey));
}
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(RegistryWrapper.ReadRegString((UIntPtr)NativeMethods.HKEY_LOCAL_MACHINE, #"HARDWARE\DEVICEMAP\SERIALCOMM", #"\Device\Serial0"));
}
}
}
Update
It seems that AllocHGlobal and FreeHGlobal are not available on Silverlight. You can p/invoke to LocalAlloc and LocalFree instead. Or you could use CoTaskMemAlloc and CoTaskMemFree. Here's what the former looks like:
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr LocalAlloc(uint uFlags, UIntPtr uBytes);
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr LocalFree(IntPtr hMem);
Define LMEM_FIXED like this:
const uint LMEM_FIXED = 0x0000;
Then replace the call to AllocHGlobal with this:
IntPtr ptr = LocalAlloc(LMEM_FIXED, cbData);
And replace the call to FreeHGlobal with this:
LocalFree(ptr);
A BIG thank you to #Dave Heffernan,
I got this to work FINALLY...
I Added the following code within the RegistryWrapper class in Dave's Answer:
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LocalAlloc(uint uFlags, int size);
internal static IntPtr AllocHGlobal(int size)
{
uint LPTR = (uint)0x0040;
IntPtr hGlobal = LocalAlloc(LPTR, size);
if (hGlobal == IntPtr.Zero)
{
throw new OutOfMemoryException("Unmanaged memory was not allocated.");
}
return hGlobal;
}
This works around the limitation of Marshal.AllocHGlobal not being available in Silverlight.
I then also just changed the reference to Marshal.AllocHGlobal to the local AllocHGlobal method above.
At work we make our own tablets. Some of the tablets have fingerprint biometrics, some don't. Sometimes a tech forgets to plug it in. I have yet to find a way to check if that device (or any for that matter) is present.
My first approach was to use the GUID for a biometric which is {53D29EF7-377C-4D14-864B-EB3A85769359}. I would search in the registry at hklm\system\currontcontrolset\control\class and check to see if that key is present.
That doesn't work because it seems that Windows 7 has that key present even if you've never had a biometric installed. It worked in XP, but I just tried again on a unit that used to have a biometric but I took it out and that key is still present.
The hardest part about this problem is that I have to work with Windows 7, 7 embedded, xp, and xp embedded.
Next idea was to use WMI, but I couldn't find the correct class to call to check if it is present.
I then found a biometric.dll but that only works in Windows 7.
Sometimes finding a common solution to a problem is not always easy. I'm doing this project in C# but iI'm willing to convert it to any language.
Any ideas on were I should start looking?
With the help of Joshua Drake who gave me an awesome link on how to solve my problem, those are my results:
The code that I am fixing to post is kind of specialized in that it looks for a specific GUID and only looks for the first one. I adapted it from the article about how to disable a device, although this code does not disable anything it merely checks for presence.
public static bool IsDevicePresent(string guid)
{
var info = IntPtr.Zero;
var NullGuid = new Guid(guid);
try
{
info = SetupDiGetClassDevsW(ref NullGuid,null,IntPtr.Zero,DIGCF_PRESENT);
CheckError("SetupDiGetClassDevs");
var devdata = new SP_DEVINFO_DATA();
devdata.cbSize = (UInt32)Marshal.SizeOf(devdata);
// Get first device matching device criterion.
SetupDiEnumDeviceInfo(info,0,out devdata);
// if no items match filter, throw
if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
CheckError("No device found matching filter.", 0xcffff);
CheckError("SetupDiEnumDeviceInfo");
}
catch
{
return false;
}
finally
{
if (info != IntPtr.Zero)
SetupDiDestroyDeviceInfoList(info);
}
return true;
}
private static void CheckError(string message, int lasterror = -1)
{
int code = lasterror == -1 ? Marshal.GetLastWin32Error() : lasterror;
if (code != 0)
throw new ApplicationException(String.Format("Error disabling hardware device (Code {0}): {1}",code, message));
}
[DllImport("setupapi.dll", SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevsW([In] ref Guid ClassGuid,[MarshalAs(UnmanagedType.LPWStr)]string Enumerator,IntPtr parent,UInt32 flags);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiDestroyDeviceInfoList(IntPtr handle);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet,UInt32 memberIndex,[Out] out SP_DEVINFO_DATA deviceInfoData);
//used to find device info from device manager
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
public UInt32 cbSize;
public Guid classGuid;
public UInt32 devInst;
public IntPtr reserved;
}
private const uint DIGCF_PRESENT = 2;
private const uint ERROR_INVALID_DATA = 13;
private const uint ERROR_NO_MORE_ITEMS = 259;
private const uint ERROR_ELEMENT_NOT_FOUND = 1168;
And here is a simple unit test to prove it works for first device
[Test]
public void TestDevicePresent()
{
var bluetoothClassGuid = "e0cbf06c-cd8b-4647-bb8a-263b43f0f974";
var biometricClassGuid = "53D29EF7-377C-4D14-864B-EB3A85769359";
var cdromdrivClassGiud = "4d36e965-e325-11ce-bfc1-08002be10318";
Assert.False(Native.IsDevicePresent(bluetoothClassGuid));
Assert.False(Native.IsDevicePresent(biometricClassGuid));
Assert.True(Native.IsDevicePresent(cdromdrivClassGiud));
}
I've been searching around, and I haven't found how I would do this from C#.
I was wanting to make it so I could tell Google Chrome to go Forward, Back, Open New Tab, Close Tab, Open New Window, and Close Window from my C# application.
I did something similar with WinAmp using
[DllImport("user32", EntryPoint = "SendMessageA")]
private static extern int SendMessage(int Hwnd, int wMsg, int wParam, int lParam);
and a a few others. But I don't know what message to send or how to find what window to pass it to, or anything.
So could someone show me how I would send those 6 commands to Chrome from C#? thanks
EDIT:
Ok, I'm getting voted down, so maybe I wasn't clear enough, or people are assuming I didn't try to figure this out on my own.
First off, I'm not very good with the whole DllImport stuff. I'm still learning how it all works.
I found how to do the same idea in winamp a few years ago, and I was looking at my code. I made it so I could skip a song, go back, play, pause, and stop winamp from my C# code. I started by importing:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow([MarshalAs(UnmanagedType.LPTStr)] string lpClassName, [MarshalAs(UnmanagedType.LPTStr)] string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int SendMessageA(IntPtr hwnd, int wMsg, int wParam, uint lParam);
[DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
public static extern int GetWindowText(IntPtr hwnd, string lpString, int cch);
[DllImport("user32", EntryPoint = "FindWindowExA")]
private static extern int FindWindowEx(int hWnd1, int hWnd2, string lpsz1, string lpsz2);
[DllImport("user32", EntryPoint = "SendMessageA")]
private static extern int SendMessage(int Hwnd, int wMsg, int wParam, int lParam);
Then the code I found to use this used these constants for the messages I send.
const int WM_COMMAND = 0x111;
const int WA_NOTHING = 0;
const int WA_PREVTRACK = 40044;
const int WA_PLAY = 40045;
const int WA_PAUSE = 40046;
const int WA_STOP = 40047;
const int WA_NEXTTRACK = 40048;
const int WA_VOLUMEUP = 40058;
const int WA_VOLUMEDOWN = 40059;
const int WINAMP_FFWD5S = 40060;
const int WINAMP_REW5S = 40061;
I would get the hwnd (the program to send the message to) by:
IntPtr hwnd = FindWindow(m_windowName, null);
then I would send a message to that program:
SendMessageA(hwnd, WM_COMMAND, WA_STOP, WA_NOTHING);
I assume that I would do something very similar to this for Google Chrome. but I don't know what some of those values should be, and I googled around trying to find the answer, but I couldn't, which is why I asked here. So my question is how do I get the values for:
m_windowName and WM_COMMAND
and then, the values for the different commands, forward, back, new tab, close tab, new window, close window?
Start your research at http://dev.chromium.org/developers
EDIT: Sending a message to a window is only half of the work. The window has to respond to that message and act accordingly. If that window doesn't know about a message or doesn't care at all you have no chance to control it by sending window messages.
You're looking at an implementation detail on how you remote controlled Winamp. Sending messages is just one way to do it and it's the way the Winamp developers chose. Those messages you're using are user defined messages that have a specific meaning only to Winamp.
What you have to do in the first step is to find out if Chromium supports some kind of remote controlling and what those mechanisms are.
You can get the window name easily using Visual Studio's Spy++ and pressing CTRL+F, then finding chrome. I tried it and got
"Chrome_VistaFrame" for the out window. The actual window with the webpage in is "Chrome_RenderWidgetHostHWND".
As far as WM_COMMAND goes - you'll need to experiment. You'll obviously want to send button clicks (WM_MOUSEDOWN of the top off my head). As the back,forward buttons aren't their own windows, you'll need to figure out how to do this with simulating a mouse click at a certain x,y position so chrome knows what you're doing. Or you could send the keyboard shortcut equivalent for back/forward and so on.
An example I wrote a while ago does this with trillian and winamp: sending messages to windows via c# and winapi
There's also tools out there to macro out this kind of thing already, using a scripting language - autoit is one I've used: autoit.com
Ok, here's what I've got so far... I kinda know what I need to do, but it's just a matter of doing it now...
Here's the window from Spy++, I locked onto the Chrome_RenderWidgetHostHWND and clicked the Back button on my keyboard. Here's what I got:
So here's my assumptions, and I've been playing with this forever now, I just can't figure out the values.
IntPtr hWnd = FindWindow("Chrome_RenderWidgetHostHWND", null);
SendMessage(hWnd, WM_KEYDOWN, VK_BROWSER_BACK, 0);
SendMessage(hWnd, WM_KEYUP, VK_BROWSER_BACK, 0);
Now, I just don't know what I should make the WM_KEYDOWN/UP values or the VK_BROWSER_BACK/FORWARD values...
I tried this:
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int VK_BROWSER_BACK = 0x6A;
const int VK_BROWSER_FORWARD = 0x69;
The latter two values I got from the image I just showed, the ScanCodes for those two keys. I don't know if I did it right though. The former two values I got after searching google for the WM_KEYDOWN value, and someone used &H100 and &H101 for the two values. I've tried several other random ideas I've seen floating around. I just can't figure this out.
Oh, and here's the SendMessage method
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, uint lParam);
This is a great site for interop constants:
pinvoke
Another way of finding the values is to search koders.com, using C# as the language, for WM_KEYDOWN or the constant you're after:
Koders.com search
&H values look like that's from VB(6). pinvoke and koders both return results for VK_BROWSER_FORWARD,
private const UInt32 WM_KEYDOWN = 0x0100;
private const UInt32 WM_KEYUP = 0x0101;
public const ushort VK_BROWSER_BACK = 0xA6;
public const ushort VK_BROWSER_FORWARD = 0xA7;
public const ushort VK_BROWSER_REFRESH = 0xA8;
public const ushort VK_BROWSER_STOP = 0xA9;
public const ushort VK_BROWSER_SEARCH = 0xAA;
public const ushort VK_BROWSER_FAVORITES = 0xAB;
public const ushort VK_BROWSER_HOME = 0xAC;
(It's funny how many wrong defintions of VK constants are floating about, considering VK_* are 1 byte 0-255 values, and people have made them uints).
Looks slightly different from your consts. I think the function you're after is SendInput (but I haven't tried it) as it's a virtual key.
[DllImport("User32.dll")]
private static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] KEYBOARD_INPUT[] input, int structSize);
Explanation about the parameters:
Parameters
nInputs- Number of structures in the pInputs array.
pInputs - Pointer to an array of INPUT structures. Each structure represents an event to be inserted into the keyboard or mouse input stream.
cbSize - Specifies the size, in bytes, of an INPUT structure. If cbSize is not the size of an INPUT structure, the function fails.
This needs a KEYBOARD_INPUT type:
[StructLayout(LayoutKind.Sequential)]
public struct KEYBOARD_INPUT
{
public uint type;
public ushort vk;
public ushort scanCode;
public uint flags;
public uint time;
public uint extrainfo;
public uint padding1;
public uint padding2;
}
And finally a sample, which I haven't tested if it works:
/*
typedef struct tagKEYBDINPUT {
WORD wVk;
WORD wScan;
DWORD dwFlags;
DWORD time;
ULONG_PTR dwExtraInfo;
} KEYBDINPUT, *PKEYBDINPUT;
*/
public static void sendKey(int scanCode, bool press)
{
KEYBOARD_INPUT[] input = new KEYBOARD_INPUT[1];
input[0] = new KEYBOARD_INPUT();
input[0].type = INPUT_KEYBOARD;
input[0].vk = VK_BROWSER_BACK;
uint result = SendInput(1, input, Marshal.SizeOf(input[0]));
}
Also you'll need to focus the Chrome window using SetForegroundWindow