I'm working on an application that should do the following on start-up:
Connect to an external application using COM (AutoCAD).
Send message to the application to run some DLL code (opens a window).
Hide AutoCAD's window, but keep the DLL's window visible.
I've successfully completed the first 2 steps, but the third is giving me some issues.
I do not know if it is possible to make a child window visible while it's parent is not visible. Every time that I make the child visible or make it the top most window, AutoCAD becomes visible as well.
My objective is to run my DLL code, but keep AutoCAD running in the background, completely invisible to my users. The DLL must be loaded, through AutoCAD, because it allows me to work with AutoCAD's .NET interface as opposed to COM.
In any case, I'm curious if what I'm trying to do is possible, perhaps through some Windows API calls or perhaps something in .NET.
PS: I'm unsure if this window relationship is really a parent-child one. I'm assuming it is though because my window belongs to the AutoCAD application instance due to the DLL loading.
Any help is greatly appreciated. Thanks! :)
EDIT:
DLL Code to create a window.
//CommandMethod is an AutoCAD attribute for entering into the DLL. This code is called
//when the user attempts the command "AUTOCADCOMMANDNAME" or can be done by simulating
//the command programmatically.
[CommandMethod("AUTOCADCOMMANDNAME", CommandFlags.Session)]
public void CommandEntry()
{
MainWindow mainWin = new MainWindow();
mainWin.ShowDialog();
}
Main Application Code
public static void Main()
{ //Use a utility class to create a connection to AutoCAD via COM
AcadApplication acadApp = ACUtil.GetAcadInstance(out createdNewInstance);
acadApp.Visible = false;
//Irrelevant code omitted...
acadApp.ActiveDocument.SendCommand("AUTOCADCOMMANDNAME");
acadApp.Quit(); //Exit AutoCAD application
//Note: doesn't quit until mainWin closes because of ShowDialog()
}
Can't be done. Parent windows control child window visibility.
Your best alternative is to make the DLL window a top-level window (but owned by the AutoCAD window).
Note that the DLL window will still be part of the AutoCAD thread.
What you want can be achieved, despite what others may think. You just need to think about the problem in a different way. Don't think about parent and child Windows... instead, think about a splash screen Window.
Typically, splash screens appear before the main application Window, but does that make them the parent? No, it doesn't. Normally, they'd be closed after the main Window has opened, but there is no reason why you couldn't hide it instead of closing it.
To find out how to do this in WPF, please refer to my answer from the How to open a child Window like a splash screen before MainWindow in WPF? question, here on Stack Overflow. Extending that answer a little bit, I should point out that you won't need to use a Timer. Instead of the code from the linked page, you could do something like this:
private void OpenMainWindow()
{
autoCadWindow.Visiblity = Visibility.Collapsed;
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
Haha! I found it!
So, I ended up calling the SetWindowPos function in the Windows API and supplied the handle for AutoCAD window. I did this inside my main application:
[DllImport("User32.dll")]
static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int w, int h, uint flags);
public const int SWP_HIDEWINDOW = 0x0080;
public static void Main()
{
//...Setup AutoCAD...
//Change window size and hide it before calling to open mainWin inside the DLL.
SetWindowPos(new IntPtr(acadApp.HWND), new IntPtr(1), 0, 0, 0, 0, SWP_HIDEWINDOW);
//Display mainWin by entering the DLL.
acadApp.ActiveDocument.SendCommand("AUTOCADCOMMANDNAME");
//Terminate application as before...
}
Basically I'm telling the AutoCAD window to hide by modifying the HWND directly. I also set the dimensions to width=0 and height=0 which causes the window to take up the minimum size possible. Unfortunately, the window will flicker once, but for my purposes, that is negligible. If anyone can find a way to remove the flicker, that would be great! :)
EDIT: When using SetWindowPos, Windows tends to remember the values entered for the next time that application window is shown. This means that if not restored properly, then the next time the user opens AutoCAD manually, it will have the coordinates of 0,0 and the minimum width/height.
To change that behavior, it is necessary to obtain the window information. For my program, I used GetWindowRect obtain the original settings. Before closing my program, I restored those settings using SetWindowPos. See the code below for details:
First, import necessary WINAPI functions and structs:
[DllImport("User32.dll")]
static extern bool GetWindowRect(IntPtr hwnd, out RECT rect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
Obtain original settings before modifying window:
RECT originalRect;
GetWindowRect(new IntPtr(acadApp.HWND), out originalRect);
Modify the window to hide (and resize):
SetWindowPos(new IntPtr(acadApp.HWND), new IntPtr(1), 0, 0, 0, 0, SWP_HIDEWINDOW);
Restore original settings before quitting:
SetWindowPos(new IntPtr(acadApp.HWND), new IntPtr(1),
originalRect.Left,
originalRect.Top,
originalRect.Right - originalRect.Left,
originalRect.Bottom - originalRect.Top, 0);
Related
I writing a C# application. I am looking a way to find a window handle of the controls in other applications by just giving the coordinates of the mouse click (or for that matter any coordinates).
Example: On my desktop, I have calculator application opened, notepad opened and some other 3rd party application running. Screen is covered partially by each of them. Now if I run my application and if I click at any location on the screen, I want to be able to find out the window handle of the control (button, textbox, label, tab, frame, etc.) under the mouse irrespective if it was clicked on a button in calculator, File menu in notepad, or some other control in the 3rd party application. It is similar to the functionality we get from Spy++.
BTW, this has already been done for you, sounds like all you need is to clone the repo then customize to your hearts content.
I don't think Global Hooks would be necessary.
You should be able to use any number of methods to get cursor position, hooking is just going to complicate things. For example, you can try the following:
using System;
using System.Windows;
using System.Windows.Input;
using System.Runtime.InteropServices;
namespace Namespace1
{
class Class1
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
public static Point GetMousePosition()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
}
}
Then you just need a few Pinvoke signatures.
Namely, WindowFromPoint and EnumChildWindows.
Refer to "Enumerating Windows/Controls of another application from .Net".
Hope that helps.
Good luck.
Use Windows hook for mouse events (like oleksa said) then this PInvoke to get the foreground window : http://www.pinvoke.net/default.aspx/user32.getforegroundwindow
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
void yourFonction(){
[...]
IntPtr handleTopMostWindow = GetForegroundWindow();
}
you just need to call this method in your code to retrieve the window's foreground handle :
you have to set global Windows hook for mouse events. This will cause yours application to get mouse (or keyboard) clicks in foreign windows.
Please find this article on code project as sample C# wrapper on Windows API. Windows API functions are listed in the article too
I have the wonderful job to create a .exe that is called with a shortcut that is called with the windows native shortcut calling way for elements that are in the start menu folder.
It is supposed to do something with one selected file of the topmost Explorer window.
I have found ways to find all selected files across all explorer windows, I know how to get the window handle of the topmost window and I knew how to get all files selected in the topmost window if getting the topmost window handle would work, which it doesn't in my case:
Calling the native method GetForegroundWindow() doesn't give me the handle of the top window, but another one, maybe the one of the program I wrote, which doesn't even use a console window or anything though visible, so I can't even check if it's a handle from my program without changing it in a way to display the console.I want to get the handle of the foreground window by using a keyboard combination that was set in the options of a shortcut.How do I get the window that was in the foreground before I called my program?Or how do I keep my program from stealing focus?Or how do I give the focus back to the last window that had it? / Take it away from the current one.Or how do I get the topmost window of a specific process? (explorer)
"Or how do I keep my program from stealing focus?"
Try this out...
Add this code to your Form:
private const int WS_EX_NOACTIVATE = 0x8000000;
protected override System.Windows.Forms.CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | WS_EX_NOACTIVATE;
return cp;
}
}
I found my solution.
It feels kind of hacky, but it works reliably.
To explain what it does in short:
1) It collects all current handles of explorer windows and the SHDocVw.IE object behind it (I wonder if there is a better way for this)
2) I am using the absolutely unreliable and weird GetNextWindow() function until I get the first one that was previously collected as explorer handle. This is always the top window. There are usually ~20 handles between the current window and the last selected explorer window.
3) Then I give back the path to the first file selected in that folder window.
This really feels like something that shouldn't be so hard.
I am going over the top with long cat but who cares.
The method should have error handling or checks for undesired states, for example SelectedItems() can be empty or null.
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public static string returnSelectedFile()
{
IntPtr windowPtr = GetForegroundWindow();
Dictionary<string, SHDocVw.InternetExplorer> windows = new System.Collections.Generic.Dictionary<string, SHDocVw.InternetExplorer>();
foreach (SHDocVw.InternetExplorer window in new SHDocVw.ShellWindows())
if (Path.GetFileNameWithoutExtension(window.FullName).ToLower().Equals("explorer"))
windows.Add(window.HWND.ToString(), window);
long protectionCounter = 0;
while (true)
{
if (windows.ContainsKey(windowPtr.ToString()) || protectionCounter++ > 9999999)
break;
windowPtr = GetWindow(windowPtr, 2);
}
return ((Shell32.IShellFolderViewDual2)windows[windowPtr.ToString()].Document).SelectedItems().Item(0).Path;
}
In my winforms application I am trying to get a main window handle, so I can set it as parent to my wpf modal window. I am not too experienced with winforms, so after a bit of googling I found two ways to get it.
System.Windows.Forms.Application.OpenForms[0].Handle
System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle
(1) seems to always return the same value which appears to be correct (at least my modal window behaves as expected), while (2) sometimes returns the same value as (1), but sometimes - an entirely different pointer, which does not seem to work (my modal window appears on top of every other window, not just the parent window).
Can someone explain the difference between the two methods? Is it normal that sometimes they return different results?
Edit:
In case anyone else is wondering: once you get the handle, you can use it by creating WindowInteropHelper class:
public static void SetInteropParent(this Window wpfDialogWindow, IntPtr winformsParentHandle)
{
new WindowInteropHelper(wpdDialogWindow) { Owner = winformsParentHandle };
}
It is certainly not unusual for Process.MainWindowHandle to return the wrong handle. The Process class has to make a guess at which window is the "main" one. There is no mechanism in the native winapi to designate a window as such. So Process makes a guess that the first window is the main one. This has a knack for going wrong in apps that use a splash screen or a login dialog, etc, or create a window on another thread.
Application.OpenForms doesn't have this problem, but has a failure mode, it will lose track of a window when it is recreated. Which happens when the program changes certain properties of the Form that can only be specified when the window is created. The ShowInTaskbar, TransparencyKey and Opacity properties are the most common troublemakers.
The most reliable way is to override the OnHandleCreated() method of the form you want to be the parent. Which is called whenever the Handle property changes. Do note that you want to make sure that this doesn't happen while your WPF window is active, that will kill the WPF window as well. Otherwise easy to observe of course :)
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
SetWpfInteropParentHandle(this.Handle);
}
I'm currently developing a form application which works as an overlay to another program (Skype). Right now, I'm using TopMost = true, but that's a pretty bad solution.
I have a handle to the Skype window, as well as a handle to my own window. How do I make my program fulfill the following three statements:
1. It has to disappear if Skype is minimized
2. It has to appear above Skype
3. It has to appear behind any other application which is above Skype
Above and behind relates to z-order.
I'm currently using the SetWindowLong function, but I cant get the desired results.
[DllImport("user32.dll")]
public static extern int SetWindowLong(HandleRef hWnd, int nIndex, HandleRef dwNewLong);
SetWindowLong(
new HandleRef(child, child.Handle),
-8, // GWL_HWNDPARENT
new HandleRef(owner, owner.Handle));
For #1, my application continually checks if the dimensions of Skype has changed, so I could simply also check if the window is no longer visible. However, I'm completely at loss with #2 and #3.
Thanks in advance.
Kloar
I have a C#/.NET app and I want to implement the following behavior:
I have a popup menu. Whenever the user clicks on anything within the application that is not the popup menu, I want the popup menu to close.
However, whenever a user is not in the application I don't want anything to happen.
I'm trying to manage this through the LostFocus event, but I'm having trouble determining whether my application is the active window. The code looks something like this.
private void Button_LostFocus(object sender, System.EventArgs e)
{
if (InActiveWindow()) {
CloseMenu()
}
else {
// not in active window, do nothing
}
}
What I need to know is how to implement the InActiveWindow() method.
You could P/Invoke into GetForegroundWindow(), and compare the HWND returned to the application's form.Handle property.
Once you have the handle, you can also P/Invoke GetAncestor() to get the root owner window. This should be the handle of your application's main, startup window, if this is in your application.
I stumbled upon your question while working on a project and based on Reed Copsey's answer, I wrote this quick code which seems to do the job well.
Here's the code:
Public Class Form1
'''<summary>
'''Returns a handle to the foreground window.
'''</summary>
<Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function GetForegroundWindow() As IntPtr
End Function
'''<summary>
'''Gets a value indicating whether this instance is foreground window.
'''</summary>
'''<value>
'''<c>true</c> if this is the foreground window; otherwise, <c>false</c>.
'''</value>
Private ReadOnly Property IsForegroundWindow As Boolean
Get
Dim foreWnd = GetForegroundWindow()
Return ((From f In Me.MdiChildren Select f.Handle).Union(
From f In Me.OwnedForms Select f.Handle).Union(
{Me.Handle})).Contains(foreWnd)
End Get
End Property
End Class
I didn't have too much time to convert it to C# as I'm working on a project with a deadline in 2 days but I believe you can quickly do the conversion.
Here is the C# version of the VB.NET code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
///<summary>Gets a value indicating whether this instance is foreground window.</summary>
///<value><c>true</c> if this is the foreground window; otherwise, <c>false</c>.</value>
private bool IsForegroundWindow
{
get
{
var foreWnd = GetForegroundWindow();
return ((from f in this.MdiChildren select f.Handle)
.Union(from f in this.OwnedForms select f.Handle)
.Union(new IntPtr[] { this.Handle })).Contains(foreWnd);
}
}
}
It seems like the biggest reason this is tricky is because the popup loses focus before the main form is deactivated, so the active window will always be in the application at the time of this event. Really, you want to know whether it will still be the active window after the event is over.
You could set up some kind of scheme where you remember that a popup is losing focus, set aside the fact that you will need to close it, and in the LostFocus or Deactivate event of the application's main form cancel the note that tells you you need to close it; but the problem is when will you process the note?
I'm thinking it might be easier, at least if the popup is a direct child of the main form (which I suspect in your case it may be) to hook the Focus or maybe even Click event of the main form and use it close the popup if it is open (perhaps by scanning its list of child forms for any that implement an ICloseOnLostFocus interface, so that the popup will have a chance to participate in the decision and do anything else it needs to do).
I wish I knew of a better document explaining what all these events actually mean and how they are sequenced with respect to one another, MSDN leaves a lot to be desired in describing them.