Top > Programmingとか > VB / VB.NET > Microsoft.VisualBasic.dllを取り込まないビルド

2005年02月14日

04 Microsoft.VisualBasic.dllを取り込まないEXEの作り方

検証環境 自作ATX(PenIII1.2GHz/GA-6VTXE/512MB)
WinXPPro(2002)SP2/VS.NET2003(7.1.3091) & .NET Framework1.1(1.1.4322SP1)/MSDN-Lib Oct,2004

Mainをどぅにかする前に。

前項でちらっと記述したんですが、ふつーにビルドすると、起動時にコマンドプロンプトウィンドウが表示されてしまうんですね。
でも、DLLでビルドして他EXEからCallするとコマンドプロンプトウィンドウは表示されませんでした。

ってことは、プロンプトが出るか出ないかは、/targetパラメータの問題なのかなー。

と思い、msdnLibraryで調べてみたら、Visual Basic コンパイラ オプション - /targetにしっかり書いてありました。

Windows プログラムを作成するには、/target:winexe を使用します。

…そりゃそーか。
VC++だって省略時デフォルトはコンソールEXEだったもんなぁ。WINEXEって明示的に指定してやればおっけーなだけな話でした。うかつー。



さて、本題。標準モジュールを使わないで単独動作するEXEを作りたい。

うーんと。
VB.NETのスタートアッププロシージャには、「Sub Main」の他に「Form_Load」もあったんではなかったっけ。
FormクラスはSystem.Windows.Forms.Form名前空間で定義されているクラスなので、Microsoft.VisualBasic名前空間とは無関係のはずだよなぁ。

てことで、Formから起動するソースファイルを起こしてみました。
これはさすがにテキストエディタで書くのはしんどいので、VS.NET IDEのフォームエディタで生成した.vbファイルをそのまま使わせてもらうことにします。

中になんにもコードを追加しないで、フォームエディタで作ったままの素のファイルをコマンドラインからコンパイル。

vb01-04-01.gif

…うわーキビしー。
ちょっとまず、エラー数を減らしましょう。

「基本クラス'System.ComponentModel.Component'を含むアセンブリ'System'への参照が必要です。」
「型'System.Drawing.Size'が定義されていません。」

あたりは、System.dllとSystem.Drawing.dllを /reference で指定してやる必要があります。のでとりあえずそれをパラメータに追加して。

vb01-04-02.gif

…だめだ、やっぱり「'Sub Main'が見つかりません」と出てくる。
未確認ですが、IDEからForm起動でビルドすると、IDEかMicrosoft.VisualBasic.dllかでダミーのMain( )を追加または「あるものとみなす」ような処理をしているんではないでしょうか。
コマンドラインからMicrosoft.VisualBasic.dllを明示的に取り外しちゃってビルドしているわけですから、IDEの助けもMicrosoft.VisualBasic.dllの助けも受けられない。
我ながら、なんだか無意味にキツいことをやっているよぅな気になってきましたよ。

うーん、やけくそ。Formクラスの中に、無理矢理Main( )を書いてみました。

Public Sub Main()
Dim frmIts As New sal_Verify01_06
frmIts.ShowDialog()
frmIts.Dispose
End Sub

結果。

vb01-04-03.gif

やっぱり無理か。まぁ当たり前なんですが。

そ言えば、「BC30737」ってどんなエラーなんだろぅ。
msdnLibraryに載ってるのかなぁ。

ってんで、「bc30737」でmsdnLibraryを検索してみると、うわーツリーからはたどれない位置にひっそりとありました。
これ、どーいぅわけだかmsdn onlineからは検索できません。ので、CD/DVDからmsdnLibraryをインストールしていないと読めません。

しかたがないので、ここでは説明の一部を直接引用します。

コマンド ライン アプリケーションでは Sub Main を定義する必要があります。 Main をクラス内で定義する場合は Public Shared として宣言し、モジュール内で定義する場合は Public として宣言する必要があります。

…えっ?Main( )ってクラス内にも定義できるんだ!

大あわてで前述のソースを

Public Shared Sub Main()
Dim frmIts As New sal_Verify01_06
frmIts.ShowDialog()
frmIts.Dispose
End Sub
と直して再度チャレンジ。

vb01-04-04.gif

通ったー!!

実行結果。

vb01-04-05.gif

MSILの取り込み状況。

vb01-04-06.gif

完璧だー!!

いゃぁ疲れたー。



ちなみに。
上記例では呼び出される側のクラスの中に呼び出すMain( )を書くといぅわけのわかんないことをやっていますので、別のクラスをPublicで作成してその中にMain( )を書くのがスジかと。

ついでに、VS.NET IDE(devenv.exe)経由でもコマンドラインコンパイルは可能らしく、この場合は「.vb」ファイルではなく「.sln」ファイルを起点にコンパイルされるそぅです。
確かにこれならプロジェクトレベルでの参照関係を持つ複数プロジェクトをコンパイルできるんで、IDEからコンパイルかけるとの同じ状況になりますね。
ただし、この場合に /novbruntimeref パラメータが受け入れられるかどぅかはわかりません。
気が向いたら、いつかやってみよぅと思います。

2005年02月13日

03 Microsoft.VisualBasic.dllを取り込まないコマンドラインオプション

検証環境 自作ATX(PenIII1.2GHz/GA-6VTXE/512MB)
WinXPPro(2002)SP2/VS.NET2003(7.1.3091) & .NET Framework1.1(1.1.4322SP1)/MSDN-Lib Oct,2004

あきらめきれないなー、といぅわけで、Googleで検索かけてみることにしました。

プログラミング系の情報をGoogleで探したい時のコツは、

  • なるべく正確な用語を使用する。しかもできるだけ英語で。
  • 日本でヒットしない場合はWeb全体に検索範囲を広げ世界中から探す。
ってあたりだと勝手に思っているんですが。

今回のケースは「vbc.exeでMicrosoft.VisualBasic.dllを取り込まないコマンドラインオプションはないか」ですから、「Microsoft.VisualBasic.dll vbc」で探してみることにしてみました。
実際に検索してみると、日本で12件中8件、Web全体で296件中96件(いずれも2005.02.13時点)。

もちろんできれば日本語の方が私には理解しやすいので、ヒットされたページを全部ざっくり読んでみて。
…うーん、やっぱりないかぁ。なんかCompact Frameworkのページが多いのは、コマンドラインでしかビルドが提供されていないからなんでしょうか。いぇほんとぅにそぅなのか私は知らないんですが。

で、Web全体の方で見てみると。

のべ6件目に「(Non)Removal of Microsoft.VisualBasic Assembly」(Microsoft.VisualBasicアセンブリの除去…できません)ってページが。
おー?なんか私と同じテーマで嘆いているページなのかしら、と見てみたら、Dave Donaldsonさんといぅ方のBlogで。いゃなんだか主にVB.NETの方向にフカい記述てんこ盛りでしばしぼけーっと眺めてみたり。 いゃ本題に戻って。
上記blogページの最後のコメントで、Fredrikさんといぅ方の

I managed to remove the reference by compiling as "vbc /novbruntimeref HelloWorld.vb /r:Microsoft.VisualBasic.dll" Nice :)
私は"vbc ~"としてコンパイルすることで、なんとか参照を取り除いてるよ。ナーイス(^^)

えっ「ナーイス」?

いゃ問題はそこではなく。

/novbruntimeref ?

なんだそのパラメータは。
msdnLibraryのVisual Basic コンパイラ オプション一覧には載っていませんよ?

今度は「vbc novbruntimeref」でGoogle検索(Web全体)。…わずか2件。「novbruntimeref」だけでも7件中4件。
ヒットしたページのひとつ、Matthew Reynolds' .NET 247(TM)What is VB.NET anyway?でMattias Sjogrenさんが

