NSFItemInfoNext関数を64bit Windows Dominoサーバで使用するとクラッシュする
追記 2019-11-24
- 記事のリンクを追加しました。
- ソースコードにcpp指定をしました。
本編
私が、とあるプロジェクトでDominoサーバのアドインを作ったとき、奇妙な現象に悩まされた。
その現象というのは、NSFItemInfoNextというNotes C APIの関数を使うと、32bit WindowsのNotesクライアントとDominoサーバ、64bitのMacOS、32bit Linuxではまったく問題がないのに、64bit WindowsのDominoサーバでのみクラッシュするという事象である。
そもそも「NSFItemInfoNext」とは何なのか。
NSFItemInfoNext関数は、通常NSFItemInfo関数とペアで使用する。「NSFItem○○」という関数はアイテム(フィールドと同義)に関する操作を行う種類のもので、NSFItemInfoはアイテムの情報を取得するのに使用する。アイテムは、名前、型、値、属性の4つで構成されていて、NSFItemInfoは属性以外の情報を取得できる。アイテムは文書中名前(フィールド名)で区別されるため、通常1文書に1つだが、1アイテム中に保持できる値の大きさが限られているため、同名アイテムが複数保管されることがある。その時、2つめ以降の同名アイテムを取得するのに使われるのが、NSFItemInfoNext関数というわけである。
例えば、次のようにコーディングになる。
STATUS error = NSFItemInfo(hNote, "Body", strlen("Body"), &bItem, &wType, &bValue, &dwLength); if (ERR(error) != ERR_ITEM_NOT_FOUND) { if (error) return; bPrevItem = bItem; error = NSFItemInfoNext(hNote, bPrevItem, "Body", strlen("Body"), &bItem, &wType, &bValue, &dwLength); }
バイト数が決まっている数値型(NUMBER)、日時型(TIMEDATE)やシンプルなテキストデータくらいであれば、同名アイテムを2つ以上に分けて保存することはめったにないが、64キロバイトを超えるデータは文書に保存できないため、同名アイテム保存を使用する。Notesクライアントは、約40キロバイトを目安に同名アイテムを2つ以上に分けて保存する。
このように、NSFItemInfoNext関数は何か特殊な関数というわけではなく、APIとしては極めてベーシックな存在の関数なのだが、それだけにDominoサーバx64Winでクラッシュし、その原因がNSFItemInfoNextだとわかった時は、首をひねるばかりだった。
Notes C APIを使用したコードをコンパイルする場合、Windows 32ビットでは、少なくとも次の3つを指定する。
-DW -DW32 -DNT
Windows 64ビットでは、これらに加えて、以下の3つも必要になる。
-DW -DW32 -DNT -DW64 -DND64 -D_AMD64_
これら以外に識別子の過不足がないか調べてみたが、特に問題ない。
ネット上で調べているうちに、興味深い記述を見つけた。NSFItemInfoNext関数は以下のような引数を取る。
STATUS LNPUBLIC NSFItemInfoNext ( NOTEHANDLE hNote, BLOCKID NextItem, const char far *Name, WORD NameLength, BLOCKID far *retbhItem, WORD far *retDataType, BLOCKID far *retbhValue, DWORD far *retValueLength );
その記事では、この関数をLotusScript内から呼び出した時に、BLOCKID型の引数を倍精度小数点数(double型)の幅8バイトで指定する必要があるということだった。
C APIの世界では、BLOCKIDはプールハンドル(4バイト)とブロックハンドル(2バイト)の2つの値を持つ6バイト構造体だ。これを8バイトにするには、2バイトのブロックハンドルを4バイトにすることになる。
Notes C APIでブロックハンドルを定義しているのは、global.hヘッダーファイルだ。
// global.hの1076行目 typedef WORD BLOCK;
これを次のように書き換える。
#ifdef W64 typedef DWORD BLOCK; #else typedef WORD BLOCK; #endif
こうすることで、NSFItemInfoNext関数はクラッシュしなくなり、 BLOCKIDを使用するほかの関数も問題なく動作している(ように見える)。この手法が他の関数に本当に影響はないのか予断を許さないが、64bit WindowsのDominoサーバでクラッシュに困ったら、NSFItemInfoNext関数に起因していないか調べ、上記方法を試してみるのもよいかもしれない。
この修正による影響事案
この記事で提案した修正について、私自身以下の事案に遭遇しました。よろしければこちらもご参照下さい。