[C#] Clipboard(복사 + 붙여넣기 기능)을 다루는 방법


Development note/C#  2021. 1. 26. 16:12

안녕하세요. 명월입니다.


이 글은 C#에서 Clipboard(복사 + 붙여넣기 기능)을 다루는 방법에 대한 글입니다.


우리가 컴퓨터를 사용하다 보면 Ctrl + C(복사하기) 기능과 Ctrl + V(붙여넣기) 기능을 정말 많이 사용합니다. 문서 작업이나 파일 이동, 복사등등으로 많이 사용하게 됩니다.

이런 기능을 Clipboard라는 프로그램을 통해서 사용하게 되는데, 일반 text는 텍스트로 이미지 바이너리나 파일 경로등을 Clipboard에 저장하고 Ctrl + V 단축키를 통해서 복사가 이루어지게 됩니다.


응용 프로그램을 만들 때, 이런 기능이 있으면 사용자가 좀 더 편하게 사용하거나 프로그램 간에 소켓을 사용하지 않고도 데이터를 주고 받을 수 있는 임시 버퍼의 역할을 할 수 있습니다.


먼저 간단한 Text를 클립보드에 복사해 보겠습니다.

using System;
using System.Windows.Forms;

namespace Example
{
  // 실행 클래스
  public class Program
  {
    // 싱글 단일 스레드(Clipboard는 윈도우 객체이기 때문에 단일 스레드 설정이 필요하다.)
    [STAThread]
    // 실행 함수
    static void Main(string[] args)
    {
      // 클립보드에 text 저장하기
      Clipboard.SetText("Hello world");

      // 클립보드에서 text 가져오기
      var text = Clipboard.GetText();
      // 콘솔 출력
      Console.WriteLine(text);

      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

클립보드에 Hello world를 저장하고 GetText함수를 통해서 클립보드에 저장된 text값을 받아와서 콘솔에 출력했습니다.

그럼 클럽보드에 저장이 되었는지 메모장에 Ctrl + V로 확인해 보겠습니다.

메모장에도 표시가 됩니다.

클립보드에는 텍스트가 대표적이지만 함수 내용을 보면 Audio도 있고, 이미지, 파일, Object 타입도 받아들입니다.

그럼 먼저 이미지와 파일을 확인해 보겠습니다.

using System;
using System.Drawing;
using System.Windows.Forms;

namespace Example
{
  // 실행 클래스
  public class Program
  {
    // 싱글 단일 스레드(Clipboard는 윈도우 객체이기 때문에 단일 스레드 설정이 필요하다.)
    [STAThread]
    // 실행 함수
    static void Main(string[] args)
    {
      // 이미지 파일을 읽어 옵니다.
      var image = Bitmap.FromFile(@"d:\work\lena.png");
      // 클립보드에 이미지를 저장합니다.
      Clipboard.SetImage(image);

      // 클립보드에서 이미지를 가져온다.
      image = Clipboard.GetImage();
      // 파일로 저장한다.
      image.Save(@"d:\work\lena-copy.png");

      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

이번에는 이미지를 가져와서 파일에 저장했습니다.

디렉토리를 보시면 이미지 파일이 복사된 것을 확인할 수 있습니다.

이제 다시 그림판을 열어서 Ctrl + V를 해보겠습니다.

그림판에서도 이미지가 붙여넣기가 잘 됩니다.


이번에는 파일을 이동시켜 보겠습니다.

using System;
using System.Collections.Specialized;
using System.Windows.Forms;

namespace Example
{
  // 실행 클래스
  public class Program
  {
    // 싱글 단일 스레드(Clipboard는 윈도우 객체이기 때문에 단일 스레드 설정이 필요하다.)
    [STAThread]
    // 실행 함수
    static void Main(string[] args)
    {
      // 경로 리스트
      StringCollection files = new StringCollection();
      // 파일 경로 추가
      files.Add(@"d:\work\lena.png");
      // 파일 경로 추가
      files.Add(@"d:\work\lena-copy.png");
      // 클립 보드에 저장
      Clipboard.SetFileDropList(files);

      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

프로그램을 실행하고 다시 바탕화면에 대고 Ctrl + V를 누르게 되면 파일이 복사가 되는 것을 확인할 수 있습니다.

저는 파일 경로를 한 디렉토리에 있는 파일을 복사했지만, 디렉토리를 이것저것에 설정하고 하나의 디렉토리에 복사하기 하면 파일이 하나의 디렉토리에 정리되는 효과도 있습니다.


클립보드에서는 텍스트, 이미지, 파일의 복사+붙여넣기가 가장 많이 사용하지 않을까 싶습니다.

이제 사용자가 사용하지는 않지만 프로그램끼리 클립보드 값을 서로 사용하는 경우가 있습니다.

Audio라는 것과 Data라는 것인데..

MSDN에서도 Audio는 오디오 바이너리 스트림을 클립보드에 넣을 수 있다라고 설명되어 있습니다.

링크 - https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.clipboard.setaudio?view=net-5.0

근데 Audio의 바이너리를 복사+붙여넣기를 어떻게 하지?? 저는 이 부분을 Audio값을 넣는 것이 아니고 Stream으로 바이너리를 프로그램끼리 주고 받는 것이 아닐까 생각합니다.

using System;
using System.IO;
using System.Windows.Forms;

namespace Example
{
  // 실행 클래스
  public class Program
  {
    // 싱글 단일 스레드(Clipboard는 윈도우 객체이기 때문에 단일 스레드 설정이 필요하다.)
    [STAThread]
    // 실행 함수
    static void Main(string[] args)
    {
      // 파일을 stream으로 읽어온다.
      using (var stream = new FileStream(@"d:\work\lena.png", FileMode.Open, FileAccess.Read))
      {
        // 클립 보드에 저장한다.
        Clipboard.SetAudio(stream);
      }

      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

아까 lena의 이미지 파일을 Image객체가 아니고 Filestream으로 바이너리 형식으로 읽어와서 클립보드에 저장했습니다.

using System;
using System.IO;
using System.Windows.Forms;

namespace Example
{
  // 실행 클래스
  public class Program
  {
    // 싱글 단일 스레드(Clipboard는 윈도우 객체이기 때문에 단일 스레드 설정이 필요하다.)
    [STAThread]
    // 실행 함수
    static void Main(string[] args)
    {
      // 클립보드로 stream을 받는다.
      using (var stream = Clipboard.GetAudioStream())
      {
        // 파일 스트림 생성하기
        using (var fs = new FileStream(@"D:\work\lena-stream.png", FileMode.Create, FileAccess.Write))
        {
          // stream 복사
          stream.CopyTo(fs);
        }
      }
      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

다른 프로그램에서 Clipboard를 이용해서 Stream을 받고 파일 생성까지 됩니다.

간단한 바이너리 데이터를 주고 받는 것이라면 Clipboard를 통해서 데이터를 주고 받아도 될 것 같네요.

지금까지는 SetText나 SetImage, SetAudio를 통해서 데이터를 Clipboard에 저장하였지만 클래스 자체를 데이터로 넘길 수 있습니다.

using System;
using System.Windows.Forms;

namespace Example
{
  // 실행 클래스
  public class Program
  {
    // 클래스를 시리얼라이즈한다.
    [Serializable]
    class Node
    {
      public string Data { get; set; }
    }
    // 싱글 단일 스레드(Clipboard는 윈도우 객체이기 때문에 단일 스레드 설정이 필요하다.)
    [STAThread]
    // 실행 함수
    static void Main(string[] args)
    {
      // 클립보드에 Node 클래스의 인스턴스를 생성해서 Node의 키로 값을 넣는다.
      Clipboard.SetData("Node", new Node { Data = "Hello world" });
 
      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

이제 위 프로그램을 실행해서 Node 클래스의 인스턴스를 클립보드에 넣습니다.

using System;
using System.Windows.Forms;

namespace Example
{
  // 실행 클래스
  public class Program
  {
    // 클래스를 시리얼라이즈한다.
    [Serializable]
    class Node
    {
      public string Data { get; set; }
    }
    // 싱글 단일 스레드(Clipboard는 윈도우 객체이기 때문에 단일 스레드 설정이 필요하다.)
    [STAThread]
    // 실행 함수
    static void Main(string[] args)
    {
      // Node의 키로 클립보드로 Node 클래스를 읽어온다.
      Node a = Clipboard.GetData("Node") as Node;
      // 콘솔 출력
      Console.WriteLine(a.Data);

      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

다른 프로그램에서 Node키로 값을 취득해오는데 Node를 캐스팅합니다. 해당 Data 변수를 출력하니 Hello world가 나옵니다.


그외의 Clipboard 클래스에서 자주 사용하는 함수는 Clear입니다. 클립보드의 내용을 삭제해 주는 기능이 있습니다.


Clipboard는 잘 이용하면 매우 프로그램을 간단하게 만들 수 있습니다만 문제가 보안에는 취약합니다. 즉, 쉽게 저장하고 가져올 수 있을 만큼 다른 프로그램이나 서버라고 하면 다른 유저가 쉽게 정보를 가져갈 수 있습니다.

그런 점을 좀 생각해서 그냥 Notify용이나 프로그램 Ping용으로는 Clipboard를 이용하는 방법도 나쁘지는 않을 듯 싶네요.


여기까지 C#에서 Clipboard(복사 + 붙여넣기 기능)을 다루는 방법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.