Control.FromHandle for Compact Framework

  • Thread starter Thread starter jhow
  • Start date Start date
J

jhow

I am trying to find a way to create a .NET form or control from a
window outside my own application (I know the handle of the window).
The desktop way would be to simply call Control.FromHandle, but this
method does not exist in the Compact Framework.

Are there any workarounds for this?
 
What do you want to *do* with it? That should control what you do next...

Paul T.
 
I have a small popup window in my application that I would like to
make a child of the startbar. I believe if I do this, it will stop the
window being hidden by the startbar.

As a result, I would like to get the handle of the startbar, create a
control from it and set it as the Owner of my popup.
 
Sounds like a kludge to me, but you don't need Control.FromHandle for that
anyway. You're P/Invoking to find the Start bar's handle, just P/Invoke
SetParent to reparent your dialog.


--

Chris Tacke, Embedded MVP
OpenNETCF Consulting
Giving back to the embedded community
http://community.OpenNETCF.com
 
Thanks Chris, yes it is a kludge, but I've racked my brains over this
one and it's the only way I can think of. Using the Topmost property
only works until some other window such as the Startbar bumps it off
the top of the z-order. Do you have any better ideas?

I tried your SetParent solution but this fails for some reason. Any
ideas why?

[DllImport("Coredll.dll", EntryPoint="FindWindow",
CharSet=CharSet.Unicode)]
public static extern IntPtr FindWindow(string _WindowClassName, string
_WindowName);

[DllImport("Coredll.dll", EntryPoint="SetParent", CharSet =
CharSet.Unicode)]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr
hWndParent);

_pickerForm = new PickerForm();

//not necessary, but just in case
_pickerForm.Owner = null;

//returns a valid handle
IntPtr handle = FindWindow("HHTaskBar", null);

//shows "0" indicating failure
MessageBox.Show(SetParent(_pickerForm.Handle, handle).ToString());

//even this.Handle doesn't work
MessageBox.Show(SetParent(_pickerForm.Handle, this.Handle).ToString
());
 
I don't believe that this is going to be possible. You've got a process
boundary there that should not be broken and that I'm certain is not
designed to be broken by the .NET CF. Here's what I'd do:

Forget about managed code and try this in native code. If you can't make
that work (and that would not surprise me one bit), trying to do it from
managed code is a waste of time.

If it doesn't work, think about other ways that you might achieve what you
need. You need a pop up window of some sort associated with the taskbar,
right? What about a notification balloon associated with an icon in the
system tray? This breaks through the process barrier and has the shell
present what you give it and then sends you messages associated with user
actions. It's also supported...

Paul T.
 
A notification window is not quite what I'm looking for and I know
what I am trying to do is possible, because I've seen it done in at
least 3 other applications before. It is quite a common feature in
dictionary applications.

What I'm looking for is something like this:
http://www.pda4x.com/attachmentes/Day_080509/65_73680_db2b3_pxdxa.Screenshot_2.jpg

or the same popup dragged to a different position on the startbar
here:
http://wronek.wdfiles.com/local--files/mdict/mdict.JPG

If you look at the start bar on the first image, you will see a small
popup window (cum button) (aka picker window) to the right of the
title "MDict". This popup can be freely dragged about anywhere in the
startbar region, and, despite what happens to the start bar, it always
stays "on top".

Other dictionary applications allow their picker windows to be dragged
about anywhere within the entire screen area and also manage to always
stay topmost.

I'm just puzzled as to how they do it. Do these images and this
explanation shed any light to you on a possible solution?
 
It looks to me like there's either a system tray item being added or just a
front-most window that gets pushed to the top of the stack all the time. If
you just say, "I need a floating window to always be on top of everything,
even the start menu.", the first thing that comes to mind is "topmost". Of
course, other topmost windows will try to come to the front when you select
them, but I don't see any reason why you couldn't push yourself back on top
as soon as you detect that.

Paul T.
 
Hi,

jhow said:
A notification window is not quite what I'm looking for and I know
what I am trying to do is possible, because I've seen it done in at
least 3 other applications before. It is quite a common feature in
dictionary applications.

They probably are doing this by reparenting their window to the window which
represents the navbar.

Within C# / .NET CF it's all a bit of a hack (and I wouldn't really even
recommend doing it at the native level...) but if you follow these
instructions hopefully it's something close to what you want...

Start off by adding a new form to your project (I called it "IconForm"), and
set the following properties.

FormBorderStyle = none
MaximizeBox = false
ControlBox = false
Size = 24,24

Also make sure you delete the MainMenu instance.

Then add the following class to your project

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

public static class FormExtensions
{
public static void ReparentToNavBar(this Form form, Point location)
{
IntPtr hWndParent = FindWindow("HHTaskBar", null);
Reparent(form, hWndParent, location);
}

// Reparents "form" to be a child of "hWndParent" positioned
// using the x,y co-ordinates specified by "location".
public static void Reparent(this Form form, IntPtr hWndParent, Point
location)
{
IntPtr hWndChild = form.Handle;

// Remove the WS_POPUP window style and
// add the WS_CHILD window style
uint style = GetWindowLong(hWndChild, GWL_STYLE);
style &= ~WS_POPUP;
style |= WS_CHILD;
SetWindowLong(hWndChild, GWL_STYLE, style);

// Reparent the window
SetParent(hWndChild, hWndParent);

// Adjust the positioning of the form
form.Location = location;
}

private const uint WS_POPUP = 0x80000000;
private const uint WS_CHILD = 0x40000000;

private const uint WS_EX_TOPMOST = 0x00000008;

private const int GWL_STYLE = -16;
private const int GWL_EXSTYLE = -20;

[DllImport("coredll.dll")]
private static extern uint GetWindowLong(IntPtr hWnd, int nIndex);

[DllImport("coredll.dll")]
private static extern uint SetWindowLong(IntPtr hWnd, int nIndex, uint
dwNewLong);

[DllImport("coredll.dll")]
private static extern IntPtr FindWindow(string lpClassName, string
lpWindowName);

[DllImport("coredll.dll")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr
hWndNewParent);
}