You can, if you compile from the command line and use the (undocumented) /novbruntimeref parameter. That will, of course, cause a bunch of compile errors if your code uses anything in the VB runtime library.
コマンドラインコンパイルで(非公開の)「/novbruntimeref」パラメータを使えば(VBランタイムライブラリを取り除いたプロクラムの生成は)できるよ。もちろんこの場合、VBランタイムライブラリで定義されている何かをコードに書いちゃうとコンパイルエラーだからね。

…あー。非公開パラメータでしたか。
なんだか旧VBでの時のAddressOf演算子を思い出すなぁ。(これ、途中のバージョンまで非公開の機能だったんです。)

なんでこの人がたはこんな非公開情報をさくっと知っているんだろぅ。く、くやしいなぁ。
Microsoftの開発チーム周辺から非公式に教えてもらったんでなければ、vbc.exeそのものを解析したとしか思えません。
ちくしょぅ、人生は不公平だっ。



などと我が身の至らなさを嘆いていてもしょーがないので、このnovbruntimerefって奴がホントにツカエる隠しパラメータなのか、確認してみることにします。

やり方は簡単、前項で使ったソースファイルを、そのまま「novbruntimeref」オプションつけてコンパイルしてみればいいんです。

vb01-03-01.gif

おぉっ。確かに.NET Frameworkコアのランタイムmscorlib.dllと、コマンドラインで明示したSystem.Windows.Forms.dllしか読み込んでいないよ!

vb01-03-02.gif

ってあらっ。
Microsoft.VisualBasic.dllで定義されている機能は一切使っていないはずのソースなのに、「それがない」ってエラーが出ている。

「クラス'Microsoft.VisualBasic.CompilerService.StandardModuleAttribute'が見つかりません。」って…

あっ、標準モジュールってMicrosoft.VisualBasic.dllで定義されているのか!

考えてみれば.NET Frameworkにはクラスしか用意されていないはずで、標準モジュールはVB.NET専用にカスタマイズされたクラスなわけだから、その定義はMicrosoft.VisualBasic.dllの中でされていると考えるのが自然だよなぁ。言われりゃたいへんごもっとも、な話ではあります。

などと感心している場合ではないっす。標準モジュールがダメなら、Main( )をどこに書けといぅんだ。

まぁこれもいいや、後回し。
まずは /novbruntimeref でコンパイルが通って、Microsoft.VisualBasic.dllを取り込まない実行形式ファイルが生成され、それが正常に動作することを確認したいです。

てことで、前項で提示したソースを一般的なクラスに書き直し。

Imports System.Windows.Forms
Public Class sal_Verify01_04
Sub TestMethod()
MessageBox.Show("OK?" _
, "sal_Verify01-04" _
, MessageBoxButtons.OK _
, MessageBoxIcon.Information)
End Sub
End Class
で、EXEではなくDLL(クラスライブラリ)としてコンパイルが通るよぅに、/target:libraryコマンドラインオプションを追加して。

vb01-03-03.gif

これでsal_Verify01-04.vbと同じフォルダにsal_Verify01-04.dllができるので、

vb01-03-04.gif

VS.NET IDEで新規プロジェクトを作成して、sal_Verify01-04.dllを参照設定かけて、これをCallするコードを記述して実行。

vb01-03-05.gif
(クリックで拡大します)

結果。

vb01-03-06.gif

で、MSILの.NET Frameworkライブラリ取り込み状況。

vb01-03-07.gif
OK!Microsoft.VisualBasic.dllが取り込まれていないぞ!!

よーし、ここまでなんとか来ました。
あとはMain( )をどぅにかするだけなんだけどなぁ。

…どーしよぅ?

02 コマンドラインからコンパイルしてみる

検証環境 自作ATX(PenIII1.2GHz/GA-6VTXE/512MB)
WinXPPro(2002)SP2/VS.NET2003(7.1.3091) & .NET Framework1.1(1.1.4322SP1)/MSDN-Lib Oct,2004

で。
なんとかこのMicrosoft.VisualBasic.dllを取り込まない形でビルドすることはできないか、ってことを考えてみたいわけです。

そもそもプロジェクトの[参照設定]には「Microsoft.VisualBasic」は存在しないんですよね。

vb01-02-01.gif

