アーカイブ

【Visual Studio】アプリケーションのコマンドライン引数を手軽に変更してデバッグする【.NET】

先日、仕事で簡単なコンソールアプリケーションを作る機会がありました。コマンドライン引数の受け取りパターンがいくつかあり、Visual Studio標準のデバッグ機能では少し不便でした。少しでも楽にデバッグを行う方法がないか試行錯誤しましたので、備忘として残したいと思います。

目次
  1. Visual Studio での単純なデバッグ方法は…
  2. コマンドライン引数のデバッグを楽にするには…
    1. サンプルコード
    2. コードの解説
  3. おわりに

Visual Studio での単純なデバッグ方法は…

VisualStudioでは、アプリケーションのプロジェクトをスタートアッププロジェクトに設定して、「開始」するだけでデバッグを行うことができます。

また、プロジェクト設定からデバッグ実行時のコマンドライン引数を設定することができます。

VisualStudioのプロジェクト設定(クリックで拡大)

単純なデバッグはこれでOKですが、コマンドライン引数の値を頻繁に変えたいような場合は、ここを都度メンテしてデバッグを行うのは面倒です。

コマンドライン引数のデバッグを楽にするには…

コマンドライン引数を、様々な入力(画面入力/外部ファイル/ハードコーディング/etc…)で実行できれば楽です。そのためのテスト用プロジェクトを作成します。

テスト用プロジェクトでは、コマンドライン引数を任意の入力から構築して、デバッグしたいコンソールアプリケーションを実行します。

今回はデバッグを行うことが目的なので、エントリーポイント(Mainメソッド)を直接呼び出して実行してみます。

Process.Startでの起動について
別プロセスでの起動になるため、そのまま起動対象のアプリケーションのコードをデバッグすることはできません。


Mainメソッドが他プロジェクトからアクセス可能であれば、単純に呼び出せばよいのですが、アクセス不可の場合そうもいきません。基本的に公開する意味がないので、アクセス不可の場合がほとんどだと思います。

そのため、今回はアクセス不可の場合を想定してリフレクションを利用して呼び出してみます。

サンプルコード

C#

コンソールアプリケーション本体のソースコード(C#)
using System;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach(string arg in args) {
                Console.WriteLine(arg);
            }

        }
    }
}
テスト用プロジェクトのコード(C#)
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace ConsoleAppTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void execute_Click(object sender, EventArgs e)
        {
            string[] arguments = buildArguments();

            //For Non Public
            System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(@"..\..\..\ConsoleApp\bin\Debug\ConsoleApp.exe");
            assembly.EntryPoint.Invoke(null, new object[] { arguments });

        }

        private string[] buildArguments()
        {
            List<string> arguments = new List<string>();
            arguments.Add("arg1");
            arguments.Add("arg2");
            return arguments.ToArray();
        }

    }
}

VB.NET

コンソールアプリケーション本体のソースコード(VB.NET)
Public Module ConsoleAppVB

    Public Sub Main(ByVal args As String())
        For Each arg As String In args
            Console.WriteLine(arg)
        Next
    End Sub

End Module
テスト用プロジェクトのソースコード(VB.NET)
Public Class Form1

    Private Sub execute_Click(sender As Object, e As EventArgs) Handles execute.Click
        Dim arguments As String() = BuildArguments()

        'For Non Public
        Dim assembly As Reflection.Assembly = Reflection.Assembly.LoadFrom("..\..\..\ConsoleAppVB\bin\Debug\ConsoleAppVB.exe")
        assembly.EntryPoint().Invoke(Nothing, New Object() {arguments})

        'For Public
        ConsoleAppVB.ConsoleAppVB.Main(arguments)
    End Sub

    Private Function BuildArguments() As String()
        Dim arguments As New List(Of String)
        arguments.Add("arg1")
        arguments.Add("arg2")
        Return arguments.ToArray()
    End Function

End Class

コードの解説

Assemblyの読み込み

Assembly読込のソースコード(C#)
System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(@"..\..\..\ConsoleApp\bin\Debug\ConsoleApp.exe");

デバッグビルドしたコンソールアプリケーション配置パスを指定して、アセンブリを読み込みます。ここでは、対象プロジェクトのビルド出力パスを指定しています。

指定パスについて
今回のようにリフレクションによる呼び出しのみで直接参照するコードがない場合、テストプロジェクト側で、対象プロジェクトを「ローカルにコピー」で参照していても、テストプロジェクトのビルド出力パスにコンソールアプリケーションのモジュールは出力されないので注意が必要です。

Mainメソッドの呼び出し

Mainメソッド呼出のソースコード(C#)
assembly.EntryPoint.Invoke(null, new object[] { arguments });

Mainメソッドのオブジェクトは、読み込んだAssmebly.EntryPointで取得することができます。
Invokeで実行します。

第1引数は、メソッドを呼び出すインスタンスを指定します。
今回ように静的メソッドの場合はnullを指定します。

第2引数は、呼び出しメソッドの引数をObject配列で引き渡します。
今回の場合、コマンドライン引数(String配列)1つを引数に持つメソッドのため、String配列1つを要素に持つObject配列を引き渡します。

今回のサンプルではハードコーディングでコマンドライン引数を構築していますが、ここを画面値から取得するなり分岐をかけてやるなりすれば、多少手軽にデバッグが行えるようになると思います。

おわりに

あまり調べても期待する情報が出てきませんでしたが、一般的なやり方ってどうなのでしょう。

プロフィール
筆者:UMAイカ

IT企業に勤務しています。当ブログは、うダツの上がらないサラリーマンがよりよい人生を目指して購入した商品のレビューや、仕事の備忘、生活の知恵を発信します。

アーカイブ