Search code examples
c#windowswindow-handles

Is it possible to set another application's window's maximum width from its window handle?


I currently have an IntPtr of a window handle, and I tried getting its Window using HwndSource.FromHwnd but it returns null. If the Window element was retrieved, it would have been possible to set its MaxWidth attribute.

Are there other ways to set the maximum width just from having a window handle of an external application?

EDIT: Trying to see if RbMm's approach works. The question's tagged C# but this could be worth a shot using a C++ custom DLL:

bool InitializeMaxWidthHook(int threadID, HWND destination)
{
    if (g_appInstance == NULL)
    {
        return false;
    }

    SetProp(GetDesktopWindow(), "WILSON_HOOK_HCBT_MINMAX", destination);

    hookMaxWidth = SetWindowsHookEx(WH_CBT, (HOOKPROC)MinMaxHookCallback, g_appInstance, threadID);
    return hookMaxWidth != NULL;
}

void UninitializeMaxWidthHook()
{
    if (hookMaxWidth != NULL)
        UnhookWindowsHookEx(hookMaxWidth);
    hookMaxWidth = NULL;
}

static LRESULT CALLBACK MinMaxHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
    if (code >= 0)
    {
        UINT msg = 0;

        if (code == HCBT_MINMAX)
            msg = RegisterWindowMessage("WILSON_HOOK_HCBT_MINMAX");

        HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "WILSON_HOOK_HCBT_MINMAX");

        if (msg != 0)
            SendNotifyMessage(dstWnd, msg, wparam, lparam);
    }

    return CallNextHookEx(hookMaxWidth, code, wparam, lparam);
}

I'll update the question again after tinkering with this.


Solution

  • The solution I came up with isn't purely C#, so this doesn't perfectly fit the question tags, but I'll post it anyway.

    I created a third-party DLL with initialization functions for WndProc hook to listen to GETMINMAXINFO (I removed some parts of the code for privacy, but this is the one for GETMINMAXINFO:

    #include "stdafx.h"
    #include <windows.h>
    
    HINSTANCE g_appInstance = NULL;
    typedef void (CALLBACK *HookProc)(int code, WPARAM w, LPARAM l);
    
    HHOOK hookWndProc = NULL;
    static LRESULT CALLBACK WndProcHookCallback(int code, WPARAM wparam, LPARAM lparam);
    
    bool InitializeWndProcHook(int threadID, HWND destination)
    {
        if (g_appInstance == NULL)
        {
            return false;
        }
    
        SetProp(GetDesktopWindow(), "WILSON_HOOK_HWND_WND_PROC", destination);
    
        hookWndProc = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)WndProcHookCallback, g_appInstance, threadID);
        return hookWndProc != NULL;
    }
    
    void UninitializeWndProcHook()
    {
        if (hookWndProc != NULL)
            UnhookWindowsHookEx(hookWndProc);
        hookWndProc = NULL;
    }
    
    static LRESULT CALLBACK WndProcHookCallback(int code, WPARAM wparam, LPARAM lparam)
    {
        if (code >= 0)
        {
            UINT msg = 0;
    
            WPARAM newWPARAM;
            LPARAM newLPARAM;
            if (code == HC_ACTION)
            {
                CWPSTRUCT *wndProc = (CWPSTRUCT *)lparam;           
                if (shouldSendMessageOnWindow(wndProc->hwnd))
                {
                    // if the message is from other process, the value of wparam is null
                    UINT wndProcMessage = wndProc->message;
                    if (wndProcMessage == WM_GETMINMAXINFO)
                    {
                        newWPARAM = (WPARAM)wndProc->hwnd;
                        newLPARAM = (LPARAM)wndProc->lParam;
                        msg = RegisterWindowMessage("WILSON_HOOK_HC_ACTION_WND_PROC_WM_GETMINMAXINFO");
                    }
                }
            }
    
            if (msg != 0)
            {
                HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "WILSON_HOOK_HWND_WND_PROC");
                SendNotifyMessage(dstWnd, msg, newWPARAM, newLPARAM);
            }
        }
    
        return CallNextHookEx(hookWndProc, code, wparam, lparam);
    }
    

    I then initialized this hook in my C# code and waited for the MINMAX message then set the info to the size I wanted.

    private struct POINT
    {
        public int x;
        public int y;
    }
    
    private struct MINMAXINFO
    {
        public POINT ptReserved;
        public POINT ptMaxSize;
        public POINT ptMaxPosition;
        public POINT ptMinTrackSize;
        public POINT ptMaxTrackSize;
    }
    
    /**Other Code**/
    
    MINMAXINFO* minMaxInfo = (MINMAXINFO*) lParam;
    
    int currentMaxSize = minMaxInfo->ptMaxSize.x;
    Debug.WriteLine("Updated max" + currentMaxSize);
    
    minMaxInfo->ptMaxSize.x = screenWidth; 
    minMaxInfo->ptMaxSize.y = screenHeight;
    
    User32.SetWindowPos(wParam, User32.SpecialWindowHandles.HWND_TOP, 0, 0, screenWidth, screenHeight, User32.SetWindowPosFlags.SWP_DRAWFRAME | User32.SetWindowPosFlags.SWP_ASYNCWINDOWPOS);
    
    /**Other Code**/
    

    This worked for some applications (I think for Win32 apps only). Some applications such as Metro Apps and possibly Java applets can't be accessed.

    But for now, this is probably good for starters.

    Thanks to Hans Passant and RbMm for guiding me to this answer.