MinGWとDLLとManifest埋め込み
VCのIDEでやってれば縁の無い話。 MSVCRT.dllで済む場合も気にしないで善し。
VCのコマンドラインでやるならマニュアル嫁。
MSVCR80.dllやMSVCR90.dllはMSVCRTやMSVCR7xみたいに勝手にインポートさせてくれない。
夜鷹とのちょんの間幾らという気楽さは無く、店に入ってきっちり指名しないと顔見せもお触りもNGみたいな。
VCという店の常連なら店員が全部お膳立てしてくれるけど、一見さんには料金やらなにやらと教えてくれない。そんな感じ。
閑話休題。
Windowsには何時からか迷走した設計の辻褄合わせをする為のSxS(SideBySide)という機構が付いた(W2Kで実験、XPで一応の完成みたいな)。
ExeやDllからCランタイムをインポートする場合、
MSVCRT:既知のDLL(KnownDLL)として開放されてるので従来通り好きなようにやれる。
MSVCR7x:既知のDLLでは無いけれど、特別扱いもされてないので好きなようにやれる。
MSVCR80/90:特別な手順を踏まないとSxSによって阻害される。
で、MSVCR80/90を使う場合の手順。
1) manifestによって指名する必要があるのでその内容を用意する。
// ~MSVCR80.DLLの場合のmanifest内容例。~~
// ~~
基本的にはwindows\WinSxS\Manifestsディレクトリ内の目的のDLLのmanifestファイルから必要箇所を抜いてくる感じで(本当はMSDNのドキュメントを見て書くべきだけど、英語しかない気が・・・)。
テキストで適当に名前を付けて保存しとく。
先頭行にお呪いとして、
を付けておいてもいいかもしれない。なくても影響しないっぽい。
※64Bitの場合は、pros~Arch~="amd64"になるので注意。 それでもtypeはwin64になったりはしない。
hoehoe.exeの場合はファイル名を hoehoe.exe.manifestの様にして、exeと同じディレクトリに置いておけば埋め込まなくても勝手に拾い読みしてくれる。
2) 実行形式に埋め込む準備。
Dllでは拾い読みしてくれないので、Dllに埋め込む必要がある。
2a) 前述の内容のテキストファイルを用意する。 名前はtest.manifestとか適当に。
埋め込むにはリソースファイル化する必要があるのでリソースのソースファイルを用意する。
// ~ ヘッダ名はMinGWの場合。最小の内容例。~~
#include
// 対象がExeの場合。
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "test.manifest"
// 対象がDllの場合。
ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST "test.manifest"
// ~~
ヘッダではRT_MANIFESTは24、CREATE~は1、ISOLA~は2なので「 1 24 "file" 」の様に簡素にも書ける。
"file"を取り込んでIDを付けるって事やね。
上記をtest.rcとして保存する。
2b) rcファイルをバイナリのresファイルにコンパイルする。
windres.exeを使う。
windres -J rc -O coff test.rc -o test.res とか、
windres --input-format=rc --output-format=coff test.rc -o test.res 。
入力をrc形式、出力をcoff形式としてコンパイル。 coff形式がなんなのかは考えない(w
※64bitの場合は、「 -F pe-x86-64 」が必要。一応32bitは「 -F pe-i386 」
64bit対応しているwindresが必要(Versionではなくbuild依存?)
3) 出来たresファイルをリンクする。
普通のプログラムバイナリと同じようにリンカに渡してリンクするだけ。
最小内容の場合はexeやdll固有のファイルとなるわけでは無いので、一つ作っておいて使いまわすのも有りかも。
※以下は無視してよし、消してないだけw
付録。
MSVCR80/90を使うように(動的リンク)するには -lmsvc80 のように指定する(libmsvc80.aとして検索される)。
但し、gccへのコマンドラインオプションでは列挙する位置によってMSVCRTもリンクされる可能性があるので注意。
なので、コマンドラインではなくSPECファイルを使った指定をするのが無難。
SPECファイルに付いては取りあえず割愛。
後、素だと定数 __MSVCRT_VERSION__が0x600みたいなので、MSVCRTのverに合わせて0x700や0x800を定義しないと、増えた関数等を使えないかも。
----
verを0x700や0x800にする事で使える関数がちょびっと増えたりはするけれど、_set_SSE2_enabelとか使えても意味無いし、あえてMSVCRTを避ける理由が見つからない(w
mingw側のヘッダやLibの関係か、○○_s()といったセキュア系も使えないし('з`)
VC2005の頃はMSVCRTからMSVCR80へリダイレクトしてるような話を見かけたけどどうなんじゃろ?
----
msvcr80からpexportでdefを作り、それを使ってdlltoolでmsvcr80.aを作ってみた。
_onexit()とatexit()がmingwのcrt2.oとぶつかってエラーになるので、defから_onexitを削除、atexitはdef内に見つからないから、リンクエラーで指摘された部分をarで削除。
ヘッダを真似て書いて、一応strcpy_s()とstrcat_s()してprintf_s()するだけの物はエラーやワーン無くコンパイル&リンク&実行出来た。
objdumpで見てもちゃんとmsvcr80からインポートしてる、が、msvcrtもリンクされてる(w
単に-lの位置問題というわけではなく、msvcrtの_onexit等をインポートしてるという実にアレな状態('A`)ウーン
必要な関数群だけを書いたdefから*.aを作り、arで*.oにばらしてから、mingw-runtimeのlibmsvcr80に足すとか出来るのかなぁ(;'з`)
まぁ、あれっすかね、windowsべったりになるなら素直にvc使えってとこっすかね('ε`)
----
以前もやらかした気がするけど、msvcrtとmsvcrX0がチャンポンになったDllをプラグインとして呼び出すと、以降プラグインがLoadErrorになる気がする('з`)
msvcr80自体はs3dexport(ShockWave3dExp)が使ってるから、個々のDLL内でチャンポンにさえなってなければOKって感じなのかな。
----
MinGWでmsvcr80を使ったプラグインを作った場合、該当プラグインをロードした後にFlushPluginsをし、再度ロードした際にw32-shared-ptr.cでassertしてロードに失敗する(MinGW345の場合。 441でも同等ソースでassertする)。
該当libのみをmsvcr80でコンパイルしなおせば大丈夫なのか、MinGWごと再コンパイルが必要なのか、そうしても駄目なのかは不明(現使用バイナリはsourceForgeの345とTDMの441)。
VCでなら多分大丈夫('з`) 例:s3dExport.p
VCのコマンドラインでやるならマニュアル嫁。
MSVCR80.dllやMSVCR90.dllはMSVCRTやMSVCR7xみたいに勝手にインポートさせてくれない。
夜鷹とのちょんの間幾らという気楽さは無く、店に入ってきっちり指名しないと顔見せもお触りもNGみたいな。
VCという店の常連なら店員が全部お膳立てしてくれるけど、一見さんには料金やらなにやらと教えてくれない。そんな感じ。
閑話休題。
Windowsには何時からか迷走した設計の辻褄合わせをする為のSxS(SideBySide)という機構が付いた(W2Kで実験、XPで一応の完成みたいな)。
ExeやDllからCランタイムをインポートする場合、
MSVCRT:既知のDLL(KnownDLL)として開放されてるので従来通り好きなようにやれる。
MSVCR7x:既知のDLLでは無いけれど、特別扱いもされてないので好きなようにやれる。
MSVCR80/90:特別な手順を踏まないとSxSによって阻害される。
で、MSVCR80/90を使う場合の手順。
1) manifestによって指名する必要があるのでその内容を用意する。
// ~MSVCR80.DLLの場合のmanifest内容例。~~
// ~~
基本的にはwindows\WinSxS\Manifestsディレクトリ内の目的のDLLのmanifestファイルから必要箇所を抜いてくる感じで(本当はMSDNのドキュメントを見て書くべきだけど、英語しかない気が・・・)。
テキストで適当に名前を付けて保存しとく。
先頭行にお呪いとして、
を付けておいてもいいかもしれない。なくても影響しないっぽい。
※64Bitの場合は、pros~Arch~="amd64"になるので注意。 それでもtypeはwin64になったりはしない。
hoehoe.exeの場合はファイル名を hoehoe.exe.manifestの様にして、exeと同じディレクトリに置いておけば埋め込まなくても勝手に拾い読みしてくれる。
2) 実行形式に埋め込む準備。
Dllでは拾い読みしてくれないので、Dllに埋め込む必要がある。
2a) 前述の内容のテキストファイルを用意する。 名前はtest.manifestとか適当に。
埋め込むにはリソースファイル化する必要があるのでリソースのソースファイルを用意する。
// ~ ヘッダ名はMinGWの場合。最小の内容例。~~
#include
// 対象がExeの場合。
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "test.manifest"
// 対象がDllの場合。
ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST "test.manifest"
// ~~
ヘッダではRT_MANIFESTは24、CREATE~は1、ISOLA~は2なので「 1 24 "file" 」の様に簡素にも書ける。
"file"を取り込んでIDを付けるって事やね。
上記をtest.rcとして保存する。
2b) rcファイルをバイナリのresファイルにコンパイルする。
windres.exeを使う。
windres -J rc -O coff test.rc -o test.res とか、
windres --input-format=rc --output-format=coff test.rc -o test.res 。
入力をrc形式、出力をcoff形式としてコンパイル。 coff形式がなんなのかは考えない(w
※64bitの場合は、「 -F pe-x86-64 」が必要。一応32bitは「 -F pe-i386 」
64bit対応しているwindresが必要(Versionではなくbuild依存?)
3) 出来たresファイルをリンクする。
普通のプログラムバイナリと同じようにリンカに渡してリンクするだけ。
最小内容の場合はexeやdll固有のファイルとなるわけでは無いので、一つ作っておいて使いまわすのも有りかも。
※以下は無視してよし、消してないだけw
付録。
MSVCR80/90を使うように(動的リンク)するには -lmsvc80 のように指定する(libmsvc80.aとして検索される)。
但し、gccへのコマンドラインオプションでは列挙する位置によってMSVCRTもリンクされる可能性があるので注意。
なので、コマンドラインではなくSPECファイルを使った指定をするのが無難。
SPECファイルに付いては取りあえず割愛。
後、素だと定数 __MSVCRT_VERSION__が0x600みたいなので、MSVCRTのverに合わせて0x700や0x800を定義しないと、増えた関数等を使えないかも。
----
verを0x700や0x800にする事で使える関数がちょびっと増えたりはするけれど、_set_SSE2_enabelとか使えても意味無いし、あえてMSVCRTを避ける理由が見つからない(w
mingw側のヘッダやLibの関係か、○○_s()といったセキュア系も使えないし('з`)
VC2005の頃はMSVCRTからMSVCR80へリダイレクトしてるような話を見かけたけどどうなんじゃろ?
----
msvcr80からpexportでdefを作り、それを使ってdlltoolでmsvcr80.aを作ってみた。
_onexit()とatexit()がmingwのcrt2.oとぶつかってエラーになるので、defから_onexitを削除、atexitはdef内に見つからないから、リンクエラーで指摘された部分をarで削除。
ヘッダを真似て書いて、一応strcpy_s()とstrcat_s()してprintf_s()するだけの物はエラーやワーン無くコンパイル&リンク&実行出来た。
objdumpで見てもちゃんとmsvcr80からインポートしてる、が、msvcrtもリンクされてる(w
単に-lの位置問題というわけではなく、msvcrtの_onexit等をインポートしてるという実にアレな状態('A`)ウーン
必要な関数群だけを書いたdefから*.aを作り、arで*.oにばらしてから、mingw-runtimeのlibmsvcr80に足すとか出来るのかなぁ(;'з`)
まぁ、あれっすかね、windowsべったりになるなら素直にvc使えってとこっすかね('ε`)
----
以前もやらかした気がするけど、msvcrtとmsvcrX0がチャンポンになったDllをプラグインとして呼び出すと、以降プラグインがLoadErrorになる気がする('з`)
msvcr80自体はs3dexport(ShockWave3dExp)が使ってるから、個々のDLL内でチャンポンにさえなってなければOKって感じなのかな。
----
MinGWでmsvcr80を使ったプラグインを作った場合、該当プラグインをロードした後にFlushPluginsをし、再度ロードした際にw32-shared-ptr.cでassertしてロードに失敗する(MinGW345の場合。 441でも同等ソースでassertする)。
該当libのみをmsvcr80でコンパイルしなおせば大丈夫なのか、MinGWごと再コンパイルが必要なのか、そうしても駄目なのかは不明(現使用バイナリはsourceForgeの345とTDMの441)。
VCでなら多分大丈夫('з`) 例:s3dExport.p
この記事へのコメント