티스토리 뷰

IT/C#

[CS][user32.dll] 화면 캡쳐 방지 함수 - SetWindowDisplayAffinity

주인장 진빼이

SetWindowDisplayAffinity 함수는 화면 캡쳐(동영상 녹화, 스크린샷) 시 해당 창을 검은색으로 만들어버린다. (MSDN)
Windows 10 20H 버전 이상이 아닌 경우 총 2가지 속성을 사용할 수 있다. (None, Monitor 속성)
SetWindowDisplayAffinity 함수에서 요구하는 핸들은 캡쳐를 방지할 폼에 대한 핸들이다. (컨트롤에 대한 핸들 X)

캡쳐 방지 또한 DWM이 활성화 되어 있는 상태에서만 작동된다. (Window 8 부터 계속 활성화 됨)
DWM 활성화 여부 체크는 다음 글을 참고하자.

테스트 케이스

  • 보호여부가 체크 상태에 따라 다음과 같은 상태 적용
    • 체크: Monitor 속성 적용
    • 체크 해제: None 속성 적용

아쉽게도 초록색 패널만 캡쳐 방지 효과를 줘보려고 했으나, 실패했다.
폼에 배치된 컨트롤(초록색 패널 영역)은 적용되지 않고 폼만 적용되는 것으로 확인되었다.

코드 (Form.cs)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SetWindowDisplayAffinityTest
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll")]
        static extern bool SetWindowDisplayAffinity(IntPtr hwnd, DisplayAffinity affinity);
        enum DisplayAffinity : uint
        {
            None = 0,
            Monitor = 1
        }

        public Form1()
        {
            InitializeComponent();
            this.checkBox1.CheckedChanged += CheckBox1_CheckedChanged;
        }

        private void CheckBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked)
            {
                // protect
                label2.Text = "Protect";
                SetWindowDisplayAffinity(this.Handle, DisplayAffinity.Monitor);
            } 
            else
            {
                label2.Text = "Disable";
                SetWindowDisplayAffinity(this.Handle, DisplayAffinity.None);
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.ShowIcon = false;
            this.MaximizeBox = false;
        }
    }
}

코드 (Designer.cs)

namespace SetWindowDisplayAffinityTest
{
    partial class Form1
    {
        /// <summary>
        /// 필수 디자이너 변수입니다.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 사용 중인 모든 리소스를 정리합니다.
        /// </summary>
        /// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form 디자이너에서 생성한 코드

        /// <summary>
        /// 디자이너 지원에 필요한 메서드입니다. 
        /// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
        /// </summary>
        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.checkBox1 = new System.Windows.Forms.CheckBox();
            this.panel1 = new System.Windows.Forms.Panel();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(12, 38);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(44, 12);
            this.label1.TabIndex = 1;
            this.label1.Text = "Status:";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(62, 38);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(11, 12);
            this.label2.TabIndex = 2;
            this.label2.Text = "-";
            // 
            // checkBox1
            // 
            this.checkBox1.AutoSize = true;
            this.checkBox1.Location = new System.Drawing.Point(12, 12);
            this.checkBox1.Name = "checkBox1";
            this.checkBox1.Size = new System.Drawing.Size(76, 16);
            this.checkBox1.TabIndex = 3;
            this.checkBox1.Text = "보호 여부";
            this.checkBox1.UseVisualStyleBackColor = true;
            // 
            // panel1
            // 
            this.panel1.BackColor = System.Drawing.Color.Green;
            this.panel1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
            this.panel1.Location = new System.Drawing.Point(12, 66);
            this.panel1.Name = "panel1";
            this.panel1.Size = new System.Drawing.Size(402, 140);
            this.panel1.TabIndex = 4;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(426, 218);
            this.Controls.Add(this.panel1);
            this.Controls.Add(this.checkBox1);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
            this.Name = "Form1";
            this.Text = "특정 윈도우 보호";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.CheckBox checkBox1;
        private System.Windows.Forms.Panel panel1;
    }
}

결과

녹화가 진행 중인 경우 녹화된 영상에서 Monitor 속성을 매개변수로 호출하면 핸들을 준 창이 검은색 창으로만 보이게 된다.
녹화가 실시간으로 진행 중이더라도 사용자 화면에는 정상적인 윈도우의 모습을 볼 수 있었고, 동영상 녹화된 영상에서만 검은색 창으로 보여졌다.

SetWindowDisplayAffinity 함수가 호출되어 Monitor 속성이 적용된 창에 대해서 스크린샷도 캡쳐할 수 없었다.
아래는 Monitor 속성이 적용된 스크린샷으로 캡쳐한 창 모습이다.

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함