この状態でなんで参照できているのかなぁ。
で、メニューから[プロジェクト]-[プロパティ]-[インポート]と見てみると、こちらには「Microsoft.VisualBasic」がインポートされています。

vb01-02-02.gif
(クリックで拡大します)

じゃあインポートを削除しちゃえばいいのかなぁ。

ってやってみましたけど結果は同じ。あいかわらずMSILの中では「Microsoft.VisualBasic」が取り込まれています。
MsgBoxとかVisualBasic名前空間に属する機能をコードに記述すると「名前'MsgBox'は宣言されていません」とかエラーが出ますが、これは単に名前空間の省略が効かなくなっているだけで、「Microsoft.VisualBasic.MsgBox」って記述するとビルドが通っちゃうので。
このインポートって奴は実コードのインポートではなく、名前空間の定義の取り込みみたいなんですね。…JITに対応するってことはそーいぅことなんだなぁ。

うーん。
あと取り込むアセンブリとかをコントロールできそぅなところが見つからない…。

なんかないかなぁとmsdnLibraryをあちこち読んでいたら、コンパイル コマンド ラインのサンプルってページに、「コマンド ラインからコンパイルする場合、/reference コンパイラ オプションを使用して Microsoft Visual Basic ランタイム ライブラリを明示的に参照する必要があります。」って一文が。

てゅうことは、逆にコマンドラインから「/referenceコンパイラオプション」をつけないでやればMicrosoft.VisualBasic.dllが外せるのか?



VB.NETはVS.NET IDEからコンパイルするのがふつーですが、昔ながらのコマンドラインコンパイラ(vbc.exe)も用意されています。
しかもこのvbc.exe、VS.NETではなく.NET Framework側の機能として用意されていますので、VS.NETを買わなくとも、無償で.NET Framework SDKを入手しちゃえば最低テキストエディタとvbc.exeでVB.NET製プログラムの製造はできるんですね。…いちおぅ。

「いちおぅ」ってのは、VS.NETで提供されている「効率よく開発を行う」機能がまったく使えなくなっちゃうのでやっぱり使いにくいんですね。ので、今どきの皆さんにはあまり積極的にはお奨めしませんが、まぁ毛嫌いするほどのモノでもありません。

で、vbc.exeでのコンパイルは、コマンドプロンプトから実行してやる必要があります。
ホントは簡単にコンパイルできるよぅに最初に環境変数を整えた方がいいんですが。環境変数を整えるバッチファイル(vsvars32.bat)はどーいぅわけかVS.NETのインストールフォルダ下のCommon7\Toolsフォルダ(デフォルトでインストールするとC:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools)にあるんですよね。
自前で用意してできないバッチじゃありませんが、かなりめんどいのも確かです。せっかくコンパイラを.NET Frameworkで提供してるんですから、このバッチも.NET Framework側で用意してほしかったなぁ。

まぁそれはそれとして。

  1. Windowsの[スタート]メニューから[すべてのプログラム]-[アクセサリ]とたどり、コマンドプロンプトを起動します。(XPの場合)

  2. cd (VS.NETのインストールフォルダ下のCommon7\Toolsフォルダ)と入力してEnterを押します。
    フォルダパスにスペースがある場合は、パス文字列の前後を「"」(ダブルクォーテーション)でくくる必要があります。
    めんどい場合は、「cd(スペース)」まで入力してから、移動したい先のフォルダをエクスプローラからコマンドプロンプトにドラッグ & ドロップすると楽ちんかもしれません。

  3. vsbars32と入力してEnterを押します。

  4. cd (コンパイルしたいソースファイルのあるフォルダ)と入力してEnterを押します。
ここまででコンパイルの準備完了。
vsvars32.batとかコンパイルしたいソースファイルがcドライブ以外にある場合は、適宜d:とかe:とかカレントドライブを切り替えるのも忘れないでください。
なんのことを言っているのかよくわからない方は、今さらですがMS-DOSの入門書を読んでみてください。