Once this has been done, you should be able to add a button click event
handler or something similiar to invoke the code to add your custom window
to the nav bar. A code snippet similiar to the following should be
sufficient:

Form f = new IconForm(); // or what ever you named your custom form
f.Show();
f.ReparentToNavBar(new Point(45, 0));

And to remove it, you would simply execute

f.Close();
f.Dispose();

You should see that this window appears on the top of your screen, and
should be fairly visible throughout most applications (except those which
purposely hide the navbar). Its rather hacky though and potentially prone to
breaking.

Hope this helps,
Christopher Fairbairn
 
You are right Christopher. I did a Remote Spy on my mobile device and
found that the MDict dictionary picker is being attached as a child to
the navbar. (I've noticed another app (Magic Button) has also attached
itself there, so it seems it's not such an uncommon practice).

Another problem has arisen though. I would like my application, to sit
on top of all the other children on the navbar. The MDict dictionary
picker, as soon as it is turned on, always manages to get to the top
of the z-order. I can see this visually on my mobile screen as well as
in the order it is listed in Remote Spy.

I copied and tweaked this code from pinvoke.net but can't seem to get
it to work:

static public class FormHelper2
{
[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);

public const int SWP_NOMOVE = 0x0002;
public const int SWP_NOSIZE = 0x0001;

public const int HWND_TOPMOST = -1;

public static void MakeTopMost(Form form)
{
//THIS RETURNS TRUE
bool b = SetWindowPos(form.Handle, (IntPtr)HWND_TOPMOST, 0, 0,
0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
}
 
You are right Christopher. I did a Remote Spy on my mobile device and
found that the MDict dictionary picker is being attached as a child to
the navbar. (I've noticed another app (Magic Button) has also attached
itself there, so it seems it's not such an uncommon practice).

As a result I used the code you kindly provided and it does work.
Thanks for that.

But another problem has arisen. I would like my application, to sit on
top of all the other children on the navbar. The MDict dictionary
picker, as soon as it is turned on, always manages to get to the top
of the z-order. I can see this visually on my mobile screen as well as
in the order it is listed in Remote Spy and would like to do this for
my app as well.

I copied and tweaked this code from pinvoke.net, but can't seem to get
it to work. The SetWindowPos() method below returns true, but the
window behaviour on my mobile screen and Remote Spy both indicate that
my window is still at the bottom of the Z order. Any ideas?

(By the way, the extended style for my window is WS_EX_TOPMOST).

static public class FormHelper2
{
[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);

public const int SWP_NOMOVE = 0x0002;
public const int SWP_NOSIZE = 0x0001;

public const int HWND_TOPMOST = -1;

public static void MakeTopMost(Form form)
{
bool b = SetWindowPos(form.Handle, (IntPtr)HWND_TOPMOST, 0, 0,
0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
}
 
I'd be inclined to use Spy++ and actually look to see what the handle
relationships actually are instead of guessing based on behavior. It's a
lot more reliable.


--

Chris Tacke, Embedded MVP
OpenNETCF Consulting
Giving back to the embedded community
http://community.OpenNETCF.com
 
Changes in frontmost order should appear.  Again, Spy++...

Paul T.

I mean how would I detect it programmatically?

Anyone have any suggestions as to why my SetWindowPos(form.Handle,
(IntPtr)HWND_TOPMOST, 0, 0,
0, 0, SWP_NOMOVE | SWP_NOSIZE); mentioned above is not working?
Unfortunately this is returning a true value indicating success, so
that would mean GetLastError is irrelevant.

However the behaviour, and Spy++ shows that the method is not working.
In Spy++ my window still sits at the bottom of the z-order of the
Navbar's children, and visually, it is getting hidden by the other
children.
 
Look for the messages sent to you when you are no longer frontmost. You'll
get an NCACTIVATE message, indicating that you are not in front any more,
and an ACTIVATE message indicating the same. When you get one of those, you
can adjust yourself to be in front again (although you should probably take
care, in that case, that there aren't a bunch of these windows floating
around or you might get in a war, each trying to force itself to the front
endlessly).

It works fine for me. Perhaps your parameter values are wrong (flags or
HWND_TOPMOST), or you're calling things in the wrong order or something. I
did a FindWindow to find my Windows CE taskbar, a SetParent to set it to the
parent of my window, and a SetWindowPos to make it topmost and it works
exactly as I'd hope. I'm not using Pocket PC, so I can't promise that it
will work the same there, but I would expect it to.

Paul T.

Changes in frontmost order should appear. Again, Spy++...

Paul T.

I mean how would I detect it programmatically?

Anyone have any suggestions as to why my SetWindowPos(form.Handle,
(IntPtr)HWND_TOPMOST, 0, 0,
0, 0, SWP_NOMOVE | SWP_NOSIZE); mentioned above is not working?
Unfortunately this is returning a true value indicating success, so
that would mean GetLastError is irrelevant.

However the behaviour, and Spy++ shows that the method is not working.
In Spy++ my window still sits at the bottom of the z-order of the
Navbar's children, and visually, it is getting hidden by the other
children.
 
Will I really receive NCACTIVATE or ACTIVATE messages when I lose
topmost status?

I thought these messages were only for when a window lose active
status. Often when my window is topmost, it will not be the active
application, and therefore will not be deactivated when it loses its
topmost position.
 
Back
Top