読者です 読者をやめる 読者になる 読者になる

伝説のツール「NotesPeek」をQtでリメイクする(その2・ファイルの取得)

ではNotesPeekリメイク版、最初のミッションです。

最初の目標は、「ローカルのデータディレクトリから直下のファイルを取得」します。

Notes APIで、データベースの一覧を取得する関数は、NSFSearch関数です。

#include <nsfsearc.h>

STATUS LNPUBLIC NSFSearch (
    DBHANDLE hDB,
    FORMULAHANDLE hFormula,
    char far *ViewTitle,
    WORD SearchFlags,
    WORD NoteClassMask,
    TIMEDATE far *Since,
    NSFSEARCHPROC EnumRoutine,
    void far *EnumRoutineParameter,
    TIMEDATE far *retUntil
);

NSFSearchは、この引数のネーミング通りに受け止めれば、文書検索が目的の関数です。しかし、ファイルリストを取得するのにも利用します。ディレクトリに対してDBHANDLEを取得することができ、NoteClassMaskにFILE_xxxを適用すると、目的がファイル検索になるという、不思議な関数です。

このNSFSearch関数を利用するには、DBHANDLEを取得しておかなければなりません。通常、データベースへのネットパス(ポート名、サーバ名、ファイルパスを組み合わせたNotesの世界のフルパス)を元に、データベースハンドルを取得しますが、ディレクトリへのネットパスでNSFDbOpen関数を使うと、ディレクトリに対するデータベースハンドルを取得することができます。

#include <nsfdb.h>

STATUS LNPUBLIC NSFDbOpen (const char far *PathName, DBHANDLE far *rethDB);

もちろん、ディレクトリに対してデータベース操作はできません。データベースハンドルが、データベースのものか、ディレクトリのものか、判別する関数があります。

#include <nsfdb.h>

STATUS LNPUBLIC NSFDbModeGet (DBHANDLE hDB, USHORT far *retMode);

このNSFDbModeGet関数に、ハンドルとUSHORT変数へのポインタを渡すと、DB_LOADED(データベース)か、DB_DIRECTORY(ディレクトリ)かを返してくれます。

NSFSearch関数に話を戻しましょう。

2番目の引数FORMULAHANDLEは、コンパイル済み@関数の選択式へのハンドルですが、ファイル検索では使用しないので、NULLHANDLEを渡します。3番目の引数ViewNameは、前述の選択式内に@ViewTitleを使っている場合、ビューの名前を指定する必要があります。これもファイル検索では使わないので、nullptrを渡します。

4番目のSearchFlagsは、検索フラグを指定します。ここでSEARCH_FILETYPEというフラグを立てておくと、検索対象がDB中の文書から、ディレクトリ中のファイルに変わります。続く5番目のNoteClassMask(文書クラスのマスク)が、FILE_xxx(ファイル用マスク)の意味に変わります。

4番目のSearchFlagsではもう1つ、SEARCH_SUMMARYというフラグを立てておきます。これを立てておくと、サマリーバッファというデータにアクセスすることが可能になります。サマリーバッファについては、今回説明は割愛しますが、簡単に言うと「文書を開かなくても読み取り可能なデータ群」のことで、ファイル検索時には、データベースファイルに関する情報は、このサマリーバッファから取得することになります。

5番目のNoteClassMaskには、前述の通りFILE_xxxで定義されているファイル用のマスクを使って、取得したいファイルの種類を指定します。通常、FILE_DBANY(NSFファイル)、FILE_FTANY(NTFテンプレートファイル)を使いますが、他にもいくつか定義されています。また、上位8ビットがフラグビットになっていて、FILE_DIRS(ディレクトリ名も取得)、FILE_RECURSE(サブディレクトリも検索)などを組み合わせることもできます。ちなみに、下位8ビットはフラグビット状にはなっていません(0から順に取得できるファイルの種類が決められている)。

6番目のSinceは、この日時以降の文書を検索対象とするもので、ファイル検索には使用しないので、NULLを指定します。

7番目の関数ポインタは、検索対象が見つかるたびに呼び出されるコールバック関数です。形式は以下の通りです。

#include <nsfsearc.h>

typedef STATUS (LNCALLBACKPTR NSFSEARCHPROC)(
    void far *EnumRoutineParameter,
    SEARCH_MATCH far *SearchMatch,
    ITEM_TABLE far *SummaryBuffer);

