2009年5月21日木曜日

DLL修正ではまる

ずっと昔に他の人(退職しちゃっててもう居ない)がVC6で作成したDLLを修正することになりました。引数を1個追加するだけなので簡単……と思ったら、コンパイルは通るけどリンクでエラー。

リンク中...
ライブラリ .\Release/Test.lib とオブジェクト .\Release/Test.exp を作成中
Test.exp : error LNK2001: 外部シンボル "_TestFunction@16" は未解決です
Test.exp : error LNK2001: 外部シンボル "_TestFunction@16" は未解決です
.\Release\Test.dll : fatal error LNK1120: 外部参照 1 が未解決です。
link.exe の実行エラー

Test.dll - エラー 3、警告 0

と、結構ありがちなエラー。外部シンボルが未解決というと、.defファイルに書いてる関数名が間違っているというのがパターンなんですが、何度見直しても合ってるし……。.defファイルはこんな感じ。

EXPORTS
TestFunction = _TestFunction@16 ;VB用の export
_TestFunction@16 ;VC用の export

関数名は合ってると。では怪しいのは @16 なんだけどこれって何?
.defファイルの書式やらをググると、@の後の数字は序数で……とか書いてあったりするのですが、この場合は違いました。さらにキーワードを変えつつググっていたら、やっとそれらしい情報に到達。

C の装飾名の形式

関数宣言時の呼び出し規約が __stdcall の場合、.defファイルで記述するCの装飾名の形式は、

先頭にアンダースコア (_) が付き、末尾にアット マーク (@) が付き、その後ろにパラメータ リストのバイト数が続きます。

ということでした。つまり @16 というのは関数の引数リストのバイト数合計が16バイトということだったのですね。
今回は引数を追加したわけなので、そのバイト数分増やさないといけなかったのです。
4バイトの引数を1個追加したので、@16 → @20 に修正してリビルドしたら……リンク通りました。

なんかこれだけ調べるのに丸1日以上かかってしまったよ。とほほ。最初はVC6の環境がなくてVC2005で開いてやってたから、そのせいかもと思って古い環境でやり直したりとか……。
DLLの作成方法にはいろいろあって、.defを使用しない方法もあるんですけど、今回は修正元が使ってたのをそのまま使ったので。

0 件のコメント: