Link to home
Start Free TrialLog in
Avatar of Kyle Abrahams, PMP
Kyle Abrahams, PMPFlag for United States of America

asked on

COM - Hide Button in a 3rd Party Windows form

Hi All,

I'm looking for a way to hide a button on a 3rd party Windows form using COM or some other method.  Is there any way to achieve this?

My code is in C# but if I need to use "unsafe" code I have no issues.

Thanks.
Avatar of jkr
jkr
Flag of Germany image

You don't need any special technologies for that - the worst would be to call the Windows API directly. In order to do that, you need the control's HWND, which you can obtain via 'FindWindow()' (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633499(v=vs.85).aspx, see http://www.codeproject.com/Articles/34981/FindWindow on how to call that API from C#)
Once you have the HWND, all you need to do is calling 'ShowWindow()' (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx) passing that HWND and 'SW_HIDE' as the 2nd parameter.
Avatar of Kyle Abrahams, PMP

ASKER

Hi Jkr,

Thanks for the help.  This is a button on the form itself . . . now like the maximize button or anything.

So find Window gets me a reference to the window . . . now I need the control (say a button named "Button1") that's on a form in that window.

Will take a look at the find window for now.  

Much appreciated!
Unless it's a web browser control, that approach should work, managed or unmanaged code. In case it still doesn't, you  can still use a Windows hook that does that for you, but this approach IMO is more straightfoward.
Let's start talking about a windows hook.

The whole ribbon is defined as one window:
User generated image
Nah, that would be the 2nd best solution, for it's sheer complexity (you'd need control IDs etc).

What's the trouble you are having with findong the contro? You'd use 'FindWindow()' to locate the main window, and then 'EnumChildWindows()' to find the control, e.g. like in http://social.msdn.microsoft.com/Forums/vstudio/en-US/8f74e954-f1a2-4f46-8de0-f6c5423bfe46/how-to-correctly-return-a-systemintptr-value-to-enumchildwindows-lparam?forum=csharpgeneral

private static bool EnumWindowsProc(IntPtr hWnd, int lParam)

{

string title = GetWindowText(hWnd)+" "+hWnd.ToString();

if (title.Contains("something"))  //something is the string you are searching for

Whandle = hWnd;  //Whandle is defined in the class, hWnd is the current handle and is stored

mTitlesList.Add(title);

return true;

}

Open in new window

BTW, that's what I'd do:

- use Spy++ or it's managed counterpart (http://msdn.microsoft.com/en-us/magazine/cc163617.aspx) to get the HWND of the control in question
- hard-code the value in your C# app to test if it works
- if it does, extend the whole thing to autmatially obtain the window handle
I really appreciate the responses.  I'm going to need some time disecting.  COM is all new to me, and what to understand completely what's going on.  


For more details on what I'm trying to do:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/c69897ca-4d60-4102-ad60-d5576e0c34a4/appointment-seperate-form-hide-from-showgroup?forum=vsto

Thanks again, will post back after I try a few things.
Well, again - there is no COM involved for the task of hiding a control so far, and I doubt that you will have to use it.
Here's what I have so far.  I'm running into issues now because not all of the windows have titles (fun times).  

I see within the first iteration of appointment that there are some ribbons so I'm diving into those.  Is there a way to get the class?

If this isn't COM . . . I get you're just using an external windows DLL?  I haven't been this low in the windows API before.

Thanks for the help.



using System.Collections.Generic;
using System.Runtime.InteropServices;
using System;
using System.Text;
using System.Collections;

namespace IVCi_Cloud.Classes
{


    /// <summary>
    /// EnumDesktopWindows Demo - shows the caption of all desktop windows.
    /// Authors: Svetlin Nakov, Martin Kulov 
    /// Bulgarian Association of Software Developers - http://www.devbg.org/en/
    /// </summary>
    public class user32
    {
        /// <summary>
        /// filter function
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        public delegate bool EnumDelegate(IntPtr hWnd, int lParam);
        private delegate bool EnumWindowsProc(IntPtr hWnd, ref IntPtr lParam);


