見出し画像

Notes C API探訪: NSFNoteOpen/NSFNoteClose(関数)(その2)

今回はNSFNoteOpen関数と、NSFNoteClose関数を使ったサンプルコードを紹介します。
NSFNoteOpen関数は、文書を開くのに文書ID(NOTEID)が必要ですが、ただの数字である文書IDを、Notesクライアントで事前に調べない限り、直接プログラムにハードコードする・・・なんて話は聞いたことがありません。ではどうするか?今回はNSFSearch関数で文書を特定して、文書IDを取得することにします。

#include "logger.h"
#include <iostream>
#include "ex_nsfdb.hpp"
#include "ex_nsfsearch.hpp"
#include "ex_nsfnote.hpp"
#include "ex_osfile.hpp"

#ifdef NT
#pragma pack(push, 1)
#endif

#include <osmem.h>

#ifdef NT
#pragma pack(pop)
#endif

bool nsfNoteOpen() {
 auto hDb = openDatabase("", "docs.nsf");
 if (!!hDb) {
   if (run2(hDb.value())) {
     nsfdb::closeDb(hDb.value());
     return true;
   }
 }
 return false;
}

nsfNoteOpen関数を実行すると、まずopenDatabase関数でローカルのdocs.nsfデータベースを開きます。
std::optional<DBHANDLE>型のデータが返ってくるので、正しく取得できたらrun2関数を実行します。
正常に実行できたらデータベースハンドルを閉じます。

次はrun2関数の内容を見てみます。

bool run2(DBHANDLE hDb) {
 auto noteId = searchDocument(hDb, "Subject=\"Test\"");
 if (!!noteId) {
   return run3(hDb, noteId.value());
 }
 return false;
}

まず、searchDocumentでSubjectフィールドがTest文字列のものを検索します。
検索が終わるとstd::optional<NOTEID>型のデータが返ってきます。このとき内容がstd::nulloptだと検索が失敗したことになります。std::nullopt以外なら検索は成功で、その文書IDを取得できます。
その文書IDを元にrun3関数を実行します。

bool run3(DBHANDLE hDb, NOTEID noteId) {
 auto hNote = nsfnote::openNote(hDb, noteId);
 if (!!hNote) {
   std::cout << "NoteID '" << std::hex << noteId << "' is opened."
             << std::endl;
   return run4(hNote.value());
 }
 return false;
}

run3関数では、受け取った文書IDで、nsfnote::openNote関数を使って文書を開いています。オープンに成功したらrun4関数を実行します。

bool run4(DHANDLE hNote) {
 // do something
 return nsfnote::closeNote(hNote);
}

ここでは、nsfnote::closeNote関数を実行するだけですが、それまでの間で何らかの文書操作をすることができます。

続いて、サブ関数を説明します。nsfNoteOpen関数で呼び出すopenDatabase関数は以下のようになっています。前回までに紹介した関数、osfile::constructPathNet、nsfdb::openDbを呼びだしています。成功すればDBHANDLE、失敗すればstd::nulloptを返します。

std::optional<DBHANDLE> openDatabase(
   const std::string &server,
   const std::string &path
   ) {
 // ネットパスを構築
 auto netPath = osfile::constructPathNet(path, server);
 if (!netPath) {
   return std::nullopt;
 }
 std::cout << "netPath=" << netPath.value() << std::endl;

 // データベースをオープン
 auto hDB = nsfdb::openDb(netPath.value());
 if (!hDB) {
   return std::nullopt;
 }
 std::cout << "opened; " << netPath.value() << std::endl;
 return hDB;
}

次はrun2関数で呼び出すsearchDocument関数です。検索用の@式をテキストで与えると、コンパイルしてNSFSearch関数で利用し、検索がすんだら検索ハンドルを解放しています。
検索が複数の文書にマッチしても、最初の一回で済むように、ERR_USERDEFINEDマクロを使って、未定義のSTATUS値で疑似エラーを発生させて、NSFSearch関数を終了するようにしています。
検索が疑似エラーで終わったら、NOTEID変数の中身を返します。

