views:

436

answers:

3

Hey everyone,

I am writing a .Net COM DLL that runs inside a native Windows application.

I am attempting to inject an additional pane into this app's statusbar, and it does not have any specific implementation to do so, so I am trying to subclass the app's statusbar myself.

I am using the Win32 API SetParent() to switch the parent of a label control from a .Net form to the msctls_statusbar32 instance. I used a label because it is the closest implementation to a native "static" class control that I could find without writing my own control.

Somehow I've even managed to get NativeWindow to successfully hook in to both the statusbar and my label's messages (though at the moment it just passes them all to the next WndProc), and I've assigned matching styles and styleExs to my label's window, and I can see my label as a child with the msctls_statusbar32 as its parent. Everything looks like it should be working correctly, but it's not. My control does not show up in the parent app's statusbar.

What I don't understand is why it is not showing up. Nearly everything I can think of matches correctly -- granted, the class for my label is "WindowsForms10.STATIC.app.0.378734a" and not "static", but other than that it is on the correct process and thread, has matching window styles (at least the hex value... Spy++ seems to enumerate them differently), and for all purposes pretty much blends in with the rest of the controls. Would anybody know what else needs to be done to get it to be visible?

(I had originally gone the route of CreateWindowEx and setting WNDPROC callbacks but I could not get the app to work... it would freeze for a minute or so and then unfreeze, and I would notice my window disappeared from the window tree)

Thank you!

A: 

Many possible reasons:

  1. The .Net Label control blows up when it finds it does not have a WinForms parent.
  2. The native Status bar is drawing over the label control becuase of incorrect Z-Order.
  3. The Label control is not visible.
logicnp
+2  A: 

you can try working with existing status bar control; what you can do is to reset text to an existing section of it or add a new one; also you would probably need to set up new widths to existing sections of the statusbar. You can find details on how to work with statusbar control here:msdn Status Bars

Please find an example of how you could do it below. I actually tried it with c# com object used by win32 application and it seem to work fine for me.

    [ComVisible(true)]
    [Guid("CC5B405F-F3CD-417E-AA00-4638A12A2E94"),
    ClassInterface(ClassInterfaceType.None)]
    public class TestInterface : ITestInterface // see declaration of the interface below
    {
        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, IntPtr lParam);

        public const int SB_SETTEXT = 1035;
        public const int SB_SETPARTS = 1028;
        public const int SB_GETPARTS = 1030;

        public unsafe void Test()
        {
            IntPtr mainWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
            // find status bar control on the main window of the application
            IntPtr statusBarHandle = FindWindowEx(mainWindowHandle, IntPtr.Zero, "msctls_statusbar32", IntPtr.Zero);
            if (statusBarHandle != IntPtr.Zero)
            {
                // set text for the existing part with index 0
                IntPtr text = Marshal.StringToHGlobalAuto("test text 0");
                SendMessage(statusBarHandle, SB_SETTEXT, 0, text);
                Marshal.FreeHGlobal(text);

                // create new parts width array
                int nParts = SendMessage(statusBarHandle, SB_GETPARTS, 0, IntPtr.Zero).ToInt32();
                nParts++;
                IntPtr memPtr = Marshal.AllocHGlobal(sizeof(int) * nParts);
                int partWidth = 100; // set parts width according to the form size
                for (int i = 0; i < nParts; i++)
                {
                    Marshal.WriteInt32(memPtr, i*sizeof(int), partWidth);
                    partWidth += partWidth;
                }
                SendMessage(statusBarHandle, SB_SETPARTS, nParts, memPtr);
                Marshal.FreeHGlobal(memPtr);

                // set text for the new part
                IntPtr text0 = Marshal.StringToHGlobalAuto("new section text 1");
                SendMessage(statusBarHandle, SB_SETTEXT, nParts-1, text0);
                Marshal.FreeHGlobal(text0);
            }
        }
    }

    [ComVisible(true)]
    [Guid("694C1820-04B6-4988-928F-FD858B95C880")]
    public interface ITestInterface
    {
       [DispId(1)]
       void Test();
    }

hope this helps, regards

serge_gubenko
A: 

As it turns out the answer was dumbfoundingly simple... the label's X and Y coords were out of the display area of the statusbar parent. Moving them to (0, 0) and it shows up right there! Of course, now the problems have moved on to: http://stackoverflow.com/questions/1938995/c-winforms-control-in-net-com-server-wont-redraw

Tom the Junglist