如何创建可以覆盖所有 windows 的文本,如 Windows 激活水印?

How does one create text that can overlay all windows like the Windows Activation Watermark?

我想创建一个倒计时计时器,它显示在屏幕的右下角,带有稍微透明的文本,无论您在屏幕上有什么,都可以看到。想想当您的 windows 副本未激活时出现的 windows 未激活水印消息。它显示在右下角,覆盖所有 windows 并“卡在”屏幕上。

有没有办法通过程序来做到这一点? C# 首选。

到目前为止,在我的研究中,我只遇到了 Deskbands,它允许你在任务栏中放置一些东西,但不能放在任务栏之外。

注意: 下面的答案创建了一个正常的 semi-transparent 最上面的形状,边缘平滑。它并不完全像 windows 激活文本,例如它位于工具提示 windows 或菜单后面,但它位于其他 non-topmost windows.[=21= 之上]

您可以创建一个 Layered Window by setting WS_EX_LAYERED style, then you can assign a smooth-edge transparent region to the window. You can also set WS_EX_TRANSPARENT for the window, then it will ignore the mouse event. Then to make it always on top set its TopMost to true. And finally if you want to make it semi-transparent use suitable Opacity 值。

例子

在下面的示例中,我创建了一个始终位于顶部并显示时间的叠加表单:

1 - 将以下 class 添加到您的项目中,其中包含所需的本机方法:

using System;
using System.Runtime.InteropServices;
public class NativeMethods
{
    public const int WS_EX_LAYERED = 0x80000;
    public const int HTCAPTION = 0x02;
    public const int WM_NCHITTEST = 0x84;
    public const int ULW_ALPHA = 0x02;
    public const byte AC_SRC_OVER = 0x00;
    public const byte AC_SRC_ALPHA = 0x01;
    public const int WS_EX_TRANSPARENT = 0x20;
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int x;
        public int y;

        public POINT(int x, int y)
        { this.x = x; this.y = y; }
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct SIZE
    {
        public int cx;
        public int cy;

        public SIZE(int cx, int cy)
        { this.cx = cx; this.cy = cy; }
    }
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct BLENDFUNCTION
    {
        public byte BlendOp;
        public byte BlendFlags;
        public byte SourceConstantAlpha;
        public byte AlphaFormat;
    }
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
        ref POINT pptDst, ref SIZE psize, IntPtr hdcSrc, ref POINT pprSrc,
        int crKey, ref BLENDFUNCTION pblend, int dwFlags);
    [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr GetDC(IntPtr hWnd);
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
    [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool DeleteDC(IntPtr hdc);
    [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
    [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool DeleteObject(IntPtr hObject);
}

2 - 添加以下 class,它是 semi-transparent smooth-edge windows 形式的基础 class:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using static NativeMethods;
public class PerPixelAlphaForm : Form
{
    public PerPixelAlphaForm()
    {
        this.FormBorderStyle = FormBorderStyle.None;
        this.ShowInTaskbar = false;
        this.TopMost = true;
    }
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams createParams = base.CreateParams;
            if (!DesignMode)
                createParams.ExStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
            return createParams;
        }
    }
    public void SelectBitmap(Bitmap bitmap, int opacity = 255)
    {
        if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
        {
            throw new ApplicationException(
                "The bitmap must be 32bpp with alpha-channel.");
        }
        IntPtr screenDc = GetDC(IntPtr.Zero);
        IntPtr memDc = CreateCompatibleDC(screenDc);
        IntPtr hBitmap = IntPtr.Zero;
        IntPtr hOldBitmap = IntPtr.Zero;
        try
        {
            hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
            hOldBitmap = SelectObject(memDc, hBitmap);
            SIZE newSize = new SIZE(bitmap.Width, bitmap.Height);
            POINT sourceLocation = new POINT(0, 0);
            POINT newLocation = new POINT(this.Left, this.Top);
            BLENDFUNCTION blend = new BLENDFUNCTION();
            blend.BlendOp = AC_SRC_OVER;
            blend.BlendFlags = 0;
            blend.SourceConstantAlpha = (byte)opacity;
            blend.AlphaFormat = AC_SRC_ALPHA;
            UpdateLayeredWindow(this.Handle, screenDc, ref newLocation, 
                ref newSize, memDc, ref sourceLocation, 0, ref blend, ULW_ALPHA);
        }
        finally
        {
            ReleaseDC(IntPtr.Zero, screenDc);
            if (hBitmap != IntPtr.Zero)
            {
                SelectObject(memDc, hOldBitmap);
                DeleteObject(hBitmap);
            }
            DeleteDC(memDc);
        }
    }
}

3 - 然后添加以下 class 这是一个表格,有一个显示时间的计时器:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Windows.Forms;
public partial class Form1 : PerPixelAlphaForm
{
    private IContainer components = null;
    private Timer timer1;
    private Bitmap image;
    public Form1()
    {
        this.components = new Container();
        this.timer1 = new Timer(this.components);
        this.timer1.Enabled = true;
        this.timer1.Interval = 500;
        this.timer1.Tick += new EventHandler(this.timer1_Tick);
        this.StartPosition = FormStartPosition.Manual;
        this.Location = new Point(300, 300);
        this.Size = new Size(800, 500);
        this.image = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
    }
    private void timer1_Tick(object sender, EventArgs e)
    {
        using (var g = Graphics.FromImage(image))
        {
            g.Clear(Color.Transparent);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.TextRenderingHint = TextRenderingHint.AntiAlias;
            g.DrawString(DateTime.Now.ToString("HH:mm:ss"),
                new Font(this.Font.FontFamily, 60, FontStyle.Bold), Brushes.Black,
                ClientRectangle, StringFormat.GenericDefault);
            SelectBitmap(image, 150);
        }
    }
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
            components.Dispose();
        if (disposing && image != null)
            image.Dispose();
        base.Dispose(disposing);
    }
}

您可以在以下帖子中找到更多信息: