How do I get a tooltip that is attached to the mouse cursor using C#? I'm trying to achieve an effect like the following, a small tooltip showing the status of Ctrl / Shift / Alt keys.
I'm currently using a Tooltip but it refuses to display unless it has about 2 lines of text.
tt = new ToolTip();
tt.AutomaticDelay = 0;
tt.ShowAlways = true;
tt.SetToolTip(this, " ");
In mouse move:
tt.ToolTipTitle = ".....";
So I don't think there is any way you could do this purely with managed code. You would have to go native.
The way I see it there are two options.
P/Invoke the SendMessage function. Set the hwnd to your target window and pass in a TTM_ADDTOOL message and a TOOLINFO structure for the lParam. This is useful when you want a tooltip on an external window you haven't created (one that isn't in your app). You could get its hwnd by calling FindWindow.
See how all this is done here in this article. You just have to add the P/Invoke.
Apparently you can use the CreateWindowEx() function with TOOLTIPS_CLASS as a classname and it will generate a tooltip for you. Something like this:
HWND hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
hwndParent, NULL, hinstMyDll,
NULL);
SetWindowPos(hwndTip, HWND_TOPMOST,0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
See the whole article here: http://msdn.microsoft.com/en-us/library/windows/desktop/bb760250(v=vs.85).aspx
To get you upto speed, you would have something like this defined in you .NET code. I got the definition from here.
You will find all the structures I've mentioned in my answer on the same website (or other similar ones) Once you have defined all of them in your code, you can then easily transpose/port the C samples that are in my answer and the linked articles.:
class NativeFunctions
{
[DllImport("user32.dll", SetLastError=true)]
static extern IntPtr CreateWindowEx(
WindowStylesEx dwExStyle,
string lpClassName,
string lpWindowName,
WindowStyles dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
}
Related
How do you perform mouse movements & clicks virtually ?
What I mean by virtually is that, the main mouse isn't affected, so basically "creating" a second mouse that is controlled by setting x/y position (pref with user32.dll).
I have read a few similar questions here but the answer usually is with your main mouse, Example:
DllImport("user32")]
public static extern int SetCursorPos(int x, int y);
^ this moves your main mouse to x&y position on screen, what I want is to perform this action with a "virtual mouse" so my main mouse isn't moved, so basically I can keep using my computer while this "virtual mouse" does something else in another window for example.
then using this "virtual mouse" to perform clicks [virtual key codes]
I have tried this:
IntPtr MakeLParam(int x, int y) => (IntPtr)((y << 16) | (x & 0xFFFF));
var pointPtr = MakeLParam(900, 1000);//x y cords
IntPtr hWnd = ScreenCapture.FindWindow(null, ScreenCapture.GetWindowName()); //Finds Window
PostMessage(hWnd, WM_MOUSEMOVE | WM_LBUTTONDOWN | WM_LBUTTONUP, IntPtr.Zero, pointPtr);
[DllImport("User32.dll")]
public static extern Int32 PostMessage(
IntPtr hWnd, // handle to destination window
int Msg, // message
IntPtr wParam, // first message parameter
IntPtr lParam); // second message parameter
But nothing happens at all, and my guess is that WM_MOUSEMOVE is not working properly
public const int WM_KEYDOWN = 0x100;
public const int WM_KEYUP = 0x101;
public const int WM_COMMAND = 0x111;
public const int WM_LBUTTONDOWN = 0x201;
public const int WM_LBUTTONUP = 0x202;
public const int WM_LBUTTONDBLCLK = 0x203;
public const int WM_RBUTTONDOWN = 0x204;
public const int WM_RBUTTONUP = 0x205;
public const int WM_RBUTTONDBLCLK = 0x206;
public const int WM_MOUSEMOVE = 0x0200;
UPDATE, I think I solved it somewhat !
After reading more about PostMessage /SendInput, I found out that "application windows" can have layers of "application windows", so I used ispy++ to check it out, and yes the program had another layer where I wanted to click.
So to solve this I took the class name of the window where I wanted to click as well as the window name, here is an example code:
public static bool ClickTest()
{
IntPtr MakeLParam(int x, int y) => (IntPtr)((y << 16) | (x & 0xFFFF));//Just converts x&y to InPtr lParam
IntPtr WindowhWid = ScreenCapture.FindWindow(null, ScreenCapture.GetWindowName()); //Gets the window hWid
var ClasshWid = FindWindowEx(WindowhWid, IntPtr.Zero, "Classname", null); //Gets the Class hWid using WindowhWid AND the Class name (Need to find a way to get the classname using process list)
PostMessage(ClasshWid, WM_LBUTTONDOWN | WM_LBUTTONUP, 0, MakeLParam(938, 1011));//Finally sends it to the classhWid
return true;
}
Using these two functions
[DllImport("User32.dll")]
public static extern int FindWindowEx(
IntPtr hwndParent,
IntPtr hwndChildAfter,
string strClassName,
string strWindowName);
[DllImport("User32.dll")]
public static extern Int32 PostMessage(
int hWnd, // handle to destination window
int Msg, // message
int wParam, // first message parameter
IntPtr lParam); // second message parameter
Note that it won't click if the appilication window is MINIMIZED, although it will click if the window is behind another appilication window !
Also the X & Y is the screen X & Y and not the Application X & Y !
Now the question remains, why doesn't it work while the application window is minimized ?
The click doesn't go anywhere but to the application, so shouldn't it work while the application is minimized ?
It migiht not be what you're up to and what you look after, but AutoIT Dll offers plenty of automation ready to use and in the easiest way possible. Since you rely on a windows DLL then it's perfect.
If you're still on your own way to user32.dll then check Low level hook, that's what I used.
I highly recommend AutoIT dll though, it will save you ton of hair pulling hours guessing what's happening to the windows messages that do not respond properly
So I found an example from an answer provided here
There was an answer that gave this example of code to move the Notepad window to the top left corner of the screen. I tried it and it worked fine. I then tried it on a small project I am working on and I couldn't move it.
NOTE: I did change the "Notepad" to the name at the top of the window I wanted to move.
using System;
using System.Runtime.InteropServices; // For the P/Invoke signatures.
public static class PositionWindowDemo
{
// P/Invoke declarations.
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
const uint SWP_NOSIZE = 0x0001;
const uint SWP_NOZORDER = 0x0004;
public static void Main()
{
// Find (the first-in-Z-order) Notepad window.
IntPtr hWnd = FindWindow("Notepad", null);
// If found, position it.
if (hWnd != IntPtr.Zero)
{
// Move the window to (0,0) without changing its size or position
// in the Z order.
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
}
}
I will give an example. Consider Visual Studios and how it has the Solution Explorer Window or the Output window, and I can drag them with the mouse and move them or undock them. Would there be a way to have an application that has windows inside of it similar to Visual Studios and get the position of them in a program?
I have seen many answers on here about moving a window or finding the active window etc. However I am not sure if I will be able to access this subWindow that is inside of another application.
Thanks
I've made this little .ps1 script as it allows me to run C# without using a compiler (directly at least). I'd like to move the "Accessibility On-Screen Keyboard" that opens with cmd /c osk.exe as I can't really use TabTip - the panned touchscreen keyboard on Win8+.
As the On-Screen Keyboard isn't really that pretty like the panned keyboard, I'd like to move the keyboard to a desired location and resize it. I noticed the OSK has a child window (OSKMainClass → DirectUIHWND), so I went even for that, but no luck. On the other hand, the same code for a single window works for notepad and correctly places and resizes it.
I put Process.Start() into the if, so that it gave back some feedback, therefore I see it found the child window - that's nice. BUT, it didn't move it.
An interesting thing appeared when I pressed Alt+Tab and held the Alt - the OSK window appeared like a grey fullscreen one (metro-like style). I'm not sure if that's an intended behavior for a parent window or not.
Also, I thought it'd be the window styles' thingy, but no, the styles are almost the same (except two unrelated styles), so I'm without any clue how to continue. Any ideas?
Code:
$CSsource = #"
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Win {
public static class API {
[DllImport("user32.dll")]
static extern IntPtr FindWindow(
string lpClassName,
string lpWindowName
);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(
IntPtr parentHwnd,
IntPtr childAfter,
string className,
string windowTitle
);
[DllImport("user32.dll")]
static extern bool ShowWindow(
IntPtr hWnd,
int nCmdShow
);
[DllImport("user32.dll")]
static extern bool MoveWindow(
IntPtr hWnd,
int X, int Y,
int Width, int Height,
bool Repaint
);
public static void Move(
string wClass, string wName,
string childClass,
int top, int left,
int width, int height
) {
IntPtr hwnd = FindWindow(wClass, wName);
if ((int) hwnd > 0) {
IntPtr subHwnd;
if (childClass != String.Empty) {
subHwnd = FindWindowEx(hwnd, IntPtr.Zero, childClass, null);
} else {
subHwnd = IntPtr.Zero;
}
if ((int) subHwnd > 0) {
MoveWindow(subHwnd, left, top, width, height + 50, true);
Process.Start("cmd"); //feedback from loop, heh
} else {
MoveWindow(hwnd, left, top, width, height + 50, true);
}
}
}
}
}
"#
add-type -TypeDefinition $CSsource
#[Win.API]::Move('OSKMainClass', 'On-Screen Keyboard', 'DirectUIHWND', 50, 50, 200, 100)
#[Win.API]::Move('OSKMainClass', 'Accessibility On-Screen Keyboard', 'DirectUIHWND', 50, 50, 200, 100)
[Win.API]::Move('OSKMainClass', 'Accessibility On-Screen Keyboard', '', 50, 50, 200, 100)
[Win.API]::Move('Notepad', 'Untitled - Notepad', '', 50, 50, 200, 100)
OSK window styles:
WS_CAPTION
WS_VISIBLE
WS_CLIPSIBLINGS
WS_CLIPCHILDREN
WS_SYSMENU
WS_THICKFRAME
WS_OVERLAPPED
WS_MINIMIZEBOX
WS_EX_LEFT
WS_EX_LTRREADING
WS_EX_TOPMOST
WS_EX_WINDOWEDGE
WS_EX_APPWINDOW
WS_EX_LAYERED
WS_EX_NOACTIVATE
Notepad window styles:
above +
WS_RIGHTSCROLLBAR
WS_ACCEPTFILES
OSK has UIAccess="true" in its manifest so it runs at a higher integrity level (slightly above medium).
To interact with it you need to:
Run your app elevated
or
Put UIAccess="true" in your manifest
Sign the .exe (This blog post indicates that you can self sign during testing)
Put the .exe somewhere inside the Program Files folder
You can also try to disable UAC to verify that your lack of UIAccess is the problem.
I have an app that is essentially a wizard that goes through some dialog boxes. One of the forms has just a button on it that brings up the common "take picture" dialog.
After that picture functionality is dismissed the little keyboard icon shows up (inconveniently covering over one of my wizard buttons).
I tried setting the covered window to the fron by calling:
nextButton.BringToFront();
But that has no effect. I need to disable the little keyboard icon somehow and not sure how to do it.
Note - it is not the soft keyboard - but the image that the user clicks that will bring that up.
Note - there are no text controls on this form - there are only 4 buttons - one that initiates the CameraCaptureDialog, and a few others that control the user going to the "next" and "previous" screens.
EDIT
Given that two people were very confident their code would work, and looking at the references online I figured they might be right I figured I would elaborate on the issue since neither suggestions fix the problem.
The keyboard item seems to be a remnant left over after I select either the cancel or OK button on the menu in the "take picture"/CameraCaptureDialog.
On exiting the Dialog I seem to have the middle/keyboard menu item left over and there is nothing I seem to be able to do about it.
Here is what it looks like in the emulator (happens on emulator as well)
Note - calling all the following have NO effect on the keyboard icon thingy hiding the button:
// nextButton is the Button on the control hidden by the keyboard icon thingy
nextButton.Focus();
nextButton.BringToFront();
nextButton.Invalidate();
nextButton.Refresh();
nextButton.Show();
I was also looking for the solution to hide the small keyboard icon (SIP icon) and I achieved this by using the FindWindowW and MoveWindow or SetWindowPos functions of coredll.dll and user32.dll
Declare the function we are interested in:
[DllImport("coredll.dll", EntryPoint = "FindWindowW", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("coredll.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
Then find the handle to keyboard icon and call the SetWindowPos to hide it:
IntPtr hWnd = FindWindow(Nothing, "MS_SIPBUTTON");
SetWindowPos(hWnd, 1, 0, 0, 0, 0, &H80);
Useful links:
P/Invoke - coredll.dll
Disable keyboard icon in Windows Mobile using VB.net
Manage SIP - skip to the bottom on this post and look for
comments of user name Mark
EDIT
I had to modify this slightly to compile.
const int SWP_HIDE = 0x0080;
IntPtr hWnd = FindWindow(null, "MS_SIPBUTTON");
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_HIDE);
[DllImport("coredll.dll", EntryPoint = "SipShowIM")]
public static extern bool SipShowIMP(int code);
SipShowIMP(1); //Show the keyboard
SipShowIMP(0); //Hide the keyboard
That should do it :-)
This answer was taken from the following article http://beemobile4.net/support/technical-articles/windows-mobile-programming-tricks-on-net-compact-framework-12 (I have only added the using statements). I'm on Windows Mobile 6.1 Classic, .NET CF 3.5.
using System;
using System.Runtime.InteropServices;
[DllImport("coredll.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string caption, string className);
[DllImport("coredll.dll", SetLastError = true)]
private static extern bool ShowWindow(IntPtr hwnd, int state);
[DllImport("coredll.dll")]
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
private const int SW_HIDE = 0;
private const int SW_SHOW = 1;
private const int GW_CHILD = 5;
///
/// Shows the SIP (Software Input Panel) button.
///
static public void ShowHideSIP(int nShowOrHide)
{
IntPtr hSipWindow = FindWindow("MS_SIPBUTTON", "MS_SIPBUTTON");
if (hSipWindow != IntPtr.Zero)
{
IntPtr hSipButton = GetWindow(hSipWindow, GW_CHILD);
if (hSipButton != IntPtr.Zero)
{
bool res = ShowWindow(hSipButton, nShowOrHide);
}
}
}
Some background
One of my current clients runs a chain of Internet points where customers an access the net through PC:s set up as "kiosks" (a custom-built application "locks" the computer until a user has signed in, and the running account is heavily restricted through the Windows group policy). Currently, each computer is running Windows XP and uses Active Desktop to display advertisements in the background. However, since my client has got problems with Active Desktop crashing on a daily basis (in addition to generally slowing down the computer) I have been asked to develop an application that replaces it.
The problem
I am trying to investigate whether it is possible to build a Windows forms application (using C#) that always stays in the background. The application should lie above the desktop (so that it covers any icons, files etc) but always behind all other running applications. I guess I'm really looking for a BottomMost property of the Form class (which doesn't exist, of course).
Any tips or pointers on how to achieve this would be highly appreciated.
This isn't directly supported by the .NET Form class, so you have two options:
1) Use the Win32 API SetWindowPos function.
pinvoke.net shows how to declare this for use in C#:
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
So in your code, call:
SetWindowPos(Handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
As you commented, this moves the form to the bottom of the z-order but doesn't keep it there. The only workaround I can see for this is to call SetWindowPos from the Form_Load and Form_Activate events. If your application is maximized and the user is unable to move or minimise the form then you might get away with this approach, but it's still something of a hack. Also the user might see a slight "flicker" if the form gets brought to the front of the z-order before the SetWindowPos call gets made.
2) subclass the form, override the WndProc function and intercept the WM_WINDOWPOSCHANGING Windows message, setting the SWP_NOZORDER flag (taken from this page).
I think the best way to do so is using the activated event handler and SendToBack method, like so:
private void Form1_Activated(object sender, EventArgs e)
{
this.SendToBack();
}
Set your window to be a child window of the desktop (the "Program Manager" or "progman" process). I've succeeded with this method in Windows XP (x86) and Windows Vista (x64).
I stumbled on this method while searching for a way to make a screensaver display as if it were wallpaper. It turns out, this is sort of built in to the system's .scr handler. You use screensaver.scr /p PID, where PID is the process id of another program to attach to. So write a program to find progman's handle, then invoke the .scr with that as the /p argument, and you have screensaver wallpaper!
The project I'm playing with now is desktop status display (shows the time, some tasks, mounted disks, etc), and it's built on Strawberry Perl and plain Win32 APIS (mainly the Win32::GUI and Win32::API modules), so the code is easy to port to or understand any dynamic language with similar Win32 API bindings or access to Windows' Scripting Host (eg, ActivePerl, Python, JScript, VBScript). Here's a relevant portion of the class that produces the window:
do { Win32::API->Import(#$_) or die "Win32::API can't import #$_ ($^E)" } for
[user32 => 'HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)'],
[user32 => 'HWND SetParent(HWND hWndChild, HWND hWndNewParent)'],
sub __screen_x {
Win32::GUI::GetSystemMetrics(SM_CXSCREEN)
}
sub __screen_y {
Win32::GUI::GetSystemMetrics(SM_CYSCREEN)
}
sub _create_window { # create window that covers desktop
my $self = shift;
my $wnd = $$self{_wnd} = Win32::GUI::Window->new(
-width => __screen_x(), -left => 0,
-height => __screen_y(), -top => 0,
) or die "can't create window ($^E)";
$wnd->SetWindowLong(GWL_STYLE,
WS_VISIBLE
| WS_POPUP # popup: no caption or border
);
$wnd->SetWindowLong(GWL_EXSTYLE,
WS_EX_NOACTIVATE # noactivate: doesn't activate when clicked
| WS_EX_NOPARENTNOTIFY # noparentnotify: doesn't notify parent window when created or destroyed
| WS_EX_TOOLWINDOW # toolwindow: hide from taskbar
);
SetParent($$wnd{-handle}, # pin window to desktop (bottommost)
(FindWindow('Progman', 'Program Manager') or die "can't find desktop window ($^E)")
) or die "can't pin to desktop ($^E)";
Win32::GUI::DoEvents; # allow sizing and styling to take effect (otherwise DC bitmaps are the wrong size)
}
This program buffers output to prevent flickering, which you'll probably want to do as well. I create a DC (device context) and PaintDesktop to it (you could use any bitmap with only a couple more lines -- CreateCompatibleBitmap, read in a file, and select the bitmap's handle as a brush), then create a holding buffer to keep a clean copy of that background and a working buffer to assemble the pieces -- on each loop, copy in background, then draw lines and brush bitmaps and use TextOut -- which is then copied to the original DC, at which time it appears on screen.
Yes, function SetWindowPos with flag HWND_BOTTOM should help you. But, from my experience: even after calling SetWindowPos as result of some user operations your window may bring to front.
subclass the form, override the WndProc function and intercept the Windows message(s) that are responsible for moving it up the z-order when it gets activated.
Create a Panel that cover your form, but what ever you want on that Panel, then in the Panel's Click-Event write this.sendback .
I've managed to get rid of the flickering when using setwindowpos...
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_NOZORDER = 0x0004;
const int WM_ACTIVATEAPP = 0x001C;
const int WM_ACTIVATE = 0x0006;
const int WM_SETFOCUS = 0x0007;
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
const int WM_WINDOWPOSCHANGING = 0x0046;
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd,
IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr BeginDeferWindowPos(int nNumWindows);
[DllImport("user32.dll")]
static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);
private void Window_Loaded(object sender, RoutedEventArgs e)
{
IntPtr hWnd = new WindowInteropHelper(this).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SETFOCUS)
{
IntPtr hWnd = new WindowInteropHelper(this).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
handled = true;
}
return IntPtr.Zero;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.RemoveHook(new HwndSourceHook(this.WndProc));
}