        /// <summary>
        /// check if windows visible
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool IsWindowVisible(IntPtr hWnd);

        /// <summary>
        /// return windows text
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="lpWindowText"></param>
        /// <param name="nMaxCount"></param>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "GetWindowText",
        ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount);

        /// <summary>
        /// enumarator on all desktop windows
        /// </summary>
        /// <param name="hDesktop"></param>
        /// <param name="lpEnumCallbackFunction"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "EnumDesktopWindows",
        ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumDelegate lpEnumCallbackFunction, IntPtr lParam);


        [DllImport("user32")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);

        public static List<IntPtr> GetChildWindows(IntPtr parent)
        {
            List<IntPtr> result = new List<IntPtr>();
            GCHandle listHandle = GCHandle.Alloc(result);
            try
            {
                EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
                EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
            }
            finally
            {
                if (listHandle.IsAllocated)
                    listHandle.Free();
            }
            return result;
        }

        private static bool EnumWindow(IntPtr handle, IntPtr pointer)
        {
            GCHandle gch = GCHandle.FromIntPtr(pointer);
            List<IntPtr> list = gch.Target as List<IntPtr>;
            if (list == null)
            {
                throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
            }
            list.Add(handle);
            //  You can modify this to check to see if you want to cancel the operation, then return a null here
            return true;
        }

        /// <summary>
        /// Delegate for the EnumChildWindows method
        /// </summary>
        /// <param name="hWnd">Window handle</param>
        /// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
        /// <returns>True to continue enumerating, false to bail.</returns>
        public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);





        public static IntPtr Whandle;
        public static int i = 0;
        /// <summary>
        /// entry point of the program
        /// </summary>
        public static ArrayList GetAllWindows()
        {
            List<IntPtr> collection = new List<IntPtr>();

            user32.EnumDelegate filter = delegate(IntPtr hWnd, int lParam)
            {
                StringBuilder strbTitle = new StringBuilder(255);
                int nLength = user32.GetWindowText(hWnd, strbTitle, strbTitle.Capacity + 1);
                string strTitle = strbTitle.ToString();

                if (strTitle.Contains("Appointment"))
                    Whandle = hWnd;

                if (user32.IsWindowVisible(hWnd) && string.IsNullOrEmpty(strTitle) == false)
                {
                    collection.Add(hWnd);
                }
                return true;
            };

            EnumedWindow callBackPtr = GetWindowHandle;
            EnumChildWindows(Whandle, callBackPtr, windowHandles);
            i++;
        
            EnumChildWindows(Whandle, callBackPtr, windowHandles);



            return windowHandles;
        }
    
        static void ClearArrays()
        {
            windowHandles.Clear();
            windowTitles.Clear();
        }


        static ArrayList windowHandles = new ArrayList();
        static ArrayList windowTitles = new ArrayList();

        private delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool EnumWindows(EnumedWindow lpEnumFunc, ArrayList lParam);

        [DllImport("user32")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool EnumChildWindows(IntPtr window, EnumedWindow callback, ArrayList lParam);

        private static bool GetWindowHandle(IntPtr windowHandle, ArrayList windowHandles)
        {
            StringBuilder strbTitle = new StringBuilder(255);
            int nLength = user32.GetWindowText(windowHandle, strbTitle, strbTitle.Capacity + 1);
            string strTitle = strbTitle.ToString();

            if (strTitle.Contains("Appointment") && i == 0)
            {
                windowHandles.Add(windowHandle);
                windowTitles.Add(strTitle);
                Whandle = windowHandle;
            }
            else if (i==1)
            {
                windowHandles.Add(windowHandle);
                windowTitles.Add(strTitle);
            }

            return true;
        }





    }




}

Open in new window

