I am trying to create a Visual Tree Explorer application (like Snoop, Mole) which should automatically identify all desktop applications and detect whether it is a WPF app. or not. I am tring to do this in WPF. Is there any way to get all WPF opened applications. Any special API, or native functions in system dlls, etc.
Regards,
Jawahar
I think you can iterate through the list of all processes and for those processes who have a window, check if the window class name starts with HwndWrapper (I've noticed the WPF windows have a class name like this: HwndWrapper[DefaultDomain;;2e60e21a-8752-4daf-820b-aed289668930])
The code should be something like this:
Process[] procs = Process.GetProcesses();
foreach(Process p in procs)
{
if (p.MainWindowHandle != null)
{
Console.WriteLine(p.MainWindowTitle);
StringBuilder sb = new StringBuilder(257);
RealGetWindowClass(p.MainWindowHandle, sb, 256);
Console.WriteLine(sb.ToString());
if(sb.ToString().StartsWith("HwndWrapper"))
{
Console.WriteLine("WPF window");
}
}
}
[DllImport("user32.dll")]
static extern uint RealGetWindowClass(IntPtr hwnd, [Out] StringBuilder pszType, uint cchType);
with maybe some adjustments depending on your case. When dealing with such a window, one should assume it's a WPF window not take it for a certainty, so error checking must be extensive.
Related
we have a wpf application that should be 'piloted' by a legacy win32 winform application. (We don't own the code ;) )
The legacy application should pilot our application (minimize, bring to front, shut, etc) via windowsclass name we should provide as a configuration parameter written into an ini file.
The problem is we cannot make it work with wpf since if we insert the classname Spy++ gives us, nothing happens. The point is Spi++ returns something like this
HwndWrapper[MyWpfProgram.exe;;16978ce2-3b8d-4c46-81ee-e1c6d6de4e6d]
where the guid is randomly generated at every run.
Is there any way to solve this issue?
Thank you.
There is no way to do what I asked. But we found a walkaround. "Simply" embedding the xaml windows within a windows form.
These are the steps we followed:
1 - Add a Windows Form to the project.
2 - Remove the app.xaml and make the new form the entry point of the application.
3 - Since we need the hwnd of the main.xaml we added this prop to its code behind
public IntPtr Hwnd
{
get { return new WindowInteropHelper(this).Handle; }
}
4 - then from the constructor of the form we create an instance of the wpf window class
private Main app;
public ContainerForm()
{
InitializeComponent();
app = new Main();
ElementHost.EnableModelessKeyboardInterop(app);
}
we needed
ElementHost.EnableModelessKeyboardInterop(app);
since we want all the keyboard input to pass from the the windows form to the xaml window
5 - now we want to bond the xpf window to the winform. In order to do that we need to use Windows Api and we do it at the OnShow event of the form (the reason why will be explicated later).
[DllImport("user32.dll", SetLastError = true)]
private static extern long SetFocus(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("user32.dll", EntryPoint = "SetWindowLongA", SetLastError = true)]
private static extern long SetWindowLong(IntPtr hwnd, int nIndex, long dwNewLong);
private const int GWL_STYLE = (-16);
private const int WS_VISIBLE = 0x10000000;
private void ContainerForm_Shown(object sender, EventArgs e)
{
app.Show();
SetParent(app.Hwnd, this.Handle);
SetWindowLong(app.Hwnd, GWL_STYLE, WS_VISIBLE);
MoveWindow(app.Hwnd, 0, 0, this.Width, this.Height, true);
SetFocus(app.Hwnd);
}
with
SetParent(app.Hwnd, this.Handle);
wo do the magic, then with
SetWindowLong(app.Hwnd, GWL_STYLE, WS_VISIBLE);
we remove al the chrome from the wpf window (there is a border even if the window is defined borderless, don't ask me why)
then we make the wpf window fill all the client area of the winform
MoveWindow(app.Hwnd, 0, 0, this.Width, this.Height, true);
and then we focus the wpf window
SetFocus(app.Hwnd);
that's the reason why we do everything in the show event. Since if we do it at form's constructor, then the wpf window will loose its focus since the in winform the main window got the focus from the operating system.
We didn't understand why we needed to add the other api calls at this point, but if we left them at constructor's the trick didn't work.
Anyway,
problem solved ;)
Use HwndSource.
You can use native Windows API calls to create a window with the expected classname, then use HwndSource to add WPF content to it:
var source = HwndSource.FromHwnd(nativeWindowHandle);
source.RootVisual = mainGrid;
If you need to use a WPF window, I think you could still solve this with a "proxy" window, but it wouldn't be pretty:
Have your WPF application spawn a native message-only window.
Use HwndSource.AddHook to handle messages like WM_CLOSE, WM_SIZE on the native window and pass them along to the "real" WPF window.
For the window handles, titles, and class names, Spy++ uses fairly simple Windows APIs.
FindWindowEx http://msdn.microsoft.com/en-us/library/windows/desktop/ms633500%28v=vs.85%29.aspx
EnumWindows http://msdn.microsoft.com/en-us/library/windows/desktop/ms633497%28v=vs.85%29.aspx
GetClassName http://msdn.microsoft.com/en-us/library/windows/desktop/ms633582%28v=vs.85%29.aspx
You can create a "loader" program that will...
Start the wpf app
Use the above APIs to get the proper class names and windows handles
Edit the legacy INI
Start the legacy app
One of our customers hosts our WinForms .NET grid control iGrid.NET (http://www.10tec.com/) inside a WPF ElementHost container together with other WPF controls. It may look strange as it's a WinForms control inside a WPF host inside a WinForms form, but they have no choice because of the other WPF stuff they use (it's the AvalonDock http://avalondock.codeplex.com/ docking container).
The problem is that our .NET datagrid control's infrastructure requires to know the parent WinForms form, but the following construction we use for that always return null in this situation:
Form myTopLevelOwnerForm = fCurrentGrid.TopLevelControl as Form;
I.e. the standard Control.TopLevelControl property intended for this purpose returns null - though most likely it should be so in the case of WPF host.
The question is: are there other ways to know the parent form from the current control's code? Say, using WinAPI handles or better other native .NET memebrs?
The following code works. At least, in our project :)
// API declaration
[System.Runtime.InteropServices.DllImport("user32.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
public static extern IntPtr GetParent(IntPtr hWnd);
// Main code snippet
Control myTopLevelControl = fOwner.TopLevelControl;
if (myTopLevelControl == null)
{
IntPtr handle = fOwner.Handle;
while (true)
{
IntPtr parentHandle = GetParent(handle);
if (parentHandle == IntPtr.Zero)
{
myTopLevelControl = Control.FromHandle(handle) as Form;
break;
}
handle = parentHandle;
}
}
I've heard (well read, at http://www.ddj.com/windows/184416861), that it's possible to set custom properties on a Win32 window.
The trouble is, the download for the article above is on an ftp server that won't let me in.
As a bit of background info, I have a .NET app. The main window is registered to handle custom window messages. From a separate app, I need to post messages to this window. I can't find the window by caption as the caption changes. I can't find it JUST by window class, as the window class is the same for all forms in that app domain.
Ideally, I'd like to set a custom property on the Win32 window of the main form (Form1) that say, yes, this is form1. Then when I'm enumerating the windows of this app, I can tell that this is the required form by seeing if this custom property exists.
Cheers,
Steve
See here for an overview of window properties. Basically, you call the Win32 API function SetProp to set a window property and GetProp to retrieve it. There are a few more functions for enumerating properties and the like, but it sounds as if SetProp and GetProp is all you need.
As Martin says, the answer is the Win32 APIs GetProp and SetProp.
Here's what I now do when I create the main form:
[DllImport("user32.dll", SetLastError=true)]
static extern bool SetProp(IntPtr hWnd, string lpString, IntPtr hData);
SetProp( this.Handle, #"foo", new IntPtr( 1 ) ) ;
Now, I can check this property when enumerating the windows:
[DllImport("user32.dll")]
private static extern IntPtr GetProp(IntPtr hWnd, string lpString);
IntPtr result = GetProp( (IntPtr) hWnd, #"foo" ) ;
I am trying to use this function from a COM API which enables the window to receive drops (as in drag & dop) from another application.
It is pretty straightforward in Windows Forms and works:
public void EnableDropSupport(System.Windows.Forms.Form form)
{
IntPtr hwnd = form.Handle;
_comAPI.RegisterDropWindow((int)hwnd);
}
But I have a WPF window where it does not work and I don't understand why. I have tried the following:
public void EnableDropSupport(System.Windows.Window window)
{
window.AllowDrop = true;
WindowInteropHelper windowInteropHelper = new WindowInteropHelper(window);
IntPtr hwnd = windowInteropHelper.Handle;
_comAPI.RegisterDropWindow((int)hwnd);
}
The last two lines are basically identical but it will just not work in WPF. While
window.AllowDrop = true;
will make it appear as if it will accept the drop, the drop event of that COM API is not raised.
Am I missing something or can someone help?
This is Pavel Minaev's answer (which he posted as a comment to the question) which was correct:
You're not missing anything on WPF
side of things. Most likely the
problem is with RegisterDropWindow.
How can I go about hosting flash content inside a WPF form and still use transparency/alpha on my WPF window? Hosting a WinForms flash controls does not allow this.
Unless the control you use to display the Flash content is built in WPF, you will run in to these "airspace" issues. Every display technology from Win32 to WinForms used HWNDs "under the hood", but WPF uses DirectX. The Window Manager in Windows however, still only understands HWNDs, so WPF apps have one top-level HWND-based window, and everything under that is done in DirectX (actually things like context menus and tooltips also have top-level HWNDs as well). Adam Nathan has a very good description of WPF interop in this article.
Although I haven't done it, you can probably use the WebBrowser control found in WPF 3.5 sp1 to wrap your Flash content within WPF. I'm not sure how the transparency will be affected though.
Can you use Expression to convert the flash content to XAML? I believe that there are tools in there or off to the side that do this.
Just have been struggling with same problem of how to upload & Make WPF transparent with ability of displaying Flash, because if you enable on your MainWindow "Allow transparency" Flash will not show once the application will run.
1) I used WebBrowser Control to play Flash(.swf) files. They are on my PC, however it can play from internet or wherever you have hosted them. Don't forget to name your WebBrowser Control to get to it in C#.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MyHelper.ExtendFrame(this, new Thickness(-1));
this.MyBrowser.Navigate(#"C:\Happy\Download\flash\PlayWithMEGame.swf");
}
2) Now for transparency. I have set in WPF 'false' to "Allow Transparency" and set "Window Style" to 'None'. After that I have used information from HERE and HERE and created a following code that produced desired effect of allowing transparency on MainWindow and running Flash at same time, here is my code:
public class MyHelper
{
public static bool ExtendFrame(Window window, Thickness margin)
{
IntPtr hwnd = new WindowInteropHelper(window).Handle;
window.Background = Brushes.Transparent;
HwndSource.FromHwnd(hwnd).CompositionTarget.BackgroundColor = Colors.Transparent;
MARGINS margins = new MARGINS(margin);
DwmExtendFrameIntoClientArea(hwnd, ref margins);
return true;
}
[DllImport("dwmapi.dll", PreserveSig = false)]
static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);
}
struct MARGINS
{
public MARGINS(Thickness t)
{
Left = (int)t.Left;
Right = (int)t.Right;
Top = (int)t.Top;
Bottom = (int)t.Bottom;
}
public int Left;
public int Right;
public int Top;
public int Bottom;
}
And called it from Window_Loaded() + you need 'below' line for 'DllImport' to work.
using System.Runtime.InteropServices;
using System.Windows.Interop;