c# - 从另一个进程显示系统菜单(使用 WinForms,c#)

标签 c# winforms interop

我试图在我的 WinForms UI 中显示来自不同进程的系统菜单(包含最小化、恢复等)。我知道我需要像 GetSystemMenu 和 TrackPopupMenuEx 这样的互操作调用,但我没能让它工作。有人可以提供示例代码如何操作吗?

我找到了这个代码片段(对于 WPF): Open another application's System Menu

我修改成这样:

    const uint TPM_LEFTBUTTON = 0x0000;
    const uint TPM_RETURNCMD = 0x0100;
    const uint WM_SYSCOMMAND = 0x0112;

    [DllImport("user32.dll")]
    static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

    [DllImport("user32.dll")]
    static extern uint TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y, IntPtr hwnd, IntPtr lptpm);

    [return: MarshalAs(UnmanagedType.Bool)]
    [DllImport("user32.dll", SetLastError = true)]
    static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    public void ShowContextMenu()
    {
        IntPtr wMenu = GetSystemMenu(ExternalWindowHandle, false);
        // Display the menu
        uint command = TrackPopupMenuEx(wMenu, TPM_LEFTBUTTON | TPM_RETURNCMD, 10, 10, ExternalWindowHandle, IntPtr.Zero);
        if (command == 0)
            return;
        PostMessage(ExternalWindowHandle, WM_SYSCOMMAND, new IntPtr(command), IntPtr.Zero);
    }

如问题标题中所述,我不想将窗口最小化到系统托盘,我想在我选择的位置显示来自另一个进程(窗口)的系统菜单。与 Windows 任务栏的方式几乎相同。任务栏(资源管理器)貌似可以在任务栏上右键显示系统菜单。

谢谢, 斯特凡

最佳答案

我有代码的工作版本我也检查了 MSDN 库,我发现为了使 TrackPopupMenuEx 方法工作,你传递的“ExternalWindowHandle”变量即句柄代表的窗口需要在桌面的前景。

MSDN 库说明如下:

“要显示通知图标的上下文菜单,在应用程序调用 TrackPopupMenu 或 TrackPopupMenuEx 之前,当前窗口必须是前景窗口。否则,菜单不会在用户消失时消失在菜单或创建菜单的窗口之外单击(如果可见)。如果当前窗口是子窗口,则必须将(顶级)父窗口设置为前景窗口。", http://msdn.microsoft.com/en-us/library/windows/desktop/ms648003(v=vs.85).aspx

所以这意味着它只会在你的窗口处于事件状态并且在前台时才有效,例如,如果你在 visual studio 中进行调试,它就不会工作,因为窗口不在前台,即 visual studio 不是你的应用。

请参阅随附的工作代码示例,请记住,只有当应用程序窗口处于焦点/前景时,它才会工作。即当您调试或使用另一个窗口时,TrackPopupMenuEx 将始终返回 0。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        const uint TPM_LEFTBUTTON = 0x0000;
        const uint TPM_RETURNCMD = 0x0100;
        const uint WM_SYSCOMMAND = 0x0112;

        [DllImport("user32.dll")]
        static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

        [DllImport("user32.dll")]
        static extern uint TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y, IntPtr hwnd, IntPtr lptpm);

        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        public static void ShowContextMenu(IntPtr appWindow, IntPtr myWindow, Point point)
        {
            IntPtr wMenu = GetSystemMenu(appWindow, false);
            // Display the menu
            uint command = TrackPopupMenuEx(wMenu,
                TPM_LEFTBUTTON | TPM_RETURNCMD, (int)point.X, (int)point.Y, myWindow, IntPtr.Zero);
            if (command == 0)
                return;

            PostMessage(appWindow, WM_SYSCOMMAND, new IntPtr(command), IntPtr.Zero);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ShowContextMenu(new IntPtr(<<put your target window handle here>>), this.Handle, new Point(0, 0));
        }
    }
}

关于c# - 从另一个进程显示系统菜单(使用 WinForms,c#),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15129218/

相关文章:

c# - 有没有办法知道电子邮件是否已通过 C# 成功中继?

C# - 无法更新 mysql 数据库列值

.net - 如何强制Web浏览器控件始终在同一窗口中打开网页?

asp.net - 如何在frm(winForms)中创建PasswordHash asp身份o生成PasswordHash

c# - 如何隐式转换这个?

c# - JSON.NET 阅读器问题

c# - 如何像 Split View Pane 一样转换 UWP 导航 View Pane

c# Interop Word 文档添加样式

c# - 在 C++/C# 之间的结构内传递字符串/数组

c# - 如何在 Silverlight 4 中等待状态改变转换完成?