Resize window without showing window contents - c#

In Windows you have a setting to "Show window contents while dragging". When this is off you are instead resizing with an outline of the window.
My WPF application has many controls on it, so it is extremely slow to resize. Is there a way to make my application resize by showing only the window outline instead of always updating the contents?
I found this question regarding WinForms but unfortunately I'm not able to adapt it to WPF. I can hook onto the HwndSource, but the message numbers may have changed in Windows 10, so the if statement in that answer is never entered... or there may be other things at work.
Also, inside the if it calls the WndProc base after is has changed a system parameter, then resetting the system parameter when it has finished calling it. But calling that method is not an option in WPF as a Window object does not have a way to forward the message.
public void OnViewLoaded() {
HwndSource source = HwndSource.FromHwnd(
new WindowInteropHelper(this).Handle);
source?.AddHook(WndProc);
}
private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
IntPtr lParam, ref bool handled) {
if (msg == WM_SYSCOMMAND && (wParam.ToInt32() & 0xfff0) == SC_SIZE) {
// This if is never entered
int isDragFullWindow;
GetSystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, out isDragFullWindow, 0);
if (isDragFullWindow != 0)
SetSystemParametersInfo(SPI_SETDRAGFULLWINDOWS, 0, 0, 0);
// How to call this?
base.WndProc(ref m);
if (isDragFullWindow != 0)
SetSystemParametersInfo(SPI_SETDRAGFULLWINDOWS, 1, 0, 0);
}
}

Related

Mouse hook getting disconnected

I'm trying to implement a color picker that takes the color from a pixel everywhere in the screen.
To do that I'm planning to use a global mouse hook to listen to WM_MOUSEMOVE in order to update the color as the mouse is moved around and listen to mouse clicks to confirm (WM_LBUTTONDOWN) or cancel(WM_RBUTTONDOWN) the operation.
I have followed one of the many tutorials around and I came up with this (in a Console Application, just to test out if the process works):
static IntPtr hook;
static bool click;
static NativeMethods.LowLevelHookStruct llhs;
static void Main(string[] args)
{
hook = NativeMethods.SetWindowsHookEx(NativeMethods.WH_MOUSE_LL, MouseHookCallback, (IntPtr)null, 0);
if (hook != IntPtr.Zero)
{
Console.WriteLine("Hook Set");
while (!Console.KeyAvailable) {
Console.WriteLine("{0} {1} {2}", hook, llhs.pt.x, llhs.pt.y);
if(click) Console.WriteLine("click!");
click = false;
System.Threading.Thread.Sleep(250);
}
}
}
and
public static IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
NativeMethods.LowLevelHookStruct hookStruct = (NativeMethods.LowLevelHookStruct)Marshal.PtrToStructure(lParam, typeof(NativeMethods.LowLevelHookStruct));
if (NativeMethods.MouseMessages.WM_MOUSEMOVE == (NativeMethods.MouseMessages)wParam)
{
llhs = hookStruct;
}
if (NativeMethods.MouseMessages.WM_LBUTTONDOWN == (NativeMethods.MouseMessages)wParam)
{
click = true;
}
else if (NativeMethods.MouseMessages.WM_RBUTTONDOWN == (NativeMethods.MouseMessages)wParam)
{
}
}
return NativeMethods.CallNextHookEx(hook, nCode, wParam, lParam);
}
NativeMethods is simply a class where I keep all the DllImport related stuff.
Once I run the console application, the mouse cursor gets stuck for a couple of seconds, and in the console I get this - even while the cursor is stuck
Hook Set
3945554872 0 0
3945554872 0 0
3945554872 0 0
3945554872 0 0
...
Going at it in debug, it seems that my hook is never called, not even once.
Any idea what might be wrong?
Following #Hans Passant comment I moved my test code to a WinForms application, and callbacks started coming.
Then it was just a matter to find out that the callback was being garbage collected, so all I had to do was to change
hook = NativeMethods.SetWindowsHookEx(NativeMethods.WH_MOUSE_LL, MouseHookCallback, (IntPtr)null, 0);
to
private NativeMethods.LowLevelHookProc _hookCallback;
...
_hookCallback = new NativeMethods.LowLevelHookProc(MouseHookCallback);
hook = NativeMethods.SetWindowsHookEx(NativeMethods.WH_MOUSE_LL, _hookCallback, (IntPtr)null, 0);
in order to keep a reference to the callback so that it would not be GCed.

