设置托盘图标始终显示

17
我该如何设置NotifyIcon始终在右侧托盘中可见: http://screensnapr.com/v/qKWHe2.png 因为它会转移到非活动图标窗口中: http://screensnapr.com/v/jjtuK0.png 问题是托盘图标有一个上下文菜单,让某人拍摄屏幕截图。每次他们拍摄屏幕截图时,那个非活动图标窗口都会出现在图片中,像照片轰炸机一样挡住了背后的任何东西。
我知道通过代码实现是可能的,因为其他应用程序(如comodo)已经做到了,而不需要我将图标拖放到那里。 对于将快捷方式固定到任务栏,我学到了可以在此文件夹中设置快捷方式:
C:\Users\Username\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar

是否有类似托盘的东西?或者有我可以使用代码的选项。


在任务栏属性对话框中,将通知区域图标设置为“始终可见”是用户的选择。如果Windows也提供了编程接口,我会感到惊讶。 - Jon
是的,通常我不会试图强制用户这样做。但是重要的是,他们需要截取一个没有被非活动图标窗口遮挡的屏幕截图。 - Drake
然后他们可以进入“自定义...”并表示他们希望该图标始终可见。 - Joe White
即使有人点击菜单项,不活动的托盘窗口仍然保持打开状态。我可以冒险通过强制隐藏它来破坏窗口,但这是对更可靠的可能解决方案的不良黑客行为。 - Drake
我知道这是老的,但我遇到了这个问题。你的Comodo可以做到这一点的原因可能与它在ring0而不是ring3中运行有关。不过,不要引用我的话。 - Rob
你的软件并不是那么特别,需要强制用户使用它。相反,考虑帮助用户了解如何启用它自己显示。 - Charles Oppermann
5个回答

9
我在网上随机搜索时找到了这个内容。
简而言之,使用PowerShell(提供脚本)和GPO的组合来实现。
这是一个有关如何强制在Windows 7或Windows 8中始终显示通知区域图标的文章,您可以点击以下链接查看详细信息:http://4sysops.com/archives/forcing-notification-area-icons-to-always-show-in-windows-7-or-windows-8/ 长话短说,创建一个包含以下内容的PowerShell脚本即可:
param(
    [Parameter(Mandatory=$true,HelpMessage='The name of the program')][string]$ProgramName,
    [Parameter(Mandatory=$true,HelpMessage='The setting (2 = show icon and notifications 1 = hide icon and notifications, 0 = only show notifications')]
        [ValidateScript({if ($_ -lt 0 -or $_ -gt 2) { throw 'Invalid setting' } return $true})]
        [Int16]$Setting
    )

