微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

通过WinApi32显示气球弹出窗口

如何解决通过WinApi32显示气球弹出窗口

我想显示一个气球弹出框而不添加对Windows.Forms的依赖关系。因此,我决定使用Shell_NotifyIcon中的Shell32。这是我编写的代码(从F#转换为C#),应该创建一个通知图标,然后显示一个气球。

struct NotifyIconData {
  public system.int32 cbSize;
  public system.intPtr hWnd; // HWND
  public system.int32 uID; // UINT
  public system.int32 uFlags; // UINT
  public system.int32 uCallbackMessage;  // UINT
  public system.intPtr hIcon;  // HICON
  [MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)]
  public System.String szTip;   // char[128]
  public system.int32 dwState;  // DWORD
  public system.int32 dwStateMask;  // DWORD
  [MarshalAs(UnmanagedType.ByValTStr,SizeConst=256)]
  public System.String szInfo; // char[256]
  public system.int32 uTimeoutOrVersion;  // UINT
  [MarshalAs(UnmanagedType.ByValTStr,SizeConst=64)]
  public System.String szInfoTitle; // char[64]
  public system.int32 dwInfoFlags;  // DWORD
  public Guid guidItem;
  public IntPtr hBalloonIcon; //HIcon
}

[DllImport("shell32.dll",SetLastError = true)]
extern bool Shell_NotifyIcon(uint dwMessage,ref NotifyIconData pnid);

[DllImport("kernel32.dll")]
extern IntPtr GetConsoleWindow();

public NotifyIconData CreateNotify (IntPtr hwnd)
{
    var data = new NotifyIconData();
    data.cbSize = sizeof(NotifyIconData);
    data.hWnd = hwnd;
    data.uID = 0;
    data.uFlags = 0x00000001 | 0x00000002 | 0x00000004 | 0x00000008 | 0x00000020 | 0x00000080; // NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_STATE | NIF_GUID | NIF_SHOWTIP;
    data.szTip = "Tooltip";
    data.dwState = 0;
    data.dwStateMask = 0x00000001 ||| 0x00000002; // NIS_SHAREDICON | NIS_HIDDEN
    data.guidItem = Guid.NewGuid ();
    data.hIcon = PInvoke.User32.LoadIcon(IntPtr.Zero,IntPtr(32512));
    var result = Shell_NotifyIcon(0x00000000u,&(data));

    data.uVersion = 4;

    var versionResult = Shell_NotifyIcon(0x00000004u,&(data));

    return data;
}

public bool CreateBalloon (string message,NotifyIconData data)
{
    data.uFlags = 0x00000010 | 0x00000020; // NIF_INFO | NIF_GUID
    data.dwInfoFlags = 0x00000000; // NO ICON
    data.szInfo = message;
    data.szInfoTitle = "Balloon title";
    data.hBalloonIcon = PInvoke.User32.LoadIcon(IntPtr.Zero,IntPtr(32512));
    
    var result = Shell_NotifyIcon(0x000000001u,&(data));

    return result
}

public bool ShowBalloon (string message)
{
 
 let hwnd = GetConsoleWindow();
 
 var notify = CreateNotify (hwnd);
 return CreateBalloon (message,notify);
}

我可以在日志中观察到一切似乎都很好,看不到任何错误

2020-10-13T15:34:23.880+00 724 INFO create notify error 0,true
2020-10-13T15:34:23.889+00 724 INFO set version error 0,true
2020-10-13T15:34:23.889+00 724 INFO baloon error 0,true

但是气球没有出现在屏幕上。我想知道错误的位置(也许我错过了调用某些函数或应该设置图标的位置(现在我忽略了它)?)。 我还尝试过:

  • 首先创建气球,而不先创建通知
  • 调用SetThreadDesktop(OpenInputDesktop(0u,true,GENERIC_ALL)) from user32”,然后再调用显示气球”。

但是它仍然不起作用。

这是用F#编写的原始代码

type NotifyIconData =
    struct
      val mutable cbSize: system.int32  // DWORD
      val mutable hWnd: system.intPtr // HWND
      val mutable uID: system.int32 // UINT
      val mutable uFlags: system.int32  // UINT
      val mutable uCallbackMessage: system.int32  // UINT
      val mutable hIcon: system.intPtr  // HICON
      [<MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)>]
      val mutable szTip: System.String  // char[128]
      val mutable dwState: system.int32  // DWORD
      val mutable dwStateMask: system.int32  // DWORD
      [<MarshalAs(UnmanagedType.ByValTStr,SizeConst=256)>]
      val mutable szInfo: System.String // char[256]
      val mutable uTimeoutOrVersion: system.int32  // UINT
      [<MarshalAs(UnmanagedType.ByValTStr,SizeConst=64)>]
      val mutable szInfoTitle: System.String // char[64]
      val mutable dwInfoFlags: system.int32  // DWORD
      val mutable guidItem: Guid
      val mutable hBalloonIcon: IntPtr //HIcon
    end

  [<DllImport("shell32.dll",SetLastError = true)>]
  extern bool Shell_NotifyIcon(uint dwMessage,NotifyIconData& pnid)

  [<DllImport("kernel32.dll")>]
  extern IntPtr GetConsoleWindow();

  let createNotify hwnd =
    let mutable data = NotifyIconData()
    data.cbSize <- sizeof<NotifyIconData>
    data.hWnd <- hwnd
    data.uID <- 0
    data.uFlags <- 0x00000001 ||| 0x00000002 ||| 0x00000004 ||| 0x00000008 ||| 0x00000020 ||| 0x00000080
    data.szTip <- "Tooltip"
    data.dwState <- 0
    data.dwStateMask <- 0x00000001 ||| 0x00000002
    data.guidItem <- Guid.NewGuid ()
    data.hIcon <- PInvoke.User32.LoadIcon(IntPtr.Zero,IntPtr(32512))
    let result = Shell_NotifyIcon(uint NotifyCommand.Add,&(data))

    data.uTimeoutOrVersion <- 4

    let versionResult = Shell_NotifyIcon(uint NotifyCommand.SetVersion,&(data))
    data

  let createBalloon message (d: NotifyIconData) =
    let mutable data = d
    
    data.uFlags <-  0x00000010 ||| 0x00000020
    data.dwInfoFlags <- 4
    data.hBalloonIcon <- PInvoke.User32.LoadIcon(IntPtr.Zero,IntPtr(32512))
    data.szInfo <- message
    data.szInfoTitle <- "Balloon title"
    data.uTimeoutOrVersion <- 50000
    let result = Shell_NotifyIcon(uint NotifyCommand.Modify,&(data))

    result

  let showBallon hwnd message =
    GetConsoleWindow ()
    |> createNotify 
    |> createBalloon message

解决方法

您提供的示例似乎可以在C#控制台应用程序中工作:

public static NotifyIconData CreateNotify(IntPtr hwnd)
    {
        var data = new NotifyIconData();
        data.cbSize = Marshal.SizeOf(typeof(NotifyIconData));
        data.hWnd = hwnd;
        data.uID = 0;
        data.uFlags = 0x00000001 | 0x00000002 | 0x00000004 | 0x00000008 | 0x00000020 | 0x00000080; // NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_STATE | NIF_GUID | NIF_SHOWTIP;
        data.szTip = "Tooltip";
        data.dwState = 0;
        data.dwStateMask = 0x00000001 | 0x00000002; // NIS_SHAREDICON | NIS_HIDDEN
        data.guidItem = Guid.NewGuid();
        var result = Shell_NotifyIcon(0x00000000u,ref data);
        data.uTimeoutOrVersion = 4;
        var versionResult = Shell_NotifyIcon(0x00000004u,ref data);
        return data;
    }

    public static bool CreateBalloon(string message,NotifyIconData data)
    {
        data.uFlags = 0x00000010 | 0x00000020; // NIF_INFO | NIF_GUID
        data.dwInfoFlags = 0x00000000; // NO ICON
        data.szInfo = message;
        data.szInfoTitle = "Balloon title";
        var result = Shell_NotifyIcon(0x000000001u,ref data);
        return result;
    }

    public static bool ShowBalloon(string message)
    {
        IntPtr hwnd = GetConsoleWindow();
        var notify = CreateNotify(hwnd);
        return CreateBalloon(message,notify);
    }
    static void Main(string[] args)
    {
        bool ret = ShowBalloon("test");
        Console.WriteLine("Hello World!");
    }

对我来说,还有一个简单的示例工作:

static void Main(string[] args)
{
    //ShowBalloon("test");
    var data = new NOTIFYICONDATA();
    data.cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA));
    data.uID = 0;
    data.uFlags = 0x00000010; // NIF_INFO
    data.dwInfoFlags = 0x00000000; // NO ICON
    data.szInfo = "test";
    data.szInfoTitle = "Balloon title";
    bool result = Shell_NotifyIcon(0x00000001,ref data);
    Console.WriteLine("Hello World!");
}
,