How do I get the handle of a child window in Internet Explorer?

My problem is not being able to get the a flash child control inside Internet Explorer but I can do it within a webbrowser control. The webbrowser control is buggy with the flash object, so I need to do the work through Internet explorer and automate flash testing.
Below is the code for the webbrowser control which I am trying to replicate in IE 11.
private async Task<bool> clickCoorindate(Point point)
{
IntPtr handle = null;
Process[] processes = Process.GetProcessesByName("iexplore");
foreach (Process p in processes)
{
handle = p.MainWindowHandle;
}
//webBrowser1.Focus();
int x = point.X;
int y = point.Y;
// IntPtr handle = webBrowser1.Handle;
StringBuilder className = new StringBuilder(100);
while (className.ToString() != "MacromediaFlashPlayerActiveX")
{
handle = GetWindow(handle, 5); // Get a handle to the child window
GetClassName(handle, className, className.Capacity);
}
IntPtr lParam = (IntPtr)((y << 16) | x); // The coordinates
IntPtr wParam = IntPtr.Zero; // Additional parameters for the click (e.g. Ctrl)
const uint downCode = 0x201; // Left click down code
const uint upCode = 0x202; // Left click up code
const uint moveCode = 0x200;
SendMessage(handle, downCode, wParam, lParam); // Mouse button down
SendMessage(handle, upCode, wParam, lParam); // Mouse button up
Thread.Sleep(20);
SendMessage(handle, downCode, wParam, lParam); // Mouse button down
SendMessage(handle, upCode, wParam, lParam); // Mouse button up
return true;
}
When I use Spy++, the Flash object window is a child window of the IE window.
When I pass the IE mainwindow handle in the code above, it never finds the MacromediaFlashPlayerActiveX handle.
When testing this code in webbrowser control and passing the webbrowser control handle, it finds the flash object fine.
Any idea how to make this work with IE?
Someone asked a question similar here and found the solution, but unfortunately did not add it.
private static bool EnumWindow(IntPtr handle, IntPtr pointer) doesn't run
The solution comment from link above:
I found the solution :). Basically the iexplorer process had defined many different windows handlers. I had to use the EnumThreadWindows and get all the Windows Handler for that process. After that, just look for the Class Name I was interested in.

How do you fool a window into believing it has focus?

I have been trying to send mouse clicks to a WebBrowser control inside of my form using PostMessage(), and I have run into a rather significant issue. What I am trying to achieve is to simulate mouse clicks on this WebBrowser while my form is minimized. Usually PostMessage() would work just fine doing this, but it seems that it only works while my form has focus. This leads me to believe that there is some check going on to see if the particular website I am loading into my WebBrowser control is in focus before it handles mouse events.
This is how I send the clicks with my program:
private void SendClick(Point location)
{
resetHandle = true;
StringBuilder className = new StringBuilder(100);
while (className.ToString() != "Internet Explorer_Server")
{
handle = GetWindow(handle, 5); // 5 == child
GetClassName(handle, className, className.Capacity);
//MessageBox.Show(className.ToString());
}
IntPtr lParam = (IntPtr)((location.Y << 16) | location.X);
IntPtr wParam = IntPtr.Zero;
const uint downCode = 0x201;
const uint upCode = 0x202;
const uint moveCode = 0x200;
PostMessage(handle, moveCode, wParam, lParam); //move mouse
PostMessage(handle, downCode, wParam, lParam); // mousedown
PostMessage(handle, upCode, wParam, lParam); // mouseup
}
This is what the resetHandle does:
private void timer3_Tick(object sender, EventArgs e)
{
if (resetHandle == true)
{
handle = webBrowser1.Handle;
resetHandle = false;
}
}
I'm not sure if there is a better way of sending mouse events to a background window and I am open to any ideas. What I am really asking though is if it is at all possible to make a window believe it is in focus when it is actually still minimized?
Any help at all would be much appreciated!
Rather than keep the window minimized, keep it normal (restored), but set its X or Y coordinate so that it is positioned off screen.
If you want give the user the illusion of minimizing and restoring it, use HwndSource.AddHook to watch for SC_MINIMIZE. In your HwndSourceHook handler, move the window on or off screen according to the pseudo-minimized state, and set handled to true.

