How to implement Global Hotkeys in C#? - c#

I need to write an application which globally intercepts Alt+Shift+S.
What I did is I created a DLL which sets global hooks:
namespace Hotkeydll
{
public class MyHotKey
{
public static void setHooks()
{
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
}
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
//write something into file
}
}
}
Then I created a program which loads this DLL and set the hook:
using Hotkeydll;
namespace IWFHotkeyStarter
{
class Program
{
static void Main(string[] args)
{
MyHotKey.setHooks();
}
}
}
Now the problem is that the hotkey doesn't work.
It looks like the DLL is not loaded permanently into memory. I see that I can delete the dll file from file system.
So please advise what I am doing wrong?
Should I use a different approach?
Thank you.

Your Main() method sets the hooks, then immediately exits and terminates the program. Furthermore, you need a message loop to make the hook callback work. That requires a Windows Forms or WPF app. Using a real hot key instead of a hook now also becomes an option. Check this thread for an example, C# is further down the page.

Keyboard hooks are usually not the right way to get global hotkeys.
Use RegisterHotkey whenever possible.

Related

What's the point of OpenClipboard API?

After reading quite a bit on the clipboard being blocked when trying to use it, I tried using OpenClipboard() directly, to capture the clipboard and be able to use it from my window.
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
[DllImport("user32.dll", SetLastError=true)]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll", SetLastError=true)]
private static extern bool CloseClipboard();
private int idx = 0;
private void refresh_Tick(object sender, EventArgs e) {
switch (idx++) {
case 0:
OpenClipboard(Handle);
break;
default:
Clipboard.SetText(" ");
break;
}
}
}
When using SetText, I will get the infamous error:
A first chance exception of type 'System.Runtime.InteropServices.ExternalException' occurred in System.Windows.Forms.dll
Additional information: Requested Clipboard operation did not succeed.
So, the questions:
Does OpenClipboard() actually work?
What is the argument for OpenClipboard() for? The rest of the API does not need a handle to any window, so why does OpenClipboard()? In other words, the Clipboard is supposed to be shared between PROCESSES, not WINDOWS - but I don't see a way to lock it for my current process.
I can call OpenClipboard(IntPtr.Zero), which MSDN says:
If this parameter is NULL, the open clipboard is associated with the current task.
What is 'task' supposed to mean?
Does OpenClipboard() actually work?
OpenClipboard() works, but definitely not in the way you showed. It is a raw Win32 API. Here is an example of using it to copy string to clipboard (C/C++).
What is the argument for OpenClipboard() for?
To be honest, I'm not sure about this. Usually we just pass NULL aka IntPtr.Zero and let it associate with the current task. I guess it retrieves the thread that created the specified window, and then associates it with the clipboard.
What is task? Well here is my understanding. Back in Win16 days, instead of 'Thread' or 'Process', they used the terminology 'Task'. There was a function to GetCurrentTask().
Then the function is replaced by GetCurrentProcess() and GetCurrentThread().
Since it makes more sense that only one thread can access a shared resource at one time, I would say that 'Task' actually means 'Thread'.
The rest of the API does not need a handle to any window, so why does OpenClipboard()?
If my assumption above is correct, that is, only one thread can access Clipboard at a time, then chances are, Windows will use the thread/window you specified with OpenClipboard() previously, until you call CloseClipboard() or the thread/window becomes invalid. That's why you don't need to specify the HWND all the time.
In other words, the Clipboard is supposed to be shared between PROCESSES, not WINDOWS - but I don't see a way to lock it for my current process.
It locks, though I'm not sure about C# P/Invoke. If you have access to Win32/C development toolkit, compile and run the following code:
#include <conio.h>
#include <Windows.h>
int main()
{
OpenClipboard(GetForegroundWindow());
_getch();
return 0;
}
And see what happen if you use Clipboard in other program. :) Close the console window to restore.

C# form from DLL loaded by native C++

