プログラミング一覧

WinSxS問題 その2

基本的事項

まずは知っておかなくてはいけないこと。

外部マニフェストの挙動

  • 外部マニフェストはexeのみに適用される。
    • そのexeが必要とするdllらには適用されない点に注意
  • WindowsXP環境では、外部マニフェストのほうが埋め込みマニフェストよりも優先される
    • 2003より優先順序が逆転した。

症状

再頒布ランタイムパッケージがインストールできない環境で発生した。
インストールできない環境とは、ユーザーがゲスト権限くらいしかない場合を指す。
あるいはどうしても事情により入れられないとか。

このときに、exeとdllと同フォルダにおいて、
そのプライベートアセンブリとして、VCランタイムを配置した。
このときに、最近のWindowsUpdateが適用されたバージョンを置いた。
(VC90.30729.4148バージョン)

そして、exeがこのバージョンのランタイムを使うように、外部マニフェストファイルを作成して
実行をさせようとした。

この結果が、昨日のエラーを引き起こしていた。
理由は、dllにはこのマニフェストの影響が及ばないからである。
故にdllは埋め込みマニフェストに従い、21022.8のランタイムを要求しているし、
exeは外部マニフェストによって強制された 320729.4148バージョンを要求する。

ここで同一のアセンブリ名であるためにdllがロードされてチェック機構が働いた段階で、
不整合がおこり、ローダーの段階で失敗してしまう。
その結果、exeが実行されることなく強制終了されてしまう。
試した環境ではエラーダイアログすら出ない。

解決策

簡単に解決する方法がある。
ひとつは、VisualStudio2008,およびSP1環境のデフォルト動作では、
RTM版のランタイムにバインドされる。
これは、9.0.21022.8 のバージョンである。
これに対応するランタイムを入手し、それをプライベートアセンブリとして使用する。

ただし、セキュリティ関係で問題になりそうな気もするが仕方ない.

もう一つは、最初から埋め込みマニフェストで最新バージョンを使うように
全部アプリケーションをリビルドする。
これには、まずプロジェクト設定の、プリプロセッサ定義で、
_BIND_TO_CURRENT_VCLIBS_VERSION を定義しておく。

これを行うとビルドした環境の最新のバージョンへバインドされる。
よって、最近のやつであれば 30729.4148にバインドされる。
リビルドにより dllからexeまでがこれに依存するようになるため、
VisualStudioのインストールディレクトリから再配布ランタイムをコピーして使用すればよい。

最後の方法は、exeでやったように、dllに対しても依存するアセンブリ情報を強制変更する方法である。
これには、hogehoge.dll.2.config というファイルを作成する。
ポイントは”2″という部分。

このファイルにより、目的のバージョンへバインドがリダイレクトするように設定すれば、
dllとexeのバージョン要求不一致を解決できる。
この方法は一応海外のMS CONNECTで記述があったためおそらく正式な方法だと思う。

しかし、日本語での情報が見つからなかったためここに書いてみた。


WinSxS問題

Windowsのサイドバイサイドって、
強力な仕組みなんだけど、その強力さ故色々と面倒なこと引き起こしてるなぁと思います。

昔DLL HELLとか散々な目にあったからこそ作られた仕組みであると理解しているけれど、
今食らっている状況はWinSxS HELLともいえるような気がします。

マニフェストファイルを作成して、実行体と同じ場所にDLLを
プライベートアセンブリとして配置する方法でトラブル。

dllが要求するものと、exeが要求するものと同一アセンブリ名、異なるバージョンアセンブリ
という状況が出たときに問題は起こる…。

通常サイドバイサイドのポリシー設定により、適切にリダイレクトがかかるために
上記の問題は起こらない。なぜならば同一の参照先を示すことになるから

現在回避策を調査中・・・。


Redmineによるリポジトリ管理機構について

どうやら、設定で自動更新としても、
メニュー項目から、リポジトリを覗いたときに初めて情報を取得するようだ。

だから、毎回”活動”のページを開くだけでは、
ソースコードの状況を確認することが出来なかった!

そこで次のようにして、この問題をとりあえずは解決することにした。

  1. 自動更新のチェックを外す
  2. redmineユーザーのcronにスクリプト実行を設定

エンコーディング

他にもソースコードのエンコーディングが怪しかったので、設定を見直してみた。

「管理」/「設定」/「リポジトリ」/「リポジトリのエンコーディング」
とたどって、次のように設定。

utf-8,shift_jis,euc-jp


SkinnedMeshで複数描画したい

DirectXのサンプルSkinnedMeshをいじって、
リソースは1つ、描画用インスタンスは複数なんてものを作成してみました。 

色々と、DirectXの仕様で躓く点が多かったように思います。

実現の為のポイント

  • AnimationControllerは単純にCloneで作成しても、ボーン構造(FRAME)は共有されてしまう
    • 独立させたいのは、マトリックスパレット.
    • 一緒にボーンオフセット行列もインスタンスごとに保持させてしまうと、楽にはなる。
  • マトリックスパレットは、MeshContainerが保持するのではなく、一段外に追いやってみる。
    • 自分では FRAMEの中に追い出しました。MeshContainerと並列して存在する感じに。

このような点から、
描画用のオブジェクト生成時には、

  • FRAME構造をコピー(別インスタンス作成)
  • AnimationControllerをcloneする
  • 上記でそれぞれ描画用のインスタンス用としてのデータができたので、関連づける.
    • D3DXFrameRegisterNamedMatrices()を使用する

