1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

画面上のQRコードを読み取るWindowsアプリの作成

1
Last updated at Posted at 2026-05-06

🎯 背景

Windowsで画面に表示されているQRコードを読み取りたいとき、
ちょうどいいアプリが見つからなかったので自作しました。
ひとまず動きました。
作成にはAI生成を利用しています。

🧩 できること

  • 画面の任意範囲を選択
  • QRコードを読み取り
  • テキストとして取得&コピー

🛠️ 使用技術

  • .NET 10
  • WPF
  • ZXing.Net
  • ZXing.Net.Bindings.Windows.Compatibility

メイン画面

メイン画面です。ひとまず動かしたかっただけなので、雑です。

MainWindow.xaml

<Window x:Class="ScreenQRCodeReader.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:ScreenQRCodeReader"
        mc:Ignorable="d"
        Title="MainWindow" Height="400" Width="250">
    <Grid>
        <StackPanel Margin="10">
            <Button Content="QRコード取得" Height="40" Click="Capture_Click"/>
            <Image x:Name="DebugImage"
       Height="120"
       Stretch="Uniform"
       Margin="0,10"/>
            <TextBox x:Name="ResultText"
                 Margin="0,10,0,10"
                 Height="60"
                 TextWrapping="Wrap"
                 AcceptsReturn="True"/>
            <Button Content="コピー" Height="40" Click="Copy_Click"/>
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Drawing;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using ZXing.Windows.Compatibility;

namespace ScreenQRCodeReader
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Capture_Click(object sender, RoutedEventArgs e)
        {
            this.Hide();

            try
            {
                var selector = new SelectionWindow();
                if (selector.ShowDialog() == true)
                {
                    string? text = null;
                    var bmp = CaptureArea(selector.X, selector.Y, selector.W, selector.H);

                    try
                    {
                        // デバッグ表示
                        this.DebugImage.Source = BitmapUtil.ConvertBitmap(bmp);
                        // QR解析
                        text = DecodeQR(bmp);
                    }
                    finally
                    {
                        bmp.Dispose();
                    }

                    ResultText.Text = text ?? "QRコードが見つかりません";
                }
            }
            finally
            {
                this.Show();
            }
        }

        private string? DecodeQR(Bitmap bitmap)
        {
            var reader = new BarcodeReader();
            var result = reader.Decode(bitmap);
            return result?.Text;
        }

        private Bitmap CaptureArea(int x, int y, int width, int height)
        {
            var bmp = new Bitmap(width, height);

            using (var g = Graphics.FromImage(bmp))
            {
                g.CopyFromScreen(x, y, 0, 0, new System.Drawing.Size(width, height));
            }

            return bmp;
        }

        private void Copy_Click(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrEmpty(ResultText.Text))
            {
                System.Windows.Clipboard.SetText(ResultText.Text);
            }
        }
    }
}

キャプチャ用画面

SelectionWindow.xaml

<Window x:Class="ScreenQRCodeReader.SelectionWindow"
        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:ScreenQRCodeReader"
        mc:Ignorable="d"
        WindowStyle="None"
        AllowsTransparency="True"
        Background="#40000000"
        Topmost="True"
        WindowState="Maximized"
        Title="SelectionWindow">
    <Grid>
        <Canvas x:Name="RootCanvas">
			<TextBlock x:Name="CoordText"
               Foreground="White"
               Background="#80000000"
               FontSize="14"
               Padding="5"
               Visibility="Collapsed"/>
            <Rectangle x:Name="SelectionRect"
                   Stroke="Red"
                   StrokeThickness="2"
                   Fill="#40FFFFFF"
                   Visibility="Collapsed"/>
        </Canvas>
    </Grid>
</Window>

SelectionWindow.xaml.cs

using System;
using System.Drawing;
using System.Windows;
using System.Windows.Input;
using System.Windows.Shapes;
using System.Windows.Controls;

namespace ScreenQRCodeReader
{
    /// <summary>
    /// Interaction logic for SelectionWindow.xaml
    /// </summary>
    public partial class SelectionWindow : Window
    {
        private System.Windows.Point Start;
        private System.Windows.Shapes.Rectangle RangeRect;

        public int X { get; private set; }
        public int Y { get; private set; }
        public int W { get; private set; }
        public int H { get; private set; }

        public SelectionWindow()
        {
            InitializeComponent();
            RangeRect = SelectionRect;

            MouseDown += OnMouseDown;
            MouseMove += OnMouseMove;
            MouseUp += OnMouseUp;
            KeyDown += (_, e) => { if (e.Key == Key.Escape) Close(); };
        }

        private void OnMouseDown(object sender, MouseButtonEventArgs e)
        {
            Start = e.GetPosition(this);
            Canvas.SetLeft(RangeRect, Start.X);
            Canvas.SetTop(RangeRect, Start.Y);
            RangeRect.Width = 0;
            RangeRect.Height = 0;
            RangeRect.Visibility = Visibility.Visible;
        }

        private void OnMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            var pos = e.GetPosition(this);

            var startScreen = this.PointToScreen(Start);
            var posScreen = this.PointToScreen(pos);

            // 座標表示(常に更新)
            CoordText.Text = $"X:{(int)posScreen.X}  Y:{(int)posScreen.Y}";
            Canvas.SetLeft(CoordText, pos.X);
            Canvas.SetTop(CoordText, pos.Y);
            CoordText.Visibility = Visibility.Visible;

            if (e.LeftButton == MouseButtonState.Pressed)
            {
                double x = Math.Min(pos.X, Start.X);
                double y = Math.Min(pos.Y, Start.Y);
                double w = Math.Abs(pos.X - Start.X);
                double h = Math.Abs(pos.Y - Start.Y);

                Canvas.SetLeft(RangeRect, x);
                Canvas.SetTop(RangeRect, y);
                RangeRect.Width = w;
                RangeRect.Height = h;

                // 範囲サイズも表示
                CoordText.Text += $"  W:{(int)w} H:{(int)h}";
            }
        }

        private void OnMouseUp(object sender, MouseButtonEventArgs e)
        {
            var end = e.GetPosition(this);

            var startScreen = this.PointToScreen(Start);
            var endScreen = this.PointToScreen(end);

            double x = Math.Min(startScreen.X, endScreen.X);
            double y = Math.Min(startScreen.Y, endScreen.Y);
            double w = Math.Abs(startScreen.X - endScreen.X);
            double h = Math.Abs(startScreen.Y - endScreen.Y);

            if (w < 1 || h < 1) { Close(); return; }

            this.X = (int)x;
            this.Y = (int)y;
            this.W = (int)w;
            this.H = (int)h;

            DialogResult = true;
            Close();
        }
    }
}


Bitmap操作

BitmapUtil.cs

using System;
using System.Windows.Media.Imaging;
using System.Runtime.InteropServices;
using System.Drawing;

namespace ScreenQRCodeReader
{
    internal class BitmapUtil
    {
        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        public static BitmapSource ConvertBitmap(Bitmap bitmap)
        {
            IntPtr hBitmap = bitmap.GetHbitmap();

            try
            {
                return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                    hBitmap,
                    IntPtr.Zero,
                    System.Windows.Int32Rect.Empty,
                    BitmapSizeOptions.FromEmptyOptions());
            }
            finally
            {
                DeleteObject(hBitmap); // メモリリーク防止
            }
        }
    }
}

工夫ポイント

  • ウィンドウを一旦 Hide() して自分を写さない
  • PointToScreen() で正確な座標取得

はまったところ

  • 選択枠がキャプチャに写る問題
  • DPIスケーリング
    → 環境によって座標ズレる可能性あり?

まとめ

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?