Unmanaged Assemblies

昨日のVisual C++ 2005(というかコンパイラ、リンカ)の変更についてフォロー。

足し算、引き算をするライブラリ(math.cpp)を作ってみます。

#include <stdio.h>

extern "C" __declspec (dllexport) void add (int x, int y) {
    printf ("%d + %d = %d\n", x, y, x + y);
}

extern "C" __declspec (dllexport) void subtract (int x, int y) {
    printf ("%d - %d = %d\n", x, y, x - y);
}

で、コンパイル

$ cl /LD /MD math.dll

すると、

  • math.dll
  • math.dll.manifest
  • math.exp
  • math.lib
  • math.obj

これらのファイルが生成されます。.manifestという馴染みのファイルが混じっていますが、取りあえず今は気にしないことに。で、これを呼び出すプログラムを書きます。(app.cpp)

#include <stdio.h>

#pragma comment (lib, "math.lib")

extern "C" void add (int x, int y);
extern "C" void subtract (int x, int y);

void main () {
    add (10, 20);
    subtract (10, 20);
}

これも何の変哲もないコードですね。で、コンパイル

$ cl app.cpp

問題なくコンパイルされます。これを実行すれば、

 10 + 20 = 30
 10 - 20 = -10

が表示されるはずですよね。ところが、実行エラーになるんですよね。「app.exe - エントリ ポイントが見つかりません」とか言って。(^^;

ここで、先ほどのmath.dll.manifestを覗いてみましょう。

<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
  <dependency>
    <dependentAssembly>
        <assemblyIdentity type='win32' name='Microsoft.VC80.CRT' version='8.0.50608.0' 
            processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>
</assembly>

Microsoft.VC80.CRTがmsvcrt80.dllの事で、このマニフェストが無いと共有アセンブリであるmsvcrt80.dllを読み込んでくれないのです・・・て、マニフェストあるだろっ!(^^; 実は、.dllの場合、リソースとして.dllに埋め込んでやる必要があるのです。

$ mt /manifest math.dll.manifest /outputresource:math.dll;#2

最後の#2はリソースIDで、.dllの時は必ず2になります。改めてapp.exeを実行すると今度は実行されます。

知らないと結構嵌りそうですね〜(^^;

アパートの続き

同時に同じアパートで実行されるスレッドは1つだけである。

素直に考えればこの説明はSTAだけのことを言っているんだろうなぁ。でないと、MTAの時にロックが云々という話にならないだろうし。ところで、WinFormsなアプリ書くときに、Mainに

[STAThread]
static void Main() 

と、STAThread属性を付けてSTAアパートにしますが、最初、これがよく分からなかったです。(というか今も分かってないけど)。「WinFormsはSTAじゃないと動かないので云々」という説明は見かけるのですが、別に付けなくても動くし・・・(.NET1.0ではバグの所為で一部問題があったようですが)。COMのアパートの考えが有効でしたら、異なるThreadingモデル(デフォルトはMTA)の場合、アパートを越えたアクセスにはプロキシが必要でかつ、スレッドの切り替えはコストが掛かるので、STAThread属性を付けないとパフォーマンス的に無駄って結論になりますが、これであっているのでしょうか?(^^;

んー、WinForms用にSTAが作られる(ホント?)のだから、MainからApplication.RunするだけのMTAが無駄というのもあるか。何かこんがらがってきた。(^^;

(追記)
なんか最初から間違っていた気が。もし、WinFormsがアパートに影響されるのなら、(STAにしておけば)別スレッドからコントロールのメソッド呼ぶのにわざわざInvokeなんか使う必要ないだろう。多分、WinForms関連のクラスでCOM呼んでいるヤツがいて、もし、それを使うならSTAにしておいた方が都合がよい、って程度じゃないかな。