This a question arising from this thread: Native C++ use C# dll via proxy C++ managed dll
In a nutshell, I'm loading (my) C# extension into a native process via a DLL. The extension needs to show a form so that the user can control it. I'm using standard .NET forms, no 3rd party librarys or anything, and my form is not showing up. Worse yet, it hangs the target process. It's not using any CPU, so I get the feeling its waiting for some function to return, but never does.
Also of interest is that the "Initialize method" message box pops up, but not the "Test" message box. I've tested everything I can think of (STAthread, threads, DisableThreadLibraryCalls, plus different code locations), every way to Sunday. I'm inclined to think it's some obscure detail of Win32 interop, but I can't find anything that would seem to cause these symptoms.
Can one of you experts take a look at my code and point out what the issue is?
/// <summary>
/// Provides entry points for native code
/// </summary>
internal static class UnmanagedExports
{
[UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
public delegate int SendRecv([MarshalAs(UnmanagedType.SafeArray)]byte[] ByteArray, UInt64 Len);
[STAThread]
[DllExport("Initialize", CallingConvention.StdCall)]
public static int Initialize(IntPtr hInstance, SendRecv Send, SendRecv Recv)
{
return DLLinterface.Initialize(hInstance, Send, Recv);
}
[DllExport("Terminate", CallingConvention.StdCall)]
public static void Terminate()
{
DLLinterface.Terminate();
}
}
internal class DLLinterface
{
static System.Threading.Thread uiThread;
[STAThread]
internal static int Initialize(IntPtr hInstance, UnmanagedExports.SendRecv Send, UnmanagedExports.SendRecv Recv)
{
MessageBox.Show("Initialize method");
try
{
uiThread = new System.Threading.Thread(Run);
uiThread.Start();
}
catch (Exception ex)
{
MessageBox.Show("Failed to load: " + ex.Message, "Infralissa error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return 1;
}
[STAThread]
private static void Run()
{
MessageBox.Show("Test");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
internal static void Terminate()
{
MessageBox.Show("Terminating.");
if (uiThread.IsAlive)
uiThread.Abort();
}
}
It seems that the target itself was at fault. It wasn't loading the extensions directly, but instead loading a native "exensionManager.dll", where, as luck would have it, they were using DllMain to load up MY extension. In other words, I was trying to load a form under loaderlock and ran into a deadlock as. NET tried to load other assemblies.
The answer was simple, I had to show the form on a new thread. However, .NET's threading was also hanging, as it, too, required a library load which deadlocked.
In the end, I had to resort to a vanilla P/Invoke to CreateThread() directly, but the form is finally showing up.

Check if a DLL is present in the system

quick question. I want to find out if a DLL is present in the system where my application is executing.
Is this possible in C#? (in a way that would work on ALL Windows OS?)
For DLL i mean a non-.NET classic dll (a Win32 dll)
(Basically I want to make a check cause I'm using a DLL that may or may not be present on the user system, but I don't want the app to crash without warning when this is not present :P)
Call the LoadLibrary API function:
[DllImport("kernel32", SetLastError=true)]
static extern IntPtr LoadLibrary(string lpFileName);
static bool CheckLibrary(string fileName) {
return LoadLibrary(fileName) == IntPtr.Zero;
}
When using platform invoke calls in .NET, you could use the Marshal.PrelinkAll(Type) method:
Setup tasks provide early initialization and are performed
automatically when the target method is invoked. First-time tasks
include the following:
Verifying that the platform invoke metadata is correctly formatted.
Verifying that all the managed types are valid parameters of platform
invoke functions.
Locating and loading the unmanaged DLL into the process.
Locating the entry point in the process.
As you can see, it performs additional checks other than if the dll exists, like locating the entry points (e.g if SomeMethod() and SomeMethod2() actually exist in the process like in the following code).
using System.Runtime.InteropServices;
public class MY_PINVOKES
{
[DllImport("some.dll")]
private static void SomeMethod();
[DllImport("some.dll")]
private static void SomeMethod2();
}
Then use a try/catch strategy to perform your check:
try
{
// MY_PINVOKES class where P/Invokes are
Marshal.PrelinkAll( typeof( MY_PINVOKES) );
}
catch
{
// Handle error, DLL or Method may not exist
}
Actually it does not throw FileNotFoundException.
Also for that one needs to check in multiple places for path, for the LoadLibrary
There is a standard exception in .net the is derived from TypeLoadException, that is DllNotFoundException.
Best way is to wrap a method/PInvoke call in try..catch and handle the DllNotFoundException since .net will check for application path as well as any other paths set as part of PATH OS Environment variable.
[DllImport("some.dll")]
private static void SomeMethod();
public static void SomeMethodWrapper() {
try {
SomeMethod();
} catch (DllNotFoundException) {
// Handle your logic here
}
}
Call LoadLibrary.
http://msdn.microsoft.com/en-us/library/ms684175(VS.85).aspx
I'm assuming this is a PInvoke call?
If so the easiest way to make this determine if it's present is to make the call and catch the exception that results if the file does not exist.
[DllImport("some.dll")]
private static void SomeMethod();
public static void SomeMethodWrapper() {
try {
SomeMethod();
} catch (DllNotFoundException) {
// Do Nothing
}
}

How do I get the handle of a console application's window

Can someone tell me how to get the handle of a Windows console application in C#? In a Windows Forms application, I would normally try this.Handle.
Not sure it works, but you can try that :
IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
The aforementioned Process.MainWindowHandle method only works for the process that started the console
The FindWindowByCaption method is inherently unreliable
Here's a robust way to do this:
The related functions from the Console Win32 API are:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)]
static extern bool FreeConsole();
For the console the current process is attached to, just GetConsoleWindow() is enough
For the console another process is attached to, attach to it as well with AttachConsole, call GetConsoleWindow, them immediately detach with FreeConsole.
For the extra cautious, register a console event handler before attaching (and unregister it after detaching) as well so you don't accidentally get terminated if a console event happens in the tiny time frame you're attached to the console:
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine,
bool Add);
delegate Boolean ConsoleCtrlDelegate(CtrlTypes CtrlType);
enum CtrlTypes : uint {
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
bool is_attached=false;
ConsoleCtrlDelegate ConsoleCtrlDelegateDetach = delegate(CtrlType) {
if (is_attached = !FreeConsole())
Trace.Error('FreeConsole on ' + CtrlType + ': ' + new Win32Exception());
return true;
};
Making changes to the current process just to read something is rather ugly (when this is a console process, this gets really ugly since it requires a helper process to avoid terminating the current console). Nevertheless, further investigation shows that there's no other way short of injecting into the csrss process or the target process.
Console correspondence information is located in and managed by csrss.exe (or a multitude of those, one for each session, since Vista), so it can't be retrieved with the likes of ReadProcessMemory. All that csrss exposes is the CSRSS LPC API. There's only one relevant function in the full API list, SrvGetConsoleWindow. And it doesn't accept a PID but determines that of the calling party as seen in an alternative implementation or the function's disassembly in winsrv.dll.
Try this:
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
public static extern IntPtr FindWindowByCaption(IntPtr zeroOnly, string lpWindowName);
…
Console.Title = "Test";
…
IntPtr handle = FindWindowByCaption(IntPtr.Zero, Console.Title);
I've just solved this problem for myself (unfortunately before seeing Thomas's answer which is much quicker). Well, here's another way for those who are not satisfied with his answer. I'm writing this answer because I want to provide another answer + a better way to design the Program class if you're treating your console as a Window. Let's begin with that design:
I've kind of changed the default style of the Program class. I've actually made it into a class that has a program in it, and not just one method which represent it and uses other classes for content. (If you don't know what I mean, not important).
The reason I had to do it is because I wanted to write the following event handler:
private void CatchUnhandled(Object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject as Exception;
MessageBox.Show(this, exception.Message, "Error"); // Check out 1st arg.
}
It overloads this method MessageBox.Show(IWin32Window, String, String).
Because Console doesn't implement IWin32Window, I had to implement it myself, of course, in order to just call this in the 1st argument.
Here is the implementation of it and everything else:
Note: this code is copy-pasted from my application, you can feel free to change the access modifiers
Program Class Declaration:
internal class Program : IWin32Window
{
...
}
IWin32Window Implementation:
public IntPtr Handle
{
get { return NativeMethods.GetConsoleWindow(); }
}
It uses the following class:
internal static class NativeMethods
{
[DllImport("kernel32.dll")]
internal static extern IntPtr GetConsoleWindow();
}
Now, the problem is that you can't actually call this in Main, being a static method, so whatever was in Main I've moved to a new method named Start and all Main is doing is creating a new Program and calling Start.
private static void Main()
{
new Program().Start();
}
private void Start()
{
AppDomain.CurrentDomain.UnhandledException += CatchUnhandled;
throw new Exception();
}
The result was, of course, a message-box with my console's window as an owner.
Using this method for a message-box, is of course only one application of this method.
I don't think there is such a thing. The console window is not accessible to the application. You MIGHT try to iterate the process list looking for your own process name. The Process class IIRC contains a property for the program's main window handle, which might be the console window for console applications - which I'm not sure of.
One more version in VB.Net on how to show MessageBox on top of console window
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Friend Module NativeMethods
<DllImport("kernel32.dll")> Friend Function GetConsoleWindow() As IntPtr
End Function
End Module
NotInheritable Class WndProxy
Implements IWin32Window
ReadOnly Property Handle As IntPtr Implements IWin32Window.Handle
Sub New(_hwnd As IntPtr)
Handle = _hwnd
End Sub
End Class
Module Module1
Sub Main()
' using MainWindowHandle
Dim w = New WndProxy(Process.GetCurrentProcess().MainWindowHandle)
MessageBox.Show(w, "Hi")
' using GetConsoleWindow api
Dim s = New WndProxy(NativeMethods.GetConsoleWindow)
MessageBox.Show(s, "Hi")
End Sub
End Module
In a console application which streamed diagostics to the console, and for which I wanted to disable mouse input, I tried
GetConsoleWindow(),
Process.GetCurrentProcess().MainWindowHandle, and FindWindowByCaption(IntPtr.Zero, Console.Title).
Each of these returned the same non-zero handle, but when I tried to use that handle in SetConsoleMode it threw a "Invalid Handle" exception. Finally I tried SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode | ENABLE_EXTENDED_FLAGS)) with STD_INPUT_HANDLE defined as -10, and it worked. MS's documentation suggests that handles to consoles may be reassigned, and I am not comfortable or happy with this solution, but so far it is the only thing I've found that allows me to disable quick edit mode programmatically. GetStdHandle(STD_INPUT_HANDLE) returns '3', the other calls return a 7 digit value that varies each time the program is run.

