Avatar of Kyle Abrahams
Kyle Abrahams
Flag 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.
C#C++.NET Programming

Avatar of undefined
Last Comment
Kyle Abrahams

8/22/2022 - Mon
jkr

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.
Kyle Abrahams

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!
jkr

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.
All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck
Kyle Abrahams

ASKER
Let's start talking about a windows hook.

The whole ribbon is defined as one window:
Ribbon
jkr

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

jkr

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
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
Kyle Abrahams

ASKER
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.
jkr

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.
Kyle Abrahams

ASKER
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

Your help has saved me hundreds of hours of internet surfing.
fblack61
jkr

>>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.
Kyle Abrahams

ASKER
The find window isn't working for me:

FindWindow("NetUIHWND", null);  

is returning 0.
jkr

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.
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
Kyle Abrahams

ASKER
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.
jkr

Do you happen to have a 'Professional' version of VS? Because in that case, Spy++ should be installed in the 'Tools' folder.
Kyle Abrahams

ASKER
Premium.
Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
jkr

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
Kyle Abrahams

ASKER
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.
Spy Window Search
I then captured some logs.  It looks like it's using some kind of pass through based on the mouse location?
spylog

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


In addition here's the control layout that I'm seeing:
Form layout
Any suggestions greatly appreciated.
jkr

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...
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
Kyle Abrahams

ASKER
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
jkr

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
Kyle Abrahams

ASKER
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:
Form Repainted

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.
jkr

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.
Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
Kyle Abrahams

ASKER
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.