8

ウィンドウの最後の位置が保存される機能を実装する必要があります。アプリケーションの起動時に、この位置を取得して復元する必要があります。

現在、2 台目のモニターが解体されている可能性があります。最後の位置が現在表示されていないモニター上にある場合 (つまり、保存された座標が表示されている座標の外にある場合)、このケースをキャッチし、座標を最後の位置ではなくデフォルトに設定する必要があります。

モニターに関する情報を取得するには、Win32 を使用する必要があります。私がこれを機能させるのは簡単ではありません。

ヘルパー クラスを作成しました。

public static class DisplayHelper
    {
        private const int MONITOR_DEFAULTTONEAREST = 2;

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern int GetSystemMetrics(int nIndex);

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern UInt32 MonitorFromPoint(Point pt, UInt32 dwFlags);

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern bool GetMonitorInfo(UInt32 monitorHandle, ref MonitorInfo mInfo);


        public static void GetMonitorInfoNow(MonitorInfo mi, Point pt)
        {
            UInt32 mh = MonitorFromPoint(pt, 0);
            mi.cbSize = (UInt32)System.Runtime.InteropServices.Marshal.SizeOf(typeof(MonitorInfo));
            mi.dwFlags = 0;
            bool result = GetMonitorInfo(mh, ref mi);

        }
    }

そして、これらは MonitorInfo および Rect クラスを作成する私の試みです:

[StructLayout(LayoutKind.Sequential)]
    public class MonitorInfo
    {
        public UInt32 cbSize;
        public Rectangle2 rcMonitor;
        public Rectangle2 rcWork;
        public UInt32 dwFlags;

        public MonitorInfo()
        {
            rcMonitor = new Rectangle2();
            rcWork = new Rectangle2();

            cbSize = (UInt32)System.Runtime.InteropServices.Marshal.SizeOf(typeof(MonitorInfo));
            dwFlags = 0;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public class Rectangle2
    {
        public UInt64 left;
        public UInt64 top;
        public UInt64 right;
        public UInt64 bottom;

        public Rectangle2()
        {
            left = 0;
            top = 0;
            right = 0;
            bottom = 0;
        }
    }

このコードを次のように使用して、可視モニターを取得しています。

//80 means it counts only visible display monitors.
int lcdNr = DisplayHelper.GetSystemMetrics(80);
var point = new System.Drawing.Point((int) workSpaceWindow.Left, (int) workSpaceWindow.Top);
MonitorInfo monitorInfo = new MonitorInfo();
DisplayHelper.GetMonitorInfoNow(monitorInfo, point);

実行しようとすると、最後のメソッドが例外をスローします

bool result = GetMonitorInfo(mh, ref mi);

これを修正するために何をする必要があるか提案はありますか?

4

4 に答える 4

12

ネイティブ API を呼び出すのではなく、System.Windows.Forms.Screen. 必要なものがすべて揃っていて、使いやすいはずです。

Screen.FromPointオプションGetMonitorInfoNowを使用した関数の管理された同等物です。MONITOR_DEFAULTTONEARESTそのオプションを使用していないことに気付いたので、独自に作成するか、正しい P/Invoke 署名を使用する必要があるかもしれません。

System.Drawingとを参照するだけであれば、独自の記述はかなり簡単なはずですSystem.Windows.Forms。これらの両方が機能するはずです:

static Screen ScreenFromPoint1(Point p)
{
    System.Drawing.Point pt = new System.Drawing.Point((int)p.X, (int)p.Y);
    return Screen.AllScreens
                    .Where(scr => scr.Bounds.Contains(pt))
                    .FirstOrDefault();
}

static Screen ScreenFromPoint2(Point p)
{
    System.Drawing.Point pt = new System.Drawing.Point((int)p.X, (int)p.Y);
    var scr = Screen.FromPoint(pt);
    return scr.Bounds.Contains(pt) ? scr : null;
}

自分で Win32 呼び出しを行う場合、呼び出す必要がある関数の適切な P/Invoke シグネチャ (つまり、.Net DLL を逆コンパイルして得られるもの) は次のとおりです。

    [DllImport("User32.dll", CharSet=CharSet.Auto)] 
    public static extern bool GetMonitorInfo(HandleRef hmonitor, [In, Out]MONITORINFOEX info);
    [DllImport("User32.dll", ExactSpelling=true)]
    public static extern IntPtr MonitorFromPoint(POINTSTRUCT pt, int flags);

    [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Auto, Pack=4)]
    public class MONITORINFOEX { 
        public int     cbSize = Marshal.SizeOf(typeof(MONITORINFOEX));
        public RECT    rcMonitor = new RECT(); 
        public RECT    rcWork = new RECT(); 
        public int     dwFlags = 0;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)] 
        public char[]  szDevice = new char[32];
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct POINTSTRUCT { 
        public int x;
        public int y;
        public POINTSTRUCT(int x, int y) {
          this.x = x; 
          this.y = y;
        } 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    public struct RECT {
        public int left; 
        public int top; 
        public int right;
        public int bottom; 
    }
于 2011-06-08T13:10:30.833 に答える
2

私は1つの異なるものを見つけまし
public static extern bool GetMonitorInfo(IntPtr hMonitor, [In,Out] MONITORINFO lpmi)
public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi)

私の場合、ref キーワークにより、関数は常に false を返しました。
ただし、このキーワードまたは usr [In,Out] を削除すると機能します。

Thisの ref と [In,Out] に関する詳細情報。

于 2019-02-03T08:46:52.707 に答える
1

あなたの Rectangle2 は、 orではなく、Int32orを使用する必要があります。詳細については、こちらをご覧くださいintInt64

また、クラスではなく構造体である必要があります。同じことが MonitorInfo クラスにも当てはまります (構造体である必要があります)。上記のリンクからバージョンを試すか、お使いのバージョンと比較することをお勧めします。

于 2011-06-08T13:03:48.070 に答える