How do I get a System.Windows.Form instance from its Win32 handle?

The following code implements a simple singleton that ensures only 1 instance of my application can be run. However, if another instance is started, I need to be able to grab that instance's command-line arguments, pass them to the initial instance, then terminate the second instance.
The issue comes in when I'm attempting to get hold of the first instance of the application. Once I've found the handle of that instance's main form, I pass it to the Control.FromHandle() method, expecting to get back a MainForm. Instead, the return value is always null. (Control.FromChildHandle() gives the same result.)
Therefore, my question is simply: what am I doing wrong? And is this even possible in .NET?
public class MainForm : Form
{
[DllImport("user32")]
extern static int ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32")]
extern static bool SetForegroundWindow(IntPtr hWnd);
private Mutex singletonMutex;
private void MainForm_Load(object sender, EventArgs e)
{
bool wasCreated;
singletonMutex = new Mutex(false, Application.ProductName + "Mutex", out wasCreated);
// returns false for every instance except the first
if (!wasCreated)
{
Process thisProcess = Process.GetCurrentProcess();
Process[] peerProcesses = Process.GetProcessesByName(thisProcess.ProcessName.Replace(".vshost", string.Empty));
foreach (Process currentProcess in peerProcesses)
{
if (currentProcess.Handle != thisProcess.Handle)
{
ShowWindowAsync(currentProcess.MainWindowHandle, 1); // SW_NORMAL
SetForegroundWindow(currentProcess.MainWindowHandle);
// always returns null !!!
MainForm runningForm = (MainForm) Control.FromHandle(currentProcess.MainWindowHandle);
if (runningForm != null)
{
runningForm.Arguments = this.Arguments;
runningForm.ProcessArguments();
}
break;
}
}
Application.Exit();
return;
}
}
Single-instance apps are well supported by the .NET framework. Check this thread for an example that does exactly what you need.
Control.FromHandle isn't going to work because the control you're looking for is in another process (and therefore in another appdomain).
You already have the WindowHandle but it's use is limited to the Win32 API. Nothing from WinForms is going to work.
You can send (WM_) messages but it's hard to get data across.
Options
use something low-level with a
temp-file.
use remoting (WCF)
Try the following
var form = (Form)(Control.FromHandle(myHandle));
EDIT
Re-read your question and realized you are looking at a handle in another process. There is no way to convert a handle in another process to a Form instance in the current process. My solution will only work for handles in the same process.
The only way to get ahold of the Form instance is to use Remoting. But that will require cooperation on the part of both processes which does not appear to be what you are looking for.
You are really trying to implement a singleton application. There are a few examples in the Internet (sorry, haven't really tried myself), e.g.
http://www.codeproject.com/KB/cs/SingletonApplication.aspx
http://www.nathanm.com/csharp-wpf-singleton-application/
You can't call code in another process directly, you need to use some form of inter-process communication
If you are communication only between processes started by the same user on the same computer you can use window messages (using WinAPI PostMessage and overriding WndProc), otherwise I think remoting is the easiest to use in .net
I use the Microsoft.VisualBasic.dll library described in the thread that nobugz pointed to. Yes, you can use it in C#. You just override the OnStartupNextInstance and pass the command line into your program in whatever way works best for you.
This is a whole lot easier than messing around with the threads manually.

Categories

Resources