さて。今度はコンパイルするソースファイルを用意しなくちゃいけませんね。
テキストエディタでVB.NETの文法にのっとったテキストファイルを作り、「.vb」の拡張子で保存すれば、ソースファイルのできあがりです。

Public Module sal_Verify01_03
Sub Main()
MessageBox.Show("OK?" _
, "sal_Verify01-03" _
, MessageBoxButtons.OK _
, MessageBoxIcon.Information)
End Sub
End Module
起動すると無意味なメッセージボックスが表示されるだけ。まぁ動作が検証できればいいので、凝った作りにしてもねぇ。

で、さっそくコンパイルしてみます。

msdnLibraryの記述どおりにコンパイルするなら、vbc /reference:Microsoft.VisualBasic.dll sal_Verify01-03.vbって感じで。
実行結果はこちら。

vb01-02-03.gif

…あら?エラー。

えーと、BC30451ってエラーは、主に使っている機能の名前空間がうまく指定されていない場合に発生します。
調べてみると、MessageBoxとそれ用の定数はSystem.Windows.Formsにあるので、まずはソース上名前空間を省略できるようにImports行を追加。

Imports System.Windows.Forms
Public Module sal_Verify01_03
Sub Main()
MessageBox.Show("OK?" _
, "sal_Verify01-03" _
, MessageBoxButtons.OK _
, MessageBoxIcon.Information)
End Sub
End Module
でもってコマンドラインも、r:/"System.Windows.Forms.dll"を追加。
「/reference」は「/r:」と省略できるんですね。ので前述のMicrosoft.VisualBasic.dllも省略して。
ついでにコンパイル動作の詳細を表示してくれるパラメータ/verboseも追加して。の結果。

vb01-02-04.gif

OK。で実行してみる。

vb01-02-05.gif


OK。なんかバックにプロンプトウィンドウが出てしまぅけど、そこらへんはまぁ後でなんとかすることとして。

当然ながらこぅやって作ったEXEにはMicrosoft.VisualBasic.dllが取り込まれているわけで。

vb01-02-06.gif

さて、ここからよぅやっと本番。
「/r:Microsoft.VisualBasic.dll」を外してコンパイルしてみるとどぅなるか。

vb01-02-07.gif

…だめじゃん。

「/r:Microsoft.VisualBasic.dll」なんてつけてもつけなくても、
自動的にMicrosoft.VisualBasic.dllは取り込まれるじゃん。

上述のmsdnLibraryの記述はなんだったの(T-T)。

ひょっとして.NET Framework1.0の頃は必ず記述しなければならなかったけれども1.1では省略しても暗黙で取り込んでくれるよぅになったとか。でもmsdnLibraryの記述の方まではまだそこらへんが修正されていなかったりとか。

といぅ推測も成り立ちはしますが、別にこの推測の検証をしたってどぅにもなりません。
知りたいのは、Microsoft.VisualBasic.dllを取り込まないビルド方法なんですから。

…あきらめちゃおっかなー(T-T)。

2005年02月11日

01 VB.NET製のプログラムにはMicrosoft.VisualBasic.dllが必須

検証環境 自作ATX(PenIII1.2GHz/GA-6VTXE/512MB)
WinXPPro(2002)SP2/VS.NET2003(7.1.3091) & .NET Framework1.1(1.1.4322SP1)/MSDN-Lib Oct,2004

いきなりですが、先日Sugiさんとお話しさせていただいていた際に、

VB.NET製のプログラムにはMicrosoft.VisualBasic.dllが
必ずImportされて、はずせない

と教えていただきました。

え、うそ。

それは単にデフォルトでImportされるよぅになっているだけで、明示的にはずせばはずせるんではないんですかい。
あるいはVisualBasic名前空間に属するモノを使っているからであって、.NET Frameworkクラスライブラリの機能だけを使えばいいんではないんですかい。

などといろんな考えが頭の中をぐるぐるしてきました。

こんな時は、実際に調べてみることに限ります。ので、調べてみることにしましょう。



まず、前提として。

