Can't get a window handle?

被刻印的时光 ゝ 提交于 2019-12-23 23:09:07

问题


I've searched all over to try and find an answer to my predicament but cannot seem to find a valid answer. I'm trying to write some equivalent user32.dll code with Xlib instead so I can support Linux users. I'm of course running Linux, so I'm using Mono. Problem comes along when I cannot even grab a window handle from the Process class because it was never even implemented:

[MonoTODO]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[MonitoringDescription ("The handle of the main window of the process.")]
public IntPtr MainWindowHandle {
    get {
        return((IntPtr)0);
    }
}

(Source)

This is especially frustrating because there is seemingly no alternative. I'm trying to grab a window handle like so:

[DllImport("libX11")]
private static extern IntPtr XOpenDisplay(IntPtr display);

[DllImport("libX11")]
private static extern int XRaiseWindow(IntPtr display, IntPtr window);

private IntPtr ApplicationHandle;
private IntPtr Display;

private void TestXlib() {
    Process process = Process.GetProcessById(myPid);

    ApplicationHandle = process.MainWindowHandle;

    Display = XOpenDisplay(IntPtr.Zero);

    XRaiseWindow(Display, ApplicationHandle);
}

NOTE: In place of "myPid" is a proper process ID. Replace "myPid" with a valid process ID. Yes, I did make sure the replaced "myPid" was a valid process ID and my code didn't throw any errors indicating any process IDs I used as invalid.

This doesn't crash my application, but almost every time I call XRaiseWindow it prints:

X11 Error encountered: 
  Error: BadWindow (invalid Window parameter)
  Request:     12 (0)
  Resource ID: 0x0
  Serial:      121
  Hwnd:        <null>
  Control:     <null>

This obviously occurs because Process.MainWindowHandle returns IntPtr.Zero. Is there no other way to get a window handle? Thanks in advance!


回答1:


Yes, I know this was forever ago that I asked this, but I'm answering it now because I kept forgetting to answer it after I found the solution myself. I originally used @SushiHangover's solution but didn't really like it because I felt relying on an external program(xwininfo) was a hotfix and ultimately just added another dependency. Hopefully this helps other C# developers using Mono. This code was originally written for .NET Framework 2.0. It's not fancy and isn't really documented well. My solution was just to natively enumerate the windows using Xlib myself and return all windows whose title's match the described title.

In X11Wrapper.cs:

using System;
using System.Runtime.InteropServices;

namespace Program.PInvoke.Xlib {

    public static class X11Wrapper {

        public const string SOName = "libX11.so";

        [DllImport(SOName)]
        // See: https://tronche.com/gui/x/xlib/display/display-macros.html#DefaultRootWindow
        public static extern IntPtr XDefaultRootWindow(IntPtr display);

        [DllImport(SOName)]
        // See: https://tronche.com/gui/x/xlib/window-information/XQueryTree.html
        public static extern int XQueryTree(IntPtr display, IntPtr w,
                                            out IntPtr root_return, out IntPtr parent_return,
                                            out IntPtr[] children_return, out int nchildren_return);

        [DllImport(SOName)]
        // See: https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XFetchName.html
        public static extern int XFetchName(IntPtr display, IntPtr w,
                                            out string window_name_return);
    }
}

In Linux.Utilities.cs:

using Program.PInvoke.Xlib;

namespace Program {

    public static partial class Utilities {

        public static bool IsUnix {
            get {
                return Environment.OSVersion.
                       Platform == PlatformID.Unix;
            }
        }

        private static IntPtr[] FindChildWindows(IntPtr display, IntPtr window,
                                                 string title, ref List<IntPtr> windows) {
            IntPtr rootWindow;
            IntPtr parentWindow;

            IntPtr[] childWindows = new IntPtr[0];

            int childWindowsLength;

            X11Wrapper.XQueryTree(display, window,
                                  out rootWindow, out parentWindow,
                                  out childWindows, out childWindowsLength);

            childWindows = new IntPtr[childWindowsLength];

            X11Wrapper.XQueryTree(display, window,
                                  out rootWindow, out parentWindow,
                                  out childWindows, out childWindowsLength);

            string windowFetchedTitle;

            X11Wrapper.XFetchName(display, window, out windowFetchedTitle);

            if(title == windowFetchedTitle &&
               !windows.Contains(window)) {
                windows.Add(window);
            }

            for(int childWindowsIndexer = 0;
                childWindowsIndexer < childWindows.Length;
                childWindowsIndexer++) {
                IntPtr childWindow = childWindows[childWindowsIndexer];

                string childWindowFetchedTitle;

                X11Wrapper.XFetchName(display, childWindow,
                                      out childWindowFetchedTitle);

                if(title == childWindowFetchedTitle &&
                   !windows.Contains(childWindow)) {
                    windows.Add(childWindow);
                }

                FindChildWindows(display, childWindow, title, ref windows);
            }

            windows.TrimExcess();

            return windows.ToArray();
        }

        public static IntPtr[] FindWindows(IntPtr display, string title) {
            List<IntPtr> windows = new List<IntPtr>();

            return FindChildWindows(display,
                                    X11Wrapper.XDefaultRootWindow(display),
                                    title,
                                    ref windows);
        }
    }
}

Footnote: I initially stated I wasn't a C developer(Things have changed since then and I've learned C) so I was hesitant to implement the functionality myself using interop. If you do end up using Xlib a lot more like I did then consider using tronche as an Xlib API reference. It is in C but I found it was pretty easy to translate to PInvokable functions and marshable structs in C#. Has some good notes to take into account too. Another good resource to help translation is directly using the source to find the definitions of the low level types to help find C# equivalents. Something like this should greatly aid you: http://refspecs.linuxbase.org/LSB_4.0.0/LSB-Desktop-generic/LSB-Desktop-generic/libx11-ddefs.html



来源:https://stackoverflow.com/questions/42449050/cant-get-a-window-handle

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!