반응형

프로그램 화면

프로그램은 아래와 같은 순서로 동작하게 끔 만들어졌다.

만들고 있는 프로그램이 있어서 우선 시간관계상 Exception 체크와 기본 동작은 제외하고

정상동작을 가정하여 만들어졌다.

 

1. [가져오기] 버튼으로 현재 윈도우 창이 있는 프로세스를 가져온다.

2. 콤보박스에서 캡쳐할 프로세스를 선택한다.

3. 영역캡쳐 버튼을 누르면 캡쳐된 화면을 Image 컴포넌트에 뿌려준다.

 

XAML

<Window x:Class="SimcityBuilditMacro.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SimcityBuilditMacro"
        mc:Ignorable="d"
        Title="MainWindow" Height="564.7" Width="773.021">
    <Grid>
        <Image x:Name="imgCapture" Margin="10,10,0,0" HorizontalAlignment="Left" Width="744" Height="484" VerticalAlignment="Top"/>
        <ComboBox x:Name="cbxProgramAreaCapture" HorizontalAlignment="Left" Margin="92,499,0,0" VerticalAlignment="Top" Width="582" ItemsSource="{Binding}"/>
        <Button x:Name="btnProgramAreaCapture" Content="영역캡쳐" HorizontalAlignment="Left" Margin="679,499,0,0" VerticalAlignment="Top" Width="75" Height="22" RenderTransformOrigin="0.493,0.75" Click="BtnProgramAreaCapture_Click"/>
        <Button x:Name="btnProcessRead" Content="가져오기" HorizontalAlignment="Left" Margin="10,499,0,0" VerticalAlignment="Top" Width="75" Height="22" Click="BtnProcessRead_Click"/>

    </Grid>
</Window>

 

 

CS

WPF 에서는 Process 객체를 통해 Process의 정보를 가져올 수 있다.

하지만, Process를 통해 얻은 Handle 을 Graphics 객체에 바로 사용하면, OutOfMemory 가 발생한다.

 

그렇다고 MainWindowHandle을 바로 사용한다면, 캡처대상의 윈도우가 활성화 되어 있으면 문제가 없다. 하지만 대상 프로세스가 최소화 되어있다면 ShowWindowAsync 와 SetForegroundWindow 을 써서 활성화를 시켜줘야한다. 이 때는 Thread Sleep을 걸어야하므로 캡처에 쓸데없이 시간이 더 걸리게 된다.

 

그러는 것보다 아예 User32의 FindWindow 를 통해 Handle을 획득하면 비활성화 상태에서도 캡처가 가능하다.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Media.Imaging;

namespace SimcityBuilditMacro
{
    /// <summary>
    /// MainWindow.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();
        }


        [DllImport("user32.dll")]
        private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);

        [DllImport("user32.dll", SetLastError = true)]
        static extern int GetWindowRgn(IntPtr hWnd, IntPtr hRgn);

        [DllImport("gdi32.dll")]
        static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);


        private Bitmap ProcessCapture(string processName)
        {
            IntPtr hwnd = FindWindow(null, processName);

            Rectangle rc = Rectangle.Empty;
            Graphics gfxWin = Graphics.FromHwnd(hwnd);
            rc = Rectangle.Round(gfxWin.VisibleClipBounds);

            Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb);

            Graphics gfxBmp = Graphics.FromImage(bmp);
            IntPtr hdcBitmap = gfxBmp.GetHdc();

            bool succeeded = PrintWindow(hwnd, hdcBitmap, 1);
            gfxBmp.ReleaseHdc(hdcBitmap);
            if (!succeeded)
            {
                gfxBmp.FillRectangle(
                    new SolidBrush(Color.Gray),
                    new Rectangle(System.Drawing.Point.Empty, bmp.Size));
            }

            IntPtr hRgn = CreateRectRgn(0, 0, 0, 0);

            GetWindowRgn(hwnd, hRgn);

            Region region = Region.FromHrgn(hRgn);
            if (!region.IsEmpty(gfxBmp))
            {
                gfxBmp.ExcludeClip(region);
                gfxBmp.Clear(Color.Transparent);
            }
            gfxBmp.Dispose();

            //bmp.Save("C:\\SaveFileTest\\test.bmp");

            return bmp;
        }

        private void BtnProcessRead_Click(object sender, RoutedEventArgs e)
        {
            Process[] prcs = Process.GetProcesses();
            List<string> prcsList = new List<string>();
            foreach (Process p in prcs)
            {
                if (p.MainWindowTitle.Equals(""))
                {
                    continue;
                }
                prcsList.Add(p.MainWindowTitle + "|" + p.Id);
            }

            cbxProgramAreaCapture.ItemsSource = prcsList;
        }

        private BitmapImage Bitmap2BitmapImage(Bitmap src)
        {
            using (MemoryStream memory = new MemoryStream())
            {
                src.Save(memory, ImageFormat.Bmp);
                memory.Position = 0;
                BitmapImage bitmapimage = new BitmapImage();
                bitmapimage.BeginInit();
                bitmapimage.StreamSource = memory;
                bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapimage.EndInit();

                memory.Close();

                return bitmapimage;
            }
        }

        private void BtnProgramAreaCapture_Click(object sender, RoutedEventArgs e)
        {
            string prcsName = cbxProgramAreaCapture.SelectedItem.ToString();
            imgCapture.Source = Bitmap2BitmapImage(ProcessCapture(prcsName));
        }
    }
}

 

 

 

반응형

WRITTEN BY
데르벨준

,