Send emulated events to another window

I send mouse events to another application in the following way. The problem is, this works for some applications but not for others.
Why?
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
private const int downclick = 0x201;
private const int upclick = 0x202;
IntPtr handle = IntPtr.Zero;
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
foreach (Process p in Process.GetProcessesByName("mspaint"))
{
IntPtr handle = p.MainWindowHandle;
int X = 50;
int Y = 380;
IntPtr lParam = (IntPtr)((Y << 16) | X);
IntPtr wParam = IntPtr.Zero;
SendMessage(handle, downclick, wParam, lParam);
SendMessage(handle, upclick, wParam, lParam);
}
}
}
}
Using Spy++ I see that the application recieves the following data:
<00062> 0004052C S WM_LBUTTONDOWN fwKeys:0000 xPos:50 yPos:380
<00063> 0004052C R WM_LBUTTONDOWN
<00064> 0004052C S WM_LBUTTONUP fwKeys:0000 xPos:50 yPos:380
<00065> 0004052C R WM_LBUTTONUP
I assume that the events themselves are correct. But I don't know why it works for some software but not for others. How can I send mouse messages from one window to another?
The software where I want to send the messages is not always visible.
Is it possible at all?
No it's not possible in any reliable way - as you've found out in your testing. The mouse messages are only one part of the input. Windows keeps an input state and just sending messages will not update that input state. And you're also ignoring mouse move messages, etc.
For example in your WinForms application you can use the MousePosition property to get the current mouse positon. Sending messages can't simulate that.
Also you can't send the mouse message to the main window handle, you would have to find the exact button you want to click on and send the message directly to the correct button.
So maybe it will work if the application is only listening for mouse messages this will work, but if not they it won't.
They supported way to simulate mouse clicks, is the SendInput function. But that won't work with minimized applications. It literally goes through the entire Windows input process and will move the mouse cursor - which means that the application has to be visible on the screen.
Here's some information, it talks about keyboard events, but similar logic applies:
http://blogs.msdn.com/b/oldnewthing/archive/2005/05/30/423202.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2010/12/21/10107494.aspx

Adding a section to the To-Do bar in Outlook 2007/2010?