>>I get you're just using an external windows DLL?

Yup, that's called 'P/Invoke', which is used to allow .NET to directly call the Windows API. The other way around is 'COM/Interop", and that one is used by unmanaged code to use managed components, with the latter acting as COM servers. And the code that you posted seems to be a nice wrapper around these API calls.

But anyway, I'd still first try to check out if that whole aproach works for you by testing that the way I suggested above.
The find window isn't working for me:

FindWindow("NetUIHWND", null);  

is returning 0.
So 'NetUIHWND' is the window title? Then you need to call it like

FindWindow(null, "NetUIHWND");  

Open in new window


But, again, I would first:

- use Spy++ or it's managed counterpart (http://msdn.microsoft.com/en-us/magazine/cc163617.aspx) to get the HWND of the control in question
- hard-code the value in your C# app to test if it works

It does not help a lot if you can find the control just to notice that hiding it does not work.
NetUIHWND is the class, not the window title.

I've tried to use ManagedSpy, but running into an error:
System.BadImageFormatException: Could not load file or assembly 'C:\WINDOWS\assembly\NativeImages_v4.0.30319_32\mscorlib\cf58670896c5313b9b52f026f4455a5d\mscorlib.ni.dll' or one of its dependencies. The module was expected to contain an assembly manifest.
File name: 'C:\WINDOWS\assembly\NativeImages_v4.0.30319_32\mscorlib\cf58670896c5313b9b52f026f4455a5d\mscorlib.ni.dll'
   at System.Reflection.AssemblyName.nGetFileInformation(String s)

I have .Net v1 - v4 all installed on my machine.
Do you happen to have a 'Professional' version of VS? Because in that case, Spy++ should be installed in the 'Tools' folder.
Premium.
Then you should have it ;o)

If it's still not there, check your installation options or use try one of these surrogates:

http://www.codeproject.com/Articles/33459/Spying-Window-Messages-from-the-Inside
http://www.codeproject.com/Articles/1698/MS-Spy-style-Window-Finder
Hi jkr,

I was  looking in the program files folder not the actual tools in VS for it.

Here are some screen shots:

I put the cursor where the red dot is (essentially over the appointment button).  The handle returns that whole section . . . there are no drill downs underneath of it.
User generated image
I then captured some logs.  It looks like it's using some kind of pass through based on the mouse location?
User generated image

I've highlighted the mousedown / up section for you.


In addition here's the control layout that I'm seeing:
User generated image
Any suggestions greatly appreciated.
I start seeing the problem - and I don't like what I see :-/
The 'Afx' in the name does not make things easier, since it plainly means that it is a MFC control, which rules out the majority of other options (like subclassing the ribbon control, which would be hard enough anyway. Let me put my thinking cap on and get back to you...
Thanks man.  I started seeing what you were talking about other apps where the button is it's own window.  Wish it were that easy, lol.
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
It's an attempt at a solution at least.

I don't know if it's going to work because this is for a VSTO plugin.  That Ribbon is dynamically generated and also is displayed differently depending on the size of the window.  

Eg - My previous screen shots vs:
User generated image

Too bad there's not a hook when it loads this DLL to say pain the icon or not.  I'm not sure if it's worth the time at this point, but I may return to it later.  Will leave this open for a few days if you think of another way.  If not I'll close and Re-open specific questions as I approach it.

I knew there was a way, figured it was going to be a hell of hack, but if it were easy, everyone would be doing it.  Microsoft said it couldn't be done, so one step ahead.
At that level, all that matters is "window or not" - and since it is one, this would work. VSTO or not, in the end, everything ends up calling the Windows API after all.

Size and position can be an issue in terms of 'trickyness', but since a window's size is avaiable as well and the offset can be calculated, it is indeed feasible. Yet not simple.
The premise is sound but at this point I don't think we're going to implement.  Will post follow-up questions if we ever get to that point.  Thanks for the expertise.