豊田 孝

 前回は,ANSI準拠のC言語で記述されたサンプル・プログラムを紹介いたしました。サンプル・プログラムが動作を開始すると,mainという名称を持つ特殊な内部関数がWindowsシステムから呼び出されていました。今回は,前回とまったく同じサンプル・プログラムを「.NET対応Cサンプル・プログラム」として作成します(サンプル・プログラムのダウンロードはこちら)。2つのサンプル・プログラムの間には,いったいどのような相違があるのでしょう。さっそく本論に入りましょう。

.NET対応サンプル・プログラムの作成方法

 .NET対応プログラムをVisual Studio .NETで作成する場合,通常は,目的のプロジェクト・ウィザードを起動し,必要なデータとソース・コードを入力していきます。これは従来のWindowsアプリケーションやMFC(Microsoft Foundation Classes)アプリケーションを作成する方法と基本的に変わりません。前回紹介したサンプル・プログラムもこのような方法で作成しています。

 しかしご承知のように,プログラミングというものは,まずソース・テキスト・ファイルを作成し,それをコンパイラとリンカに処理させ,最終的に,バグのないバイナリ・ファイルに変換する作業のことを指します。この作業で中心となるコンパイラやリンカは,プログラム開発用の特殊なプログラムとはいえ,プログラムであることには変わりがありません。ほぼすべてのプログラムは,ユーザからの指示を受け,それに従って動作します。

 前回は,Visual Studio .NET環境が内部で起動するコンパイラとリンカに対して,「.NET対応コードを一切挿入しないように」という指示を出した上で,バイナリ・ファイルを作成しました。このようなコンパイラやリンカへの指示を出す操作を,専門的には「コンパイラやリンカのスイッチを切り替える」と呼んでいます。

 今回は,「.NET対応コードを挿入するように」という指示を出し,バイナリ・ファイルを作成します。一部の方は,スイッチの種類やその機能上の意味,生成されるバイナリ・ファイルの内部構造などに興味を持っていることでしょうが,今回はそこまでは踏み込まないことにします。

図1●.NET対応Cサンプル・プログラムの実行画面
 生成されたバイナリ・ファイル(プログラム)を実行すると,図1[拡大表示]のような画面が表示されます。

 ご覧のように,連載回数を示す数字のみが異なるだけで,前回とまったく同じ情報が表示されます。つまり,表面上は,前回のサンプル・プログラムと今回のサンプル・プログラムの間には相違がないように見えます。しかし今回は,コンパイラとリンカに対して「.NET対応コードを挿入するように」という指示を出してありますから,2つのサンプル・プログラム間には何らかの相違があるはずです。それでは,どのような相違があるのかを見てみましょう。

.NET対応Cサンプル・プログラムの特徴

図2●前回のサンプル・プログラムをVisual Studio .NETのデバッグ環境で実行
図3●.NET対応Cサンプル・プログラムをVisual Studio .NETのデバッグ環境で実行
 まず,前回のサンプル・プログラムを起動すると,図2[拡大表示のようにmain関数という特殊な関数が最初に呼び出されていました。

 そこで,Visual Studio .NETのデバッグ環境(より正確には,ステップ・イン・モード)で今回のサンプル・プログラムを実行してみました。すると,図3[拡大表示]のような画面が表示され,サンプル・プログラムは一時停止状態に入りました(Windows 2000環境では停止しますが、Windows XP環境では停止しないと思います)。

 2つの画面を見比べると分かるように,今回はmain関数が呼び出されているのではなく,“_CorExeMain”という関数が呼び出されています。_CorExeMain関数は,mscoree.dllというファイルに実装されている関数の一つです。そしてこのmscoree.dllは,「Microsoft .NET Runtime Execution Engine」という名称を持っています。結論を言えば,今回のサンプル・プログラムは,OSにより直接実行されるのではなく,.NETランタイム実行エンジンにより実行される「.NET対応Cプログラム」といえます。

 実行エンジンの採用は.NET世界に固有のものではありません。例えば,すでにご存知の方も多いと思われますが,VBScriptコードを含む拡張子.VBSを持つテキスト・ファイルが実行されるときには,VBScript.dllという実行エンジンが使われています。つまり,OSであるWindowsシステムが直接実行していたわけではありません。同じように,MicrosoftのJavaScriptバージョンであるJScriptコードを含む拡張子.jsを持つファイルはJScript.dllが処理していました。

 こうしたことから,これら2つの画面は私たちに次のようなことを教えてくれています。

(1)前回のサンプル・プログラムは,いわば単純なWin32コンソール・アプリケーションであるため,起動時にはmain関数が最初に呼び出されていた。これは,前回のサンプル・プログラムがWindowsシステムと密接な関係を持っていることを示している。

(2)しかし今回のサンプル・プログラムでは,最初に呼び出される関数は_CorExeMainという関数である。これはコンパイラやリンカのスイッチを切り替えた影響であるとともに,Windowsシステムとの密接な関係が薄れていることを示している

.NET対応Cサンプル・プログラムの真の姿

 それでは,今回の.NET対応Cサンプル・プログラムが持つその他の特徴を見てみましょう。前回,「.NET世界では関数の役割が変更される」と申し上げたと思います。そこで,次の情報を見ていただきましょう。

___[STM] main
___[STM] helpercheckcompilerinfo
___[STM] showVariable

 この情報は,Visual Studio .NETに付属するILDASM.EXEというユーティリティを使って,本日のサンプル・プログラムから直接取り出しています。mainやshowVariableなどはサンプル・プログラム内で使われている関数名ですが,.NETの世界ではこれらの関数はSTM,つまり,Static Method(静的メソッド)と呼ばれていることがわかります。.NETの世界では,関数の役割が変更され,関数という概念自体が存在しないことを示しています。

 メソッドというのは,一般的には,「クラスのメソッドを呼び出す」や「インスタンスのメソッドを呼び出す」という表現から分かるように,クラスを中心とするオブジェクト指向の世界の用語です。コンパイラやリンカのスイッチを切り替える操作は,ちょっとした技術を身に付ければ,簡単な部類の操作に入ります。しかし,2つのサンプル・アプリケーションの間に発生しているのは,「関数からクラスへ」というパラダイム・シフトであり,発想の転換が求められるのです。

 発想の転換は,簡単な部類の操作には入りません。次回は,今回のサンプル・プログラムをクラスを使って書き換えます。「関数からクラス」へというパラダイム・シフトはいったいどのような意味を持っているのでしょう。次回使用するプログラミング言語は,C++ですが,第5回連載ではVisual C# .NETに,第6回連載ではVisual Basic .NETにそれぞれ移植する予定です。

 それではまたお会いいたしましょう。ごきげんよう。