これで画像のようにインスタンスごとにアニメーションデータを持った感じで
描画ができるようになりました。

面倒といえば面倒なので、要望があったらlib化してしまうかも。


C++/CLIでの実行時エラー

C++/CLIとC++ネイティブのlibをリンクさせて、Windowsフォームアプリケーションを作ったときに、
よくわからないエラーが発生してしまうことがある。

どうやらCRT初期化の順序に問題があるのか。
これを解決するには、エントリポイントの修正を行えば解決する模様。
次のエントリポイントに設定させてあげれば問題解決できた。

ずいぶんと前にわかっていて、日記に書いたと思ったのに忘れていたので、
今更ながら追加。

上記の発生条件

どうやらグローバルなコンストラクタが実行されるときに何かがあるらしい。
C++のclassメンバにstaticなデータがある場合に発生する。
また、それが組み込み型(プリミティブ型)ならば問題なく動くが、
自分で作成したクラス型だと実行時エラーの引き金となる。

追記1

コンストラクタやデストラクタを備えるクラスだとアウトっぽい。
そのクラスがプリミティブ型しか保持していないとしても。

追記2

どうやらコンストラクタがあることが問題ではないらしい。
デストラクタがあることが問題のようだ.
デストラクタのあるクラス型で、クラスstaticメンバを持つと、このエラーが発生する。

こういったクラス構造をどこかで持ってしまうと、C++/CLIで実行時エラーとなる.


DirectXのテクスチャについて

メモリ管理やUSAGEの組み合わせで気になったので調査.

生成時フラグ

D3DPOOL D3DUSAGE 結果
Default 0 OK
Default WRITEONLY NG
Default DYNAMIC OK
Managed DYNAMIC NG
SystemMem DYNAMIC OK
Default DYNAMIC RENDERTARGET OK
Default RENDERTARGET OK

ロック時フラグ

タイプ ロック手法 結果
Default&Dynamic&&RenderTarget 0 NG
Default&RenderTarget 0 NG
Default&RenderTarget D3DLOCK_READONLY NG
Default&Dynamic&&RenderTarget D3DLOCK_DISCARD NG
Default&RenderTarget D3DLOCK_DISCARD NG
Default&Dynamic D3DLOCK_READONLY OK
Default&Dynamic D3DLOCK_DISCARD OK
SystemMem&Dynamic D3DLOCK_READONLY OK
SystemMem&Dynamic D3DLOCK_DISCARD OK

感想

意外だったのは SystemMemでDynamicタイプを作れること。

それでもSystemMemでRenderTargetは無理でしたが(当たり前)。


インターフェース変更

DirectXのヘッダを見ていて気付いたこと。
同名のIIDで、値だけ違うってまずいんじゃないだろうか。

DEFINE_GUID(IID_ID3DXEffect,
0xd165ccb1, 0x62b0, 0x4a33, 0xb3, 0xfa, 0xa9, 0x23, 0x0, 0x30, 0x5a, 0x11);

DEFINE_GUID(IID_ID3DXEffect,
0xf6ceb4b3, 0x4e4c, 0x40dd, 0xb8, 0x83, 0x8d, 0x8d, 0xe5, 0xea, 0xc, 0xd5);

確かにメソッドの数が違うから新しいIIDにするのは納得がいくのだけども、こんなことして、平気なのかな?と思う。もしや、こういう強引なことを出来るようになったのも、対応するd3dx_**.dllが分かっているから、なのか?

だれか情報知っていたら教えて欲しいです。

 


D3DXCreateEffectについて – CompileEffect

マクロで有効・無効のブロックを切り替えるにはどうしたらいいんだーということで調べてました。
ピクセルシェーダーコードでは、以下のようにして挙動をチェックしていました。

[code lang=”cpp”]
#ifdef _TEST_
return float4(1,1,1,1);
#else
return float4(0,0,1,1);
#endif
[/code]

これをEffectでやるのにネックだったのは、マクロ定義変数の部分でした。

解決策はこう。

[code lang=”cpp”]
D3DXMACRO d3dxMacros[2] = { //マクロ マクロ名
"_TEST_","", <– ここで定義する部分を設定して
0, 0 <– ここで終端を示すようにヌルターミネートしておく
};
[/code]

通りでマクロの設定長を示す引数がないわけだ。

探してみてもこの辺でつまずく人はいないのか、記述が見あたらず。
また、ほとんどの場合このマクロ設定部分NULLにしちゃうし、使う状況ってないのかなぁ。

自分では多用していこうと思っていますが。


動かないコード

最近は動かないコードばかり書いてます。コンパイルを通してニヤリ、として終了です。
コンパイル時にすでに判定や計算が終わっている、とか、プリプロセッサ終了時に状態確定とか、そんなことばっかりやってました。

ふと、覗いてみるとどうやらBoostのPreprocessorが分野的に近い。そして、templateで色々とごちゃごちゃやったことは、メタプログラミング、という分野にいるっぽいことがわかりました。

こうやってできたコード、少なくとも人に読ませられるコードじゃないなぁと実感してます。数ヶ月後の自分でも怪しい・・・。

 


昨日の答え。

意外にも日を変えて再度悩んでみると思いつくものである。
とりあえず下記コードでやりたいことはできた。
しかし、T,Sなど1文字の型引数にしておくと、次みたときに相当わからないなぁ…

しかしこのコード、少なくとも今年一番イケてると自負してます