0

我想显示一个气球弹出而不添加对 Windows.Forms 的依赖。所以我决定使用Shell_NotifyIconfrom 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
4

2 回答 2

1

您提供的示例似乎适用于 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!");
}
于 2020-10-15T08:24:23.467 回答
1

@Drake 答案向我展示了错误所在的位置,因此我向工作中的 C# 示例和 F# 示例添加了更强大的日志记录(这是在问题的末尾添加的,因为我认为代码是C# 和 F# 之间的 1:1)。问题在于设置cbSize值。我用来sizeof计算NotifyIconData结构的大小。但正如这里提到的,sizeof将返回 aSystem.Type而不是NotifyIconData. 所以我改为sizeofa 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, hwnd: 983580, uid: 0, uflags: 175, sztip: Tooltip, dwState: 0, dwStateMask: 3, guidItem: 9367e9cd-1fc2-4312-83dd-22b6e03486b9

... when we enter `createBalloon` function
cbSize: 528, hwnd: 983580, uid: 0, uflags: 175, sztip: Tooltip, dwState: 0, dwStateMask: 3, guidItem: 9367e9cd-1fc2-4312-83dd-22b6e03486b9

... after balloon creation
cbSize: 528, hwnd: 983580, uid: 0, uflags: 48, sztip: Tooltip, dwState: 0, dwStateMask: 3, guidItem: 9367e9cd-1fc2-4312-83dd-22b6e03486b9

以下是 F# 版本的日志:

... when I create notify icon



cbSize: 96, hwnd: 591150n, uid: 0, uflags: 175, sztip: "Tooltip", dwState: 0, dwStateMask: 3, guidItem: 1ee87d18-ef8d-48ba-aee9-f197de6425a4

... when version is set
cbSize: 96, hwnd: 591150n, uid: 0, uflags: 175, sztip: "Tooltip", dwState: 0, dwStateMask: 3, guidItem: 1ee87d18-ef8d-48ba-aee9-f197de6425a4

... when we enter `createBalloon` function
cbSize: 96, hwnd: 591150n, uid: 0, uflags: 175, sztip: "Tooltip", dwState: 0, dwStateMask: 3, guidItem: 1ee87d18-ef8d-48ba-aee9-f197de6425a4

... after balloon creation
cbSize: 96, hwnd: 591150n, uid: 0, uflags: 48, sztip: "Tooltip", dwState: 0, dwStateMask: 3, 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
于 2020-10-15T11:23:34.403 回答