I would like to add a new section to the To-Do Bar in Outlook 2010 (or 2007). I found some code to create a new collapsible task pane and someone claiming you can't modify the To-Do bar, but I also found a product called Add-In Express that claims it can do it (although at $349 it's not worth it for a one-off project).
Is is possible to do that?
After some research (and after having seen the product documentation of Add-in Express), I figured that it is possible to customize the To-Do Bar in Outlook 2007.
There is a proof-poof-concept on CodeProject that embeds a "custom" (read self-written) pane into Outlooks main window. The article has been written by Lukas Neumann and is available here:
Additional custom panel in Microsoft Outlook
The principle is the following:
Search the Outlook window for the child window where you want to place your own window (i.e. the To-Do Bar child window)
Resize the contents of that window to make some space for your controls
Add your own window as a child
Subclass the To-Do Bar window to hook into the message loop of that window
There is basically only two modifications that need to be done to adjust the sample code:
Get the correct child window handles: The window class of the To-Do Bar is called "WUNDERBAR". This class is used for several child windows so make sure to also check for the correct window title ("ToDoBar") or search by window title only.
Get the resizing of the panel right (simple but not always easy ;-).
(And add some proper error handling if the To-Do Bar is not found etc).
It's a strong plus if you are familiar with Spy++ as it is needed to find out the class names and window titles of Outlook's child windows.
I suggest you to download the sample code and apply the following modifications:
In Connect.cs:
private const string SIBLING_WINDOW_CLASS = "NetUINativeHWNDHost";
public delegate bool EnumChildCallback(IntPtr hwnd, ref IntPtr lParam);
[DllImport("User32.dll")]
public static extern bool EnumChildWindows(IntPtr hWndParent, EnumChildCallback lpEnumFunc, ref IntPtr lParam);
[DllImport("User32.dll")]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
public static bool EnumChildProc(IntPtr hwndChild, ref IntPtr lParam)
{
StringBuilder className = new StringBuilder(128);
GetClassName(hwndChild, className, 128);
int length = GetWindowTextLength(hwndChild);
StringBuilder windowText = new StringBuilder(length + 1);
GetWindowText(hwndChild, windowText, windowText.Capacity);
if (className.ToString() == "WUNDERBAR" && windowText.ToString() == "ToDoBar")
{
lParam = hwndChild;
return false;
}
return true;
}
public void OnStartupComplete(ref System.Array custom)
{
if (_outlookApplication == null)
return; //We were not loaded into Outlook, so do nothing
//Get the instance of Outlook active explorer (= the main window) and start capturing selection changes
_outlookExplorer = _outlookApplication.ActiveExplorer();
_outlookExplorer.SelectionChange += new ExplorerEvents_10_SelectionChangeEventHandler(outlookExplorer_SelectionChange);
//Find Outlook window handle (HWND)
IntPtr outlookWindow = FindOutlookWindow();
if (outlookWindow == IntPtr.Zero)
return;
// Find ToDoBar window handle
IntPtr todoBarWindow = IntPtr.Zero;
EnumChildCallback cb = new EnumChildCallback(EnumChildProc);
EnumChildWindows(outlookWindow, cb, ref todoBarWindow);
if (todoBarWindow == IntPtr.Zero)
return;
//Find sibling window handle (HWND)
//Sibling window is the window which we are going to "cut" to make space for our own window
IntPtr siblingWindow = SafeNativeMethods.FindWindowEx(todoBarWindow, IntPtr.Zero, SIBLING_WINDOW_CLASS, null);
if (siblingWindow == IntPtr.Zero)
return;
//Initialise PanelManager and assign own panel to it
_panelManager = new PanelManager(outlookWindow, siblingWindow);
_customPanel = new MyPanel();
_panelManager.ShowBarControl(_customPanel);
}
In PanelManager.cs:
private void ResizePanels()
{
if (_changingSize)
return; //Prevent infinite loops
_changingSize = true;
try
{
//Get size of the sibling window and main parent window
Rectangle siblingRect = SafeNativeMethods.GetWindowRectangle(this.SiblingWindow);
Rectangle parentRect = SafeNativeMethods.GetWindowRectangle(this.ParentWindow);
//Calculate position of sibling window in screen coordinates
SafeNativeMethods.POINT topLeft = new SafeNativeMethods.POINT(siblingRect.Left, siblingRect.Top);
SafeNativeMethods.ScreenToClient(this.ParentWindow, ref topLeft);
//Decrease size of the sibling window
int newHeight = parentRect.Height - topLeft.Y - _panelContainer.Height;
SafeNativeMethods.SetWindowPos(this.SiblingWindow, IntPtr.Zero, 0, 0, siblingRect.Width, newHeight, SafeNativeMethods.SWP_NOMOVE | SafeNativeMethods.SWP_NOZORDER);
//Move the bar to correct position
_panelContainer.Left = topLeft.X;
_panelContainer.Top = topLeft.Y + newHeight;
//Set correct height of the panel container
_panelContainer.Width = siblingRect.Width;
}
finally
{
_changingSize = false;
}
}
The proof-of-concept is a managed COM Add-in and not using VSTO, but a very similar approach should also work for VSTO. Let me know in case you need any further help, as the proof-of-concept already requires some knowledge about subclassing and the Office add-in architecture (IDTExtensibility2).
Please also consider that this is just a proof-of-concept showing the basic technique how to customize the Outlook UI. And my edits are far from beautiful code ;-)
What you're looking for is called a TaskPane, not exactly a Form Region. TaskPanes work a little differently than Form Regions and they are only available in Office 2007 and higher (which doesn't look like it will be a problem for you since you need this for 2007-2010). If nothing else at least knowing the right term might make googling for this a bit easier.
Here's a Custom Task Panes Overview on MSDN.
Now, as far as adding a section to an existing TaskPane, I do not know for sure. But hopefully that gets you a bit closer.
BTW, as a side note, the Add-in Express libraries are awesome. Make this sort of thing a 2-minute task. Highly recommended - and it is likely something you'll use again since they make the job so easy.
Michael,
Take a look at Outlook form regions. http://msdn.microsoft.com/en-us/library/bb386301.aspx
You can add them using a VSTO addin.
Though Add-in express has a few more options to where you can add them.
There are quite a few tutorials on the net as well.
Marcus

Categories