NotesとQtでWindows、Mac OS X、Ubuntuのデスクトップアプリ(その5 - LMBCS変換・基本編)

それでは、LMBCSをラップしたクラス、Lmbcsを定義していきます。

<ntlx_global.h>

#ifndef NTLX_GLOBAL_H
#define NTLX_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(NTLX_LIBRARY)
#  define NTLXSHARED_EXPORT Q_DECL_EXPORT
#else
#  define NTLXSHARED_EXPORT Q_DECL_IMPORT
#endif

#endif // NTLX_GLOBAL_H
<lmbcs.h>

#ifndef NTLX_LMBCS_H
#define NTLX_LMBCS_H

#include "ntlx_global.h"
#include "status.h"

#include <QByteArray>

namespace ntlx {

const int MAX_UNICODE_LEN = (1024 * 42 / 2);

class NTLXSHARED_EXPORT Lmbcs
    : public QByteArray
{
public:
  /**
   * @brief コンストラクタ
   */
  Lmbcs();

  /**
   * @brief コピー元LMBCSの文字列ポインタと長さによるコンストラクタ
   * @param コピー元LMBCSのポインタ
   * @param len コピー元LMBCSの長さ、0終端している場合は-1
   */
  Lmbcs(const char* s, int len = -1);

  /**
   * @brief ステータス値からエラーメッセージのLMBCSを生成する
   * @param status ステータス値
   */
  Lmbcs(STATUS status);

  /**
   * @brief コピーコンストラクタ
   * @param other コピー元
   */
  Lmbcs(const Lmbcs& other);

  /**
   * @brief 代入演算子
   * @param other 代入元
   * @return 自身への参照
   */
  Lmbcs& operator=(const Lmbcs& other);

  /**
   * @brief QStringに変換する
   * @return LMBCSから変換されたQString文字列
   */
  QString toQString() const;

  /**
   * @brief QStringからLmbcsに変換する
   * @note 変換できる文字列の長さはMAX_UNICODE_LENに制限
   * @param qs 変換元のQString
   * @return QStringから変換されたLmbcsオブジェクト
   */
  static Lmbcs fromQString(const QString& qs);

};

} // namespace ntlx

#endif // NTLX_LMBCS_H
<lmbcs.cpp>

#include "lmbcs.h"

#include <QString>

#if defined(NT)
#pragma pack(push, 1)
#endif

#include <osmisc.h>

#if defined(NT)
#pragma pack(pop)
#endif

namespace ntlx {

const WORD UNICODE_BYTE = (sizeof(ushort) / sizeof(char));

Lmbcs::Lmbcs()
  : QByteArray()
{
}

Lmbcs::Lmbcs(const char *s, int len)
  : QByteArray(s, len)
{
}

Lmbcs::Lmbcs(STATUS status)
  : QByteArray()
{
  char buffer[MAXWORD];
  WORD lmbcsLen = OSLoadString(0, Status(status).error(), buffer, MAXWORD);
  *this = Lmbcs(buffer, lmbcsLen);
}

Lmbcs::Lmbcs(const Lmbcs& other)
  : QByteArray(other)
{
}

Lmbcs& Lmbcs::operator=(const Lmbcs& other)
{
  if (this == &other) return *this;
  QByteArray::operator=(other);
  return *this;
}

QString Lmbcs::toQString() const
{
  char buffer[MAXWORD];
  int unicodeLen = OSTranslate(
        OS_TRANSLATE_LMBCS_TO_UNICODE
        , constData(), size() <= (int)MAXWORD ? (WORD)size() : MAXWORD
        , buffer, MAXWORD
        );
  return QString::fromUtf16(reinterpret_cast<ushort*>(buffer)
                            , unicodeLen / UNICODE_BYTE);
}

Lmbcs Lmbcs::fromQString(const QString &qs)
{
  char buffer[MAXWORD];
  QString input = qs.left(MAX_UNICODE_LEN);
  const ushort* unicode = input.utf16();
  WORD unicodeLen = (WORD)input.size() * UNICODE_BYTE;
  int lmbcsLen = OSTranslate(
        OS_TRANSLATE_UNICODE_TO_LMBCS
        , reinterpret_cast<char*>(const_cast<ushort*>(unicode))
        , unicodeLen
        , buffer, MAXWORD
        );
  return Lmbcs(buffer, lmbcsLen);
}

} // namespace ntlx

コンストラクタには、以下の4つを用意しました。

  • デフォルトコンストラクタ(空のLMBCS)
  • LMBCSポインタと長さによるコンストラクタ
  • ステータス値によるコンストラクタ
  • コピーコンストラクタ

3番目の「ステータス値によるコンストラクタ」は、API関数OSLoadStringを利用したコンストラクタです。OSLoadStringは、指定したモジュール(指定しなければNotes)からエラーコードに対応した文字列をロードします。

<osmisc.h>

WORD LNPUBLIC OSLoadString (HMODULE hModule
                            , STATUS StringCode
                            , char far *retBuffer
                            , WORD BufferLength);

hModuleハンドルは、文字列を読み込むリソースDLLで、見つからなければNotesが保有する文字列リソースを検索します。リソースDLLがなかったり、MacLinuxなどの非Windowsプラットフォームで利用する場合は0を指定します。

StringCodeがステータス値です。ここにはERR()マクロを使ってエラーコードのみにマスクする必要があります。先に定義したntlx::Status::errorメソッドが役に立ちます。

retBufferとBufferLengthは、読み込んだ文字列を書き込む領域とそのサイズを指定します。

戻り値は、実際に書き込んだ文字列の長さになります。

次に、OSTranslateを使ってどのようにLMBCSからQString、QStringからLMBCSに変換しているかを見ていきます。

Unicode(UTF-16)の1文字は、全角半角に関係なく、1文字2バイトです。一方LMBCS日本語は、半角英数字は1バイト、全角と半角カナ文字は3バイトです。UnicodeからLMBCSに変換する場合1/2〜3/2倍になります。そこで、64k÷3×2≒42kを入力Unicodeの最大値とし、文字数としては21,504文字とします。

逆に、LMBCSからUnicodeに変換する場合、2/3〜2倍になるという式は成り立ちますが、LMBCSの文字の区切りを手動で判別するのは大変な手間を要します。そこで今回は、入力LMBCSの最大値はWORD最大値まで使用するものとし、バッファあふれは無視します。

次回は、このLMBCS文字列を適切に区切り、WORD幅より大きいサイズの文字列が扱えないか考察します。(続く)