@Drake的答案向我显示了错误的位置,因此我向运行中的C#示例和F#示例添加了更可靠的日志记录(由于我认为C#和F#之间的代码是1:1)。问题在于设置cbSize的值。我使用sizeof来计算NotifyIconData结构的大小。但如here所述,sizeof将返回System.Type而不是NotifyIconData的大小。因此,我将sizeof更改为Marshal.SizeOf(typedefof<NotifyIconData>),一切按预期开始工作。

以下是C#应用程序的日志(正如Drake所说的那样,它工作正常)

... when I create notify icon
cbSize: 528,hwnd: 983580,uid: 0,uflags: 175,sztip: Tooltip,dwState: 0,dwStateMask: 3,guidItem: 9367e9cd-1fc2-4312-83dd-22b6e03486b9

... when version is set
cbSize: 528,guidItem: 9367e9cd-1fc2-4312-83dd-22b6e03486b9

... when we enter `createBalloon` function
cbSize: 528,guidItem: 9367e9cd-1fc2-4312-83dd-22b6e03486b9

... after balloon creation
cbSize: 528,uflags: 48,guidItem: 9367e9cd-1fc2-4312-83dd-22b6e03486b9

以下是F#版本的日志:

... when I create notify icon



cbSize: 96,hwnd: 591150n,sztip: "Tooltip",guidItem: 1ee87d18-ef8d-48ba-aee9-f197de6425a4

... when version is set
cbSize: 96,guidItem: 1ee87d18-ef8d-48ba-aee9-f197de6425a4

... when we enter `createBalloon` function
cbSize: 96,guidItem: 1ee87d18-ef8d-48ba-aee9-f197de6425a4

... after balloon creation
cbSize: 96,guidItem: 1ee87d18-ef8d-48ba-aee9-f197de6425a4

这是完全可用的F#示例:

  type NotifyIconData =
    struct
      val mutable cbSize: System.Int32  // DWORD
      val mutable hWnd: System.IntPtr // HWND
      val mutable uID: System.Int32 // UINT
      val mutable uFlags: System.Int32  // UINT
      val mutable uCallbackMessage: System.Int32  // UINT
      val mutable hIcon: System.IntPtr  // HICON
      [<MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)>]
      val mutable szTip: System.String  // char[128]
      val mutable dwState: System.Int32  // DWORD
      val mutable dwStateMask: System.Int32  // DWORD
      [<MarshalAs(UnmanagedType.ByValTStr,SizeConst=256)>]
      val mutable szInfo: System.String // char[256]
      val mutable uTimeoutOrVersion: System.Int32  // UINT
      [<MarshalAs(UnmanagedType.ByValTStr,SizeConst=64)>]
      val mutable szInfoTitle: System.String // char[64]
      val mutable dwInfoFlags: System.Int32  // DWORD
      val mutable guidItem: Guid
      val mutable hBalloonIcon: IntPtr //HIcon
    end

  [<DllImport("shell32.dll",SetLastError = true)>]
  extern bool Shell_NotifyIcon(uint dwMessage,NotifyIconData& pnid)

  [<DllImport("kernel32.dll")>]
  extern IntPtr GetConsoleWindow();

  let createNotify hwnd =
    let mutable data = NotifyIconData()
    data.cbSize <- Marshal.SizeOf(typedefof<NotifyIconData>)
    data.hWnd <- hwnd
    data.uID <- 0
    data.uFlags <- 0x00000001 ||| 0x00000002 ||| 0x00000004 ||| 0x00000008 ||| 0x00000020 ||| 0x00000080
    data.szTip <- "Tooltip"
    data.dwState <- 0
    data.dwStateMask <- 0x00000001 ||| 0x00000002
    data.guidItem <- Guid.NewGuid ()
    data.hIcon <- PInvoke.User32.LoadIcon(IntPtr.Zero,IntPtr(32512))
    let result = Shell_NotifyIcon(uint NotifyCommand.Add,&(data))

    data.uTimeoutOrVersion <- 4

    let versionResult = Shell_NotifyIcon(uint NotifyCommand.SetVersion,&(data))
    data

  let createBalloon message (d: NotifyIconData) =
    let mutable data = d
    
    data.uFlags <-  0x00000010 ||| 0x00000020
    data.dwInfoFlags <- 4
    data.hBalloonIcon <- PInvoke.User32.LoadIcon(IntPtr.Zero,IntPtr(32512))
    data.szInfo <- message
    data.szInfoTitle <- "Balloon title"
    data.uTimeoutOrVersion <- 50000
    let result = Shell_NotifyIcon(uint NotifyCommand.Modify,&(data))

    result

  let showBallon hwnd message =
    GetConsoleWindow ()
    |> createNotify 
    |> createBalloon message

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。