VB.NETとかC#とかで作ったプログラムは、.NET Frameworkといぅシステムの上で動作します。
この.NET Frameworkって奴がちょっとくせ者で、ライブラリなんだかOSの拡張機能なんだかなかなかピンと来にくい位置付けで動作する代物なんですが。

いちおぅ.NETの発表時の説明では「.NET Framework上で動作するプログラムは、プログラムソースをビルド時にMSILといぅ中間言語にコンパイルし、どのプログラミング言語で製造されたかに関係なく実行時に随時(もしくはインストール時に一括で)実行環境に合わせたマシン語に改めてコンパイルされて動作する」ってことだったので。

あー、VB.NETでもC#でも配布時には同じMSILになってしまぅんだなぁ。
文法が違うだけで、どの言語を選んでも同じものが配布できるんだなぁ。

と漠然と認識していたんですが。

その後いろいろ情報収集していくと、どぅもCLS(.NET Frameworkの中で実行を司る部分)は同じでも、言語によって実装できる範囲が違うとかいぅことがわかってきました。
CLSでは用意されている機能だけど、VB.NETからは使用できない(演算子のオーバーロードとか)、C#で実装するにはエラくめんどい(Office連携とか)など、言語によってCLSへのアプローチが異なるわけですね。

まぁここらへんまでは言語ごとの特性、一長一短として笑っていられたんです。

でも、「VB.NETだけVisualBasic名前空間のImportが必須」なんてことになると、これはちょっといかがなものかと。

もちろんVisualBasic名前空間も「名前空間」ってですから.NET Frameworkクラスライブラリの一部です。ので、その気になればC#などからでもImport(とは呼ばないのか?よくわかりませんが)して機能を使用できるんですが。
が、「必要な場合に取り込んで使用できる」と「用がなくても必ず取り込んでしまう」は違いますでしょう。

旧VBでもランタイムの存在をさんざん疎ましく思ってきたのに、.NETになってもVBだけコブ付きですか?

それはちょっと悲しいではないでしょぅか。つか私は悲しいです。



で、まずはそれがほんとぅなのか?ふつーにビルドした場合にMicrosoft.VisualBasic.dllが必ずImportされるのか?ってところから確認してみましょう。

ふつーにVS.NET2003を起動して。
ふつーに[スタートページ]から[新しいプロジェクト]を選択して「Visual Basicプロジェクト」→[OK]して。
できあがったプロジェクトのソリューション構成を[Release]に変更して。
ビルドして。
終了。

これで、Formを表示して終了するだけのプログラムができあがり。
Form自体はVB.NETではなく.NET FrameworkのSystem.Windows.Forms名前空間で提供されている機能なので、これだけなら言語に無関係にほぼ同じ構造のMSILになるはずです。
で、できあがったEXE(MSIL)の中の構造を見るには.NET Framework IL Disassembler (ildasm.exe)を使います。
ildasm.exeはVS.NETのインストールフォルダ(デフォルトでインストールするとC:\Program Files\Microsoft Visual Studio .NET 2003)の中の\SDK\v1.1\Binにあります。
スタートメニューには入りませんが、これは不親切なのではなく、開発ツールをインストールするとツール類はごくふつーにそんな感じです。

さて、上記で作成したEXEをildasm.exeで読み込むと、こんな感じになります。

vb01-01-01.gif

表示されているツリーの[MANIFEST]をダブルクリックすると、参照されているアセンブリ(この場合はImportされたクラスライブラリ、と思っていいです)の一覧が表示されます。

vb01-01-02.gif

あー確かにかっきり入ってるー。



上記の確認は、「VB.NETのスケルトンはVisualBasic名前空間を必要とする機能をデフォルトで組み込んでるのではないか」とか、まぁ突っ込みどころ満載ではあるんですが。
とりあえず今のところは、

VB.NETでふつーにプログラムを作るとVisualBasic名前空間
(Microsoft.VisualBasic.dll)がもれなくついてくる。

…らしい。

ってことがわかれば今回は充分です。

では次回から、VisualBasic名前空間を参照しない(Microsoft.VisualBasic.dllをImportしない)VB.NETプログラムの作り方を模索してみます。