$encText = New-Object System.Text.UTF8Encoding
[byte[]] $bytRegKey = @()
$strRegKey = ""
$bytRegKey = $(Get-ItemProperty $(Get-Item 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify').PSPath).IconStreams
for($x=0; $x -le $bytRegKey.Count; $x++)
{
    $tempString = [Convert]::ToString($bytRegKey[$x], 16)
    switch($tempString.Length)
    {
        0 {$strRegKey += "00"}
        1 {$strRegKey += "0" + $tempString}
        2 {$strRegKey += $tempString}
    }
}
[byte[]] $bytTempAppPath = @()
$bytTempAppPath = $encText.GetBytes($ProgramName)
[byte[]] $bytAppPath = @()
$strAppPath = ""

Function Rot13($byteToRot)
{
    if($byteToRot -gt 64 -and $byteToRot -lt 91)
    {
        $bytRot = $($($byteToRot - 64 + 13) % 26 + 64)
        return $bytRot
    }
    elseif($byteToRot -gt 96 -and $byteToRot -lt 123)
    {
        $bytRot = $($($byteToRot - 96 + 13) % 26 + 96)
        return $bytRot
    }
    else
    {
        return $byteToRot
    }
}

for($x = 0; $x -lt $bytTempAppPath.Count * 2; $x++)
{
    If($x % 2 -eq 0)
    {
        $curbyte = $bytTempAppPath[$([Int]($x / 2))]
            $bytAppPath += Rot13($curbyte)

    }
    Else
    {
        $bytAppPath += 0
    }
}

for($x=0; $x -lt $bytAppPath.Count; $x++)
{
    $tempString = [Convert]::ToString($bytAppPath[$x], 16)
    switch($tempString.Length)
    {
        0 {$strAppPath += "00"}
        1 {$strAppPath += "0" + $tempString}
        2 {$strAppPath += $tempString}
    }
}
if(-not $strRegKey.Contains($strAppPath))
{
    Write-Host Program not found. Programs are case sensitive.
    break
}

[byte[]] $header = @()
$items = @{}
for($x=0; $x -lt 20; $x++)
{
    $header += $bytRegKey[$x]
}

for($x=0; $x -lt $(($bytRegKey.Count-20)/1640); $x++)
{
    [byte[]] $item=@()
    $startingByte = 20 + ($x*1640)
    $item += $bytRegKey[$($startingByte)..$($startingByte+1639)]
    $items.Add($startingByte.ToString(), $item)
}

foreach($key in $items.Keys)
{
$item = $items[$key]
    $strItem = ""
    $tempString = ""

    for($x=0; $x -le $item.Count; $x++)
    {
        $tempString = [Convert]::ToString($item[$x], 16)
        switch($tempString.Length)
        {
            0 {$strItem += "00"}
            1 {$strItem += "0" + $tempString}
            2 {$strItem += $tempString}
        }
    }
    if($strItem.Contains($strAppPath))
    {
        Write-Host Item Found with $ProgramName in item starting with byte $key
            $bytRegKey[$([Convert]::ToInt32($key)+528)] = $setting
            Set-ItemProperty $($(Get-Item 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify').PSPath) -name IconStreams -value $bytRegKey
    }
}

使用您选择的名称将其保存为ps1文件。

打开组策略管理MMC。选择您选择的组策略对象,右键单击并选择"编辑"。在编辑器中,导航到"用户配置 > Windows设置 > 脚本 > 登录",然后单击"显示属性"。转到PowerShell选项卡,单击"查看文件"。

将刚才创建的脚本复制到刚刚打开的资源管理器窗口中,然后关闭该窗口。

在登录脚本属性窗口中,添加一个新的PowerShell脚本,在脚本名称中输入您使用的脚本名称(例如:NotifyIcon.ps1),然后在参数中输入程序名称(区分大小写!)后跟要使用的设置:

0 = 仅显示通知 1 = 隐藏图标和通知 2 = 显示图标和通知 <--- 您需要的

例如,如果您需要始终显示RealVNC服务器,则应输入:

winvnc4.exe 2

作为参数

您可以以几种不同的方式找出可执行文件的名称,例如打开运行对话框并键入msconfig,查看启动程序,手动导航到安装目录C:\Program Files {您的程序},或尝试通过查看任务管理器中正在运行的进程来匹配所需的程序。十有八九会成功。

为使此操作起作用,用户必须先运行应用程序,然后正确注销,以便explorer.exe有机会将更新的通知区域历史记录写入注册表。在随后的登录中,脚本应成功地找到历史记录中的程序,并将其设置为始终显示。

您还可以尝试从PowerShell提示符手动运行脚本进行调试,但在运行之前必须关闭explorer.exe('taskkill /f /im explorer.exe'),否则explorer不会看到您的更新,在退出时会覆盖它。

我不对此过程负责。我没有编写它,我只是找到了它。脚本的功劳归Micah Rowland,GPO的功劳归Geoff Kendal。

由于声望不足,无法链接到原始作者,除了开头提到的一个。


1
这在Windows 10上似乎仍然有效。如上所述,重新启动explorer.exe的更简单方法是只需打开任务管理器,找到explorer.exe并右键单击,然后选择重新启动即可。在运行powershell脚本后也可以执行此操作。 - Michael Adamission
1
女士们先生们,我认为简单地重新启动explorer.exe是不够的。请记住,我在7年前回答了这个问题,所以Windows操作方式的变化或者我的记忆衰退可能会证明下一个陈述是错误的,但是我的理解是,如果你在explorer仍在运行时运行脚本,当重新启动explorer时,它将覆盖你刚刚尝试做的事情。因此,更好的过程是杀死explorer,运行脚本,然后启动explorer。这个方法之所以在GPO登录脚本中起作用,是因为脚本在桌面会话建立之前执行。 - jparnell8839

7

5
这并不正确,因为像科摩多这样的应用程序已经出现在我的快速启动栏中,而我并没有特别将其放在那里,所以你不能说这是不可能的。你可能不知道或者不认可它,但不要声称这是普遍误解。 - Drake
如果这样的话,所有软件供应商都会这样做,并强制用户查看他们的托盘图标! - Mohamed Abed
2
看到这么多人因为被错误信息误导而声称这是不可能的事情,然后把它当作事实,真的很让人恼火。我只想让它默认在托盘中,而不是强制性的。只是默认设置。 - Drake
2
默认设置为“仅显示通知”。资源管理器会尝试猜测图标是否表示通知,有时这种猜测会产生误报,这就是您在其他程序中看到的情况。 - Raymond Chen
看起来很有趣,但是我不太懂VB。您有任何C#或只涉及注册表的参考资料吗? - Drake
显示剩余4条评论

6
给通知区域图标“始终可见”状态是用户通过任务栏属性对话框做出的选择。这是由适当的Windows团队作出的设计决策,以便将此选择置于用户的自由裁量之下。
因此,没有编程接口可以让您使图标始终可见。甚至没有界面来询问您的图标是否可见
如果我是你,我会设置一个热键,让用户完全不必涉及图标就能截屏(所有屏幕捕获程序都能做到这一点)。
如果用户真的喜欢在截屏之前右键单击,他们总是可以通过上述提到的任务栏属性更改其首选项。

1
这不是一种需要全局热键的应用程序,而是当他们想要自愿提供带有错误报告的屏幕截图进行测试时使用。为此而记忆或放弃特定的热键组合比只在托盘中显示更具侵入性。 - Drake
@Drake:我不是说强迫他们这样做。我是说“给他们选择的机会,这也将解决那些选择它的人目前的问题”。 - Jon
2
如果他们不想在活动托盘列表中显示,他们确实有一个选项。他们可以将其拖到非活动托盘中。但默认情况下,他们应该能够看到应用程序正在运行,并且随时准备接受命令。我已经可以想象到人们会尝试运行两个实例,因为他们无法看到它是活动的,或者最终忘记了它对他们可用。 - Drake
即使在代码中将图标设置为始终可见是可能的,但我已经看到过太多次被设置为这种方式的图标进入非活动列表,因此用户仍然必须注意这一点,除非程序本身以某种方式监视设置(正如有人指出的那样,这在代码中也不可能实现)并将其更改回来。 - RobH

1
使用类似这样的脚本来增强软件包,以便用户可以通过简单的复选框决定是否将图标固定到通知区域(或不固定),而无需跳过微软选择设置的各种障碍,这似乎是一种非常具有可访问性的想法。
也许如果微软在通知区域图标上提供右键固定/取消固定选项(如任务栏)或程序轻松提供这样的功能,我们就不会进行这种讨论了。

0

微软明确表示,这是用户决定而不是可以通过编程来实现的决定。是的,有各种工具可以在内部进行修改以实现此目的,但没有受支持的方法可以实现您所需的功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接