最初の引数は汎用ポインタで、検索対象のデータを格納するためのコンテナ変数へのポインタを渡すのに使用します。2番目はSEARCH_MATCH構造体へのポインタです。文書IDや文書クラス、文書の権限などの情報が含まれます。また、選択式にマッチしているかどうかを判定するのに使うメンバSERetFlagsもあります。通常、ファイル検索では使用しません。最後がサマリーバッファへのポインタです。

いったん、NSFSearch関数に話を戻します。8番目のEnumRoutineParameterは、さきほどのコールバック関数の最初の引数に渡される汎用ポインタです。9番目の引数は、検索の終了日時が返されます。次にNSFSearch関数を呼び出す時に、Sinceにこの日時を指定することで、検索対象の日時を重複させず、前回以降に追加修正があった文書だけを対象にすることができます。もちろん、ファイル検索には使用しないので、NULLを指定します。

さて、最後にサマリーバッファについて、ファイル検索時に必要な、最低限のポイントに触れておきます。

サマリーバッファは、コールバック関数に渡されるSummaryBuffer(ITEM_TABLEへのポインタ)を使ってアクセスします。ポインタ構造としては、以下のようになっています(例はアイテムが3つの場合)。

ITEM_TABLE ITEM ITEM ITEM NAME VALUE NAME VALUE NAME VALUE
ITEM_TABLE 1 2 3 1 1 2 2 3 3

(Markdown、タイトルなくてもいいようにならないかな?)

ITEM_TABLE::Itemsに、ITEM構造体がいくつあるかが示されていて、ITEM_TABLEの直後のポインタから始まっています。ITEM構造体には、名前の長さと値の長さが格納されていて、前述の通り、ITEM構造体の個数分の直後から配置されています。名前はchar*として示されたバイト数分だけ取得すればOKです。値の方はひとひねりあり、値が示している最初の2バイトは、値の型(アイテム型)を表しています。単一文字列はTYPE_TEXT、数値はTYPE_NUMBER、日時はTYPE_TIMEといったWORD型のデータです。ですから、値の取得には最初の2バイトで型を判別し、3バイト目からITEM::ValueLength - sizeof(WORD)の長さで値を取得してくる必要があります。ポインタの移動についても処理が煩雑になるので、少々面倒な作業になります。

ただ、型が単一文字列(テキスト型)で、名前もわかっていれば、NSFGetSummaryValue関数が使えます。

#include <nsfnote.h>

BOOL LNPUBLIC NSFGetSummaryValue (const void far *SummaryBuffer, const char far *Name, char far *retValue, WORD ValueBufferLength);

NSFGetSummaryValueは、サマリーバッファから既知の名前のテキストデータを取得できます。1番目にサマリーバッファへのポインタ、2番目に名前(LMBCS)、3番目に文字列格納用のバッファ、4番目にそのバッファの長さを指定します。成功すれば0以外が返ります。

ファイル名を取得するには、DBDIR_PATH_ITEMかFIELD_TITLEを、データベースタイトルを含むDB情報を取得するにはDBDIR_INFO_ITEMを指定すればOKです。Notes/Domino APIプログラミング―C++とSTLによる実践的プログラミングには、データベースタイトルの取得にFIELD_TITLEを使うと書かれていましたが、当時の仕様から変更されたのか、FIELD_TITLEではファイル名しか取得できないので、注意しましょう。

NotesPeekリメイクアプリケーションの暫定名を、「nsfinder」としました。そのデモ画面(Mac版)です。

f:id:takahide-kondoh:20170402163419p:plain

NotesPeekと同じ、ツリー構造です。「Local」というローカルディレクトリを表すアイテムが初期配置されています。

f:id:takahide-kondoh:20170402163436p:plain

Localで右クリックを押すと、コンテキストメニューが表示されます。Refresh Childrenをクリックします。

f:id:takahide-kondoh:20170402163443p:plain

ローカルのデータディレクトリが検索され、該当するNSFファイルのファイル名とデータベース名が表示されます。

構築環境は、以前のIntroQtデモと同じです。nsfinder本体と、ntlxライブラリのコードはBitbucketから取得できます。

Bitbucket/ntlxライブラリ

クローンをダウンロードしたあと、タグv0.1.1をチェックアウトしてください。

Bitbucket/nsfinder本体

こちらはタグv0.9.0をチェックアウトしてください。

Mac以外にWindowsLinux(Ubuntu)でも動作確認しています。Qt Creatorなどの設定は、以前の記事をご覧ください。