#define ERR_USERDEFINED(x) (PKG_CCONSOLE - 13 + x) // x = 0-12

std::optional<NOTEID> searchDocument(
   DBHANDLE hDb,
   const std::string &sFormula,
   WORD flags
   ) {

 // 検索式をコンパイル
 auto hFormula = nsfsearch::compile(sFormula);
 if (!hFormula) {
   return std::nullopt;
 }
 std::cout << "compiled; " << sFormula << std::endl;

 // 検索実行
 NOTEID noteId = 0;
 STATUS status = NSFSearch(
       hDb,
       hFormula.value(),
       nullptr,
       flags,
       NOTE_CLASS_DOCUMENT,
       nullptr,
       callback,
       &noteId,
       nullptr
       );
 OSMemFree(hFormula.value());
 if (status != ERR_USERDEFINED(0)) {
   return std::nullopt;
 }
 return noteId;
}

次はNSFSearch関数で呼び出されるコールバック関数です。
第1引数であるptrには、今回NOTEID変数へのポインタが渡されています。型を復元して、SEARCH_MATCH構造体のID.NoteIDをコピーしています。

STATUS LNCALLBACK callback(
   void *ptr,
   SEARCH_MATCH *pMatchOrg,
   ITEM_TABLE *
   ) {

 // SEARCH_MATCH構造体のコピー
 SEARCH_MATCH match;
 memcpy(&match, pMatchOrg, sizeof(SEARCH_MATCH));

 // 検索にマッチしているか
 if (!(match.SERetFlags & SE_FMATCH)) {
   return NOERROR;
 }

 NOTEID *pNoteId = reinterpret_cast<NOTEID*>(ptr);
 *pNoteId = match.ID.NoteID;
 return ERR_USERDEFINED(0);
}

次はヘルパー関数です。今回新たに定義したのは、ex_nsfnote.hppヘッダーの以下の内容です。

// ex_nsfnote.hpp

#ifndef EX_NSFNOTE_HPP
#define EX_NSFNOTE_HPP

#include <string>
#include <optional>
#include "logger.h"

#ifdef NT
#pragma pack(push, 1)
#endif

#include <global.h>
#include <nsfdb.h>
#include <nsfnote.h>

#ifdef NT
#pragma pack(pop)
#endif

namespace nsfnote {

inline std::optional<NOTEHANDLE> openNote(
   DBHANDLE hDb,
   NOTEID noteId,
   WORD openFlags = 0
   ) {
 NOTEHANDLE hNote = NULLHANDLE;
 STATUS status = NSFNoteOpen(hDb, noteId, openFlags, &hNote);
 if (ERR(status) != NOERROR) {
   logger::printStatusMessage(status);
   return std::nullopt;
 }
 return hNote;
}

inline bool closeNote(NOTEHANDLE hNote) {
 STATUS status = NSFNoteClose(hNote);
 if (ERR(status) != NOERROR) {
   logger::printStatusMessage(status);
   return false;
 }
 return true;
}

} // namespace nsfnote

#endif // EX_NSFNOTE_HPP

では、これまでのサンプルコードを、nsfNoteOpen関数からの実行例で見てみます。

netPath=docs.nsf
opened; docs.nsf
compiled; Subject="Test"
NoteID '8fe' is opened.

ここでは、SubjectフィールドがTestとなっている文書を検索し、取得できた文書ID 0x8fe の文書をオープンしています。

まとめ

NSFNoteOpen、NSFNoteCloseは、関数そのものはシンプルですが、文書を開くシチュエーションによってはいくつかのバリエーションがあったり、アドインで渡される文書ハンドルは安易に閉じてはいけなかったりと、実はなかなかセンシティブな側面があります。過度に怖がる必要はありませんが、注意して使うようにしましょう。

注意: コードの利用においてチブル・システムズは一切の責任を負いません。自己責任でご利用をお願いいたします。

いいなと思ったら応援しよう!