调用 Magnification API 实现 NVDA 屏幕阅读器的黑屏效果
[作者: 张赐荣]
前言
作为读屏软件的开发者,经常需要考虑如何帮助视力障碍者更好地使用计算机。一个常见需求是保护用户的屏幕隐私,避免在操作电脑期间被他人窥视。本文将详细讲解如何使用 Windows Magnification API 实现屏幕变暗功能,从而达到保护隐私的目的。
环境准备
首先,请确保你已经安装了 Visual Studio 或其他支持 C# 的开发环境。本教程将使用 C# 和 Windows Magnification API。
第一步:创建一个新的 Windows Forms 项目
- 打开 Visual Studio。
- 创建一个新的 Windows Forms 应用程序项目。
第二步:导入必要的命名空间和库
在项目中,我们需要导入一些命名空间和外部库以便使用 Windows Magnification API。
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
第三步:定义 Magnification API 的数据结构
Windows Magnification API 使用 MAGCOLOREFFECT
结构来定义颜色转换矩阵。我们需要在代码中定义这个结构。
[StructLayout(LayoutKind.Sequential)]
struct MAGCOLOREFFECT
{
// 将一个固定大小为25的数组按值进行封送。
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
public float[] transform;
}
第四步:定义黑色转换效果
需要定义一个颜色转换矩阵,将所有颜色转换为黑色。
static MAGCOLOREFFECT TRANSFORM_BLACK = new MAGCOLOREFFECT()
{
transform = new float[]
{
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1
}
};
第五步:导入 Magnification API 的函数
用 DllImport
属性导入 Magnification API 函数。
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool MagInitialize();
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool MagUninitialize();
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool MagSetFullscreenColorEffect(ref MAGCOLOREFFECT pEffect);
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool MagGetFullscreenColorEffect(out MAGCOLOREFFECT pEffect);
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool MagShowSystemCursor([MarshalAs(UnmanagedType.Bool)] bool fShowCursor);
第六步:实现屏幕变暗功能
现在我们可以实现屏幕变暗功能了。以下是完整的 ScreenCurtain
类实现。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinForms
{
static class ScreenCurtain
{
[StructLayout(LayoutKind.Sequential)]
struct MAGCOLOREFFECT
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
public float[] transform;
}
static MAGCOLOREFFECT TRANSFORM_BLACK = new MAGCOLOREFFECT()
{
transform = new float[]
{
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1
}
};
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)] static extern bool MagInitialize();
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)] static extern bool MagUninitialize();
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)] static extern bool MagSetFullscreenColorEffect(ref MAGCOLOREFFECT pEffect);
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)] static extern bool MagGetFullscreenColorEffect(out MAGCOLOREFFECT pEffect);
[DllImport("Magnification.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)] static extern bool MagShowSystemCursor([MarshalAs(UnmanagedType.Bool)] bool fShowCursor);
public static bool Start()
{
if (!(MagInitialize() && MagSetFullscreenColorEffect(ref TRANSFORM_BLACK) && MagShowSystemCursor(false)))
{
MagUninitialize();
return (false);
}
return (IsEnabled());
}
public static bool Stop()
{
return (MagUninitialize() && MagShowSystemCursor(true));
}
public static bool IsEnabled()
{
if (!MagInitialize())
{
throw new InvalidOperationException("Failed to initialize Magnification API.");
}
MagGetFullscreenColorEffect(out MAGCOLOREFFECT currentEffect);
return AreEffectsEqual(currentEffect, TRANSFORM_BLACK);
}
private static bool AreEffectsEqual(MAGCOLOREFFECT effect1, MAGCOLOREFFECT effect2)
{
for (int i = 0; i < effect1.transform.Length; i++)
{
if (effect1.transform[i] != effect2.transform[i])
{
return false;
}
}
return true;
}
}
}
第七步:测试屏幕变暗功能
在 Windows Forms 项目中,可以通过按钮或其他事件触发 ScreenCurtain.Start()
和 ScreenCurtain.Stop()
方法,以测试屏幕变暗功能。例如:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
if (ScreenCurtain.Start())
{
MessageBox.Show("屏幕遮罩已启用");
}
else
{
MessageBox.Show("屏幕遮罩启用失败");
}
}
private void btnStop_Click(object sender, EventArgs e)
{
if (ScreenCurtain.Stop())
{
MessageBox.Show("屏幕遮罩已禁用");
}
else
{
MessageBox.Show("屏幕遮罩禁用失败");
}
}
}
在窗体上添加两个按钮,一个用于启动屏幕变暗功能,另一个用于停止屏幕变暗功能,并将其 Click
事件与上述方法关联。
参考资料
- (1) 《Windows Magnification API》: https://learn.microsoft.com/en-us/windows/win32/api/_magapi/
- (2) NVDA Vision Screen Curtain function: https://github.com/nvaccess/nvda/blob/master/source/visionEnhancementProviders/screenCurtain.py