W64APIにまたしても!DNParseの落とし穴
いつぞやのNSFItemInfoNext関数に続き、またしてもW64で正常に動かないC APIを発見しました。今回は、DNParse関数です。まず、DNParse関数とはなんぞや?というところから。
DNParse関数の接頭辞、「DN」は「Distinguished Name」の略で、識別名を表します。Notesでいうところの、
CN=Taro Yamada/O=Acme/C=JP
というID名を指します。ユーザ名、サーバ名などがそれにあたります。実際に捌ける要素はこれだけにとどまらないようですが、ここでは割愛します。ここでのポイントは、「DN」を冠した関数は識別名を操作する関数ということです。
DNParseは、その識別名をパーツごとに分解することができます。例えば、前出の例を元に分解すると、
Common Name => Taro Yamada Org Name => Acme Country Name => JP
となります。ヘッダーでは、DNParse関数は次のように定義されています。
#include <dname.h> STATUS LNPUBLIC DNParse( DWORD Flags, const char far *TemplateName, const char far *InName, DN_COMPONENTS far *Comp, WORD CompSize);
FlagsとTemplateNameは0を渡します。 InNameに分解したい識別名へのポインタを入力します。 続く「DN_COMPONENTS」という構造体に、分解された情報が入ってくるので、事前にDN_COMPONENTS変数を用意して、そのポインタをCompに、変数サイズをCompSizeに与えれば取得できます。 問題なく分解できれば、NOERRORを返します。
DN_COMPONENTS dn; STATUS result = DNParse(0, 0, "CN=admin/O=acme", &dn, sizeof(dn));
ヘッダーでDN_COMPONENTSを見ると、Ver3はここまで、Ver4はここから、みたいなコメントがあり、拡張が繰り返されてきたことが伺えます。
#include <dname.h> typedef struct { DWORD Flags; /* Parsing flags */ /* (中略) */ WORD CLength; /* Country name length */ char far *C; /* Country name pointer */ WORD OLength; /* Organization name length */ char far *O; /* Organization name pointer */ WORD OULength[DN_OUNITS]; /* Org Unit name lengths */ /* OULength[0] is rightmost org unit */ char far *OU[DN_OUNITS]; /* Org unit name pointers */ /* OU[0] is rightmost org unit */ WORD CNLength; /* Common name length */ char far *CN; /* Common name pointer */ WORD DomainLength; /* Domain name length */ char far *Domain; /* Domain name pointer */ /* Original V3 structure ended here. The following fields were added in V4 */ WORD PRMDLength; /* Private management domain name length */ char far *PRMD; /* Private management domain name pointer */ /* (中略) */ } DN_COMPONENTS;
実際にDNParseをDomino Win32bitで動かすと、DN_COMPONENTSには以下のような情報が返ってきます。
ハイフンを挟んで左が文字数、右が文字列へのポインタです。Country name、Org name、Common nameのそれぞれに文字数2、文字数4、文字数11(0x0B)と、文字列が格納されているポインタが格納され、Org Unit name0〜3にはデータがないのがわかります。
全く同じコードをWin64用にコンパイルして動かすと以下のようになります。
「あれれ〜おかしいなあ〜。ポインタっぽい値が文字数変数に入り込んでいるぞ〜。」という感じになっています。
このズレは何なのか、いろいろ考えてみました。単純に、メモリ配置的に、ポインタが4バイトから8バイトになったというだけでは説明が付きませんでした。試行錯誤の結果、国名(C)、組織名(O)共通名(CN)までの変数であれば、次のようにパディングすることで解決しました。
#ifdef W64 typedef struct { DWORD Flags; /* Parsing flags */ /* (中略) */ WORD CLength; /* Country name length */ WORD c_padding; // パティング char far *C; /* Country name pointer */ WORD OLength; /* Organization name length */ WORD o_padding[3]; // パティング char far *O; /* Organization name pointer */ WORD OULength[DN_OUNITS]; /* Org Unit name lengths */ /* OULength[0] is rightmost org unit */ char far *OU[DN_OUNITS]; /* Org unit name pointers */ /* OU[0] is rightmost org unit */ WORD CNLength; /* Common name length */ WORD cn_padding[3]; // パティング char far *CN; /* Common name pointer */ WORD DomainLength; /* Domain name length */ char far *Domain; /* Domain name pointer */ /* Original V3 structure ended here. The following fields were added in V4 */ WORD PRMDLength; /* Private management domain name length */ char far *PRMD; /* Private management domain name pointer */ /* (中略) */ } DN_COMPONENTS; #else /* (オリジナルのDN_COMPONENTS定義をここに) */ #endif
CLengthとCの間にWORD一つ分、OLengthとOの間にWORD3つ分、CNLengthとCNの間も同じく3つ分パディングすると、以下のように正しく変数に格納されたのが確認できました。
Domain以下の変数に対しては試せていませんが、ズレているのであれば、逐次直していくしかないでしょう。
まとめ
Notes/Dominoは登場初期からマルチプラットフォーム指向で、C APIも早い段階から提供され、Notes/Dominoのバージョンとともに更新されてきました。ただ残念なことに、C APIに目を向ける人口が少ないせいか、時々このようにお粗末なところが放置され、それを指摘するような利用者サイドも皆無となっています。 また、こういう関数が放置されているのにWin64 Dominoサーバがほぼ問題なく動いているのは、Undocumentedな関数で実装されていることは想像に難くなく、Windows ToolのDependency Walkerにかければそれっぽい関数名が浮かび上がります。 C APIのドキュメントもきちんと整備し、Documentableになった関数からユーザに開放してくれることを切に願うばかりです。
注意: コードの利用においてチブル・システムズは一切の責任を負いません。自己責任でご利用をお願いいたします。