戻る

DelphiでCharu3プラグインを作る

DelphiでCharu3プラグインを作る際にハマった事や参照した場所等をメモしていきます。



Charu3とは?

ケイジさんが作成した、クリップボード拡張+入力支援ソフトです。
■KeiziWeb ver 4.3
※2010/2/4あたりに移転した様です
■Keijiweb ver β - どうしてkeijiweb.comになったのか?

ソフト公開

DelphiでCharu3プラグインを作るサンプルソース&DLL

DelphiでCharu3のプラグインを作る方法が書かれているソースが入っています。
2008/3/2修正 Unicodeに完全対応(今まではShift-JISで処理していたのをUnicodeに完全対応)

ダウンロード

DelphiTControlToName

 DelphiのIDE(統合開発環境)で、TControlから派生したクラス(見えているコンポーネント全てです)をコピーして、このプラグインを通すと、コピーしたクラスのNameプロパティのリストが改行区切りで返されます。

ダウンロード

80x15.png この作品はクリエイティブ・コモンズ・ライセンスの下でライセンスされています。

LoadHtmlHelp

 設定されたHtmlHelpファイルを開いて指定したキーワードで検索します。
 コマンドライン版もありますので、他の環境でも使えると思います。
 プログラミング言語のリファーレンスマニュアルがHtmlHelpファイルだった場合、エディタ上で文字列を選択してヘルプを引くような使い方を想定しています。

ダウンロード

80x15.png この作品はクリエイティブ・コモンズ・ライセンスの下でライセンスされています。

はまり雑記

参考にしたページ

  • KeiziWeb ver 4.3 Charu3
    クリップボード拡張+入力支援ソフト「Charu3」のダウンロードページです。
    ここに一緒にある「プラグイン開発キット」の内容を元しています。
  • Delphiメーリングリスト
    Delphiのメーリングリストです。
    今回プラグインを作るに当たって、はまっていた内容をこのメーリングリストで教えていただきました。
  • About Delphi
    Delphiのメーリングリストの過去ログが置いてあります。

DelphiでCharu3プラグインを作るサンプルソース

S-JIS版

library CharuPlugInTest;

{ DLL でのメモリ管理について:
  もしこの DLL が引数や返り値として String 型を使う関数/手続きをエクスポー
  トする場合、以下の USES 節とこの DLL を使うプロジェクトソースの USES 節
  の両方に、最初に現れるユニットとして ShareMem を指定しなければなりません。
  (プロジェクトソースはメニューから[プロジェクト|ソース表示] を選ぶこと
  で表示されます)
  これは構造体やクラスに埋め込まれている場合も含め String 型を DLL とやり
  取りする場合に必ず必要となります。
  ShareMem は共用メモリマネージャである BORLNDMM.DLL とのインターフェース
  です。あなたの DLL と一緒に配布する必要があります。BORLNDMM.DLL を使うの
  を避けるには、PChar または ShortString 型を使って文字列のやり取りをおこ
  なってください。}

uses
  SysUtils,
  Classes,
  Windows,
  System,
  Messages;

{$R *.res}

(*
// Charu3DLL_SRC\Plugins\addBR\replace.cppのソース引用
// Cの場合の構造体
//---------------------------------------------------
// データ構造体
//---------------------------------------------------
struct STRING_DATA
{
	char	m_cKind;		//データ種別
	char	m_cIcon;		//アイコン種別
	int		m_nMyID;		//データのID
	int		m_nParentID;	//親データのID
	time_t	m_timeCreate;	//作成日時
	time_t	m_timeEdit;		//変更日時
	CString  m_strTitle;	//設定データタイトル
	CString  m_strData;		//設定データ文字列
	CString  m_strMacro;	//拡張用文字列データ
};
*)

// Delphiの場合の構造体
// ---------------------------------------------------
type
  TSTRING_DATA = record
    m_cKind: Char;         // データ種別         (Charu3:種類)
                           //   1:一時データ 2:ロック
    m_cIcon: Char;         // アイコン種別       (Charu3:アイコン)
                           //   0:普通ロック項目 1:日時 時間系 2:ファイル実行
                           //   3:関連付け実行 4:文字列選択 5:クリップボード
                           //   6:プラグイン 7:キー入力 255:自動選択
    m_nMyID: Integer;      // データのID         (何を示したIDなのか不明)
    m_nParentID: Integer;  // 親データのID       (何を示したIDなのか不明)
    m_timeCreate: LongInt; // 作成日時
                           //   (暦時間(カレンダー時間)形式の作成日時)
    m_timeEdit: LongInt;   // 変更日時
                           //   (暦時間(カレンダー時間)形式の変更日時)
    m_strTitle: Pointer;   // 設定データタイトル (Charu3:名前)
    m_strData: Pointer;    // 設定データ文字列   (Charu3:テキスト編集のテキスト)
    m_strMacro: Pointer;   // 拡張用文字列データ (Charu3:拡張マクロ)
  end;
  PSTRING_DATA = ^TSTRING_DATA;
// ---------------------------------------------------


// [Delphi-ML:20584] Re: time_t -> TDateTime ?
// を参考にしたtime_t -> TDateTimeの関数。
// ---------------------------------------------------
const
  cStart    = 25569.0;  // = EncodeDate( 1970, 1, 1 )
  cSecPerDay = 86400.0;  // 1日あたりの秒数
  cAdjust    = 9 / 24;  // 時間帯の調整

function time_tToDateTime(t: LongInt): TDateTime;
begin
  Result := (cStart * cSecPerDay + t) / cSecPerDay + cAdjust;
end;

function DateTimeTotime_t(d: TDateTime): LongInt;
begin
  Result := Round((d - cStart - cAdjust) * cSecPerDay);
end;
// ---------------------------------------------------

(*
// Cの場合の関数宣言
// Charu3DLL_SRC\Plugins\addBR\replace.cppのソース引用
extern "C" __declspec (dllexport) bool CharuPlugIn
	(TCHAR *strSource,TCHAR *strResult,int nSize,STRING_DATA *data,void *pVoid)
*)

(*
// Charu3DLL_SRC\readme.htmlのマニュアル引用
■テキスト処理プラグイン
 簡単な関数を一つ実装すればテキスト処理プラグインを作ることが出来ます。
 TCHARはMBCSの時はcharになり、Unicodeの場合はwchar_tになります。

bool CharuPlugIn(TCHAR *strSource,TCHAR *strResult,int nSize,STRING_DATA *data,void *pVoid);
□TCHAR *strSource 
 処理の元になるテキストデータが入ってきます。$SELなどのマクロは置換されたあとです。

□TCHAR *strResult
 結果のテキストを格納します。 

□int nSize
 結果のテキストを格納するバッファのサイズです。最低で10kbあります。
 大きなテキストを扱う場合は、このバッファのサイズも大きく確保されます。

□STRING_DATA *data
 選択していたデータの元です。
 拡張マクロやID、作成時間や変更時間などのデータも取れます。$SELなどのマクロも置換前の状態です。拡張マクロにオリジナルの機能を持たせたりすることも出来ます。

□void *pVoid
 拡張用ポインタでしたが、現在は貼り付けのターゲットになるウィンドウのハンドルが
入ってきます。使う場合はキャストして使ってください。
 将来変更される可能性もなきにしもあらずですけど。
*)


// [Delphi:89848] Re: VC++で作られたソフトのプラグインDLLの作成
// を参考にして、関数の呼び出し規約をcdeclにしています

// Delphiの場合、TCHARはMBCSの時はPCharになり、Unicodeの場合はPWideCharになる?

// Charu3 S-JIS版
function CharuPlugIn(
  strSource: PChar; strResult: PChar; nSize: integer;
  data: PSTRING_DATA; pVoid: Pointer): BOOL; cdecl;
var
  WkStrSource: string;
  WkStrResult: string;

  Wkm_strTitle: string;
  Wkm_strData: string;
  WKm_strMacro: string;

  Wkm_cKind: Char;
  Wkm_cIcon: Char;

  Wkm_nMyID: Integer;
  Wkm_nParentID: Integer;

  Wkm_timeCreate2: TDateTime;
  Wkm_timeEdit2: TDateTime;

  WkpVoid: HWND;
begin
  try
    // String型にコピーする
    WkStrSource := strSource;

    // data.m_cKindの保持
    Wkm_cKind := data.m_cKind;
    // data.m_cIconの保持
    Wkm_cIcon := data.m_cIcon;

    // data.m_nMyIDの保持
    Wkm_nMyID := data.m_nMyID;
    // data.m_nParentIDの保持
    Wkm_nParentID := data.m_nParentID;

    // data.m_timeCreateはCのtime_t型なのでTDateTime型に変換&コピー
    Wkm_timeCreate2 := time_tToDateTime(data.m_timeCreate);
    // data.m_timeEditはCのtime_t型なのでTDateTime型に変換&コピー
    Wkm_timeEdit2 := time_tToDateTime(data.m_timeEdit);

    // String型にコピーする
    Wkm_strTitle := PChar(data.m_strTitle);
    // String型にコピーする
    Wkm_strData := PChar(data.m_strData);
    // String型にコピーする
    Wkm_strMacro := PChar(data.m_strMacro);

    // pVoidをHWND型に変換する
    WkpVoid := HWND(pVoid^);

    //---------------ここからコードを書くといいです---------------------

    // strSourceがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('strSource [' + WkStrSource + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // data.m_cKindがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('data.m_cKind [' + IntToStr(Integer(Wkm_cKind)) + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);
    // data.m_cIconがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('data.m_cIcon [' + IntToStr(Integer(Wkm_cIcon)) + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // data.m_nMyIDがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('data.m_nMyID [' + IntToStr(Wkm_nMyID) + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);
    // data.m_nParentIDがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('data.m_nParentID [' + IntToStr(Wkm_nParentID) + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // TDateTime型に変換されたdata.m_timeCreateがきちんと受け取れているかのテストコード
    MessageBox(0, PChar(
      'data.m_timeCreate(TDateTime) [' + DateTimeToStr(Wkm_timeCreate2) + ']'),
      '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);
    // TDateTime型に変換されたdata.m_timeEditがきちんと受け取れているかのテストコード
    MessageBox(0, PChar(
      'data.m_timeEdit(TDateTime) [' + DateTimeToStr(Wkm_timeEdit2) + ']'),
      '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // data.m_strTitleがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('data.m_strTitle [' + Wkm_strTitle + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);
    // data.m_strDataがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('data.m_strData [' + Wkm_strData + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);
    // data.m_strMacroがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('data.m_strMacro [' + Wkm_strMacro + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // pVoidがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('pVoid [' + IntToStr(WkpVoid) + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // 受け取ったstrSourceの文字列を'<'で囲う加工をするテストコード
    WkStrResult := '<' +  WkStrSource + '>';

    // 加工後のWkStrResultを表示するテストコード
    MessageBox(0, PChar('strSource [' + WkStrResult + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    //---------------ここまでコードを書くといいです---------------------

    // 値は直接strResultに入れる
    StrLCopy(strResult, PChar(WkStrResult), nSize);

    result := True;
  except
    result := False;
    Exit;
  end;
end;

exports
  CharuPlugIn;

end.

Unicode版

library CharuPlugInTestu;

{ DLL でのメモリ管理について:
  もしこの DLL が引数や返り値として String 型を使う関数/手続きをエクスポー
  トする場合、以下の USES 節とこの DLL を使うプロジェクトソースの USES 節
  の両方に、最初に現れるユニットとして ShareMem を指定しなければなりません。
  (プロジェクトソースはメニューから[プロジェクト|ソース表示] を選ぶこと
  で表示されます)
  これは構造体やクラスに埋め込まれている場合も含め String 型を DLL とやり
  取りする場合に必ず必要となります。
  ShareMem は共用メモリマネージャである BORLNDMM.DLL とのインターフェース
  です。あなたの DLL と一緒に配布する必要があります。BORLNDMM.DLL を使うの
  を避けるには、PChar または ShortString 型を使って文字列のやり取りをおこ
  なってください。}

uses
  SysUtils,
  Classes,
  Windows,
  System,
  Messages,
  {以下 TntWare Delphi Unicode Controls フリー版ライブラリを使用}
  TntSystem,
  TntSysUtils,
  TntDialogs,
  TntWideStrUtils;

{$R *.res}

(*
// Charu3DLL_SRC\Plugins\addBR\replace.cppのソース引用
// Cの場合の構造体
//---------------------------------------------------
// データ構造体
//---------------------------------------------------
struct STRING_DATA
{
	char	m_cKind;		//データ種別
	char	m_cIcon;		//アイコン種別
	int		m_nMyID;		//データのID
	int		m_nParentID;	//親データのID
	time_t	m_timeCreate;	//作成日時
	time_t	m_timeEdit;		//変更日時
	CString  m_strTitle;	//設定データタイトル
	CString  m_strData;		//設定データ文字列
	CString  m_strMacro;	//拡張用文字列データ
};
*)

// Delphiの場合の構造体
// ---------------------------------------------------
type
  TSTRING_DATA = record
    m_cKind: Char;         // データ種別         (Charu3:種類)
                           //   1:一時データ 2:ロック
    m_cIcon: Char;         // アイコン種別       (Charu3:アイコン)
                           //   0:普通ロック項目 1:日時 時間系 2:ファイル実行
                           //   3:関連付け実行 4:文字列選択 5:クリップボード
                           //   6:プラグイン 7:キー入力 255:自動選択
    m_nMyID: Integer;      // データのID         (何を示したIDなのか不明)
    m_nParentID: Integer;  // 親データのID       (何を示したIDなのか不明)
    m_timeCreate: LongInt; // 作成日時
                           //   (暦時間(カレンダー時間)形式の作成日時)
    m_timeEdit: LongInt;   // 変更日時
                           //   (暦時間(カレンダー時間)形式の変更日時)
    m_strTitle: Pointer;   // 設定データタイトル (Charu3:名前)
    m_strData: Pointer;    // 設定データ文字列   (Charu3:テキスト編集のテキスト)
    m_strMacro: Pointer;   // 拡張用文字列データ (Charu3:拡張マクロ)
  end;
  PSTRING_DATA = ^TSTRING_DATA;
// ---------------------------------------------------


// [Delphi-ML:20584] Re: time_t -> TDateTime ?
// を参考にしたtime_t -> TDateTimeの関数。
// ---------------------------------------------------
const
  cStart    = 25569.0;  // = EncodeDate( 1970, 1, 1 )
  cSecPerDay = 86400.0;  // 1日あたりの秒数
  cAdjust    = 9 / 24;  // 時間帯の調整

function time_tToDateTime(t: LongInt): TDateTime;
begin
  Result := (cStart * cSecPerDay + t) / cSecPerDay + cAdjust;
end;

function DateTimeTotime_t(d: TDateTime): LongInt;
begin
  Result := Round((d - cStart - cAdjust) * cSecPerDay);
end;
// ---------------------------------------------------

(*
// Cの場合の関数宣言
// Charu3DLL_SRC\Plugins\addBR\replace.cppのソース引用
extern "C" __declspec (dllexport) bool CharuPlugIn
	(TCHAR *strSource,TCHAR *strResult,int nSize,STRING_DATA *data,void *pVoid)
*)

(*
// Charu3DLL_SRC\readme.htmlのマニュアル引用
■テキスト処理プラグイン
 簡単な関数を一つ実装すればテキスト処理プラグインを作ることが出来ます。
 TCHARはMBCSの時はcharになり、Unicodeの場合はwchar_tになります。

bool CharuPlugIn(TCHAR *strSource,TCHAR *strResult,int nSize,STRING_DATA *data,void *pVoid);
□TCHAR *strSource 
 処理の元になるテキストデータが入ってきます。$SELなどのマクロは置換されたあとです。

□TCHAR *strResult
 結果のテキストを格納します。 

□int nSize
 結果のテキストを格納するバッファのサイズです。最低で10kbあります。
 大きなテキストを扱う場合は、このバッファのサイズも大きく確保されます。

□STRING_DATA *data
 選択していたデータの元です。
 拡張マクロやID、作成時間や変更時間などのデータも取れます。$SELなどのマクロも置換前の状態です。拡張マクロにオリジナルの機能を持たせたりすることも出来ます。

□void *pVoid
 拡張用ポインタでしたが、現在は貼り付けのターゲットになるウィンドウのハンドルが
入ってきます。使う場合はキャストして使ってください。
 将来変更される可能性もなきにしもあらずですけど。
*)


// [Delphi:89848] Re: VC++で作られたソフトのプラグインDLLの作成
// を参考にして、関数の呼び出し規約をcdeclにしています

// Delphiの場合、TCHARはMBCSの時はPCharになり、Unicodeの場合はPWideCharになる?

// Charu3 Unicode版
function CharuPlugIn(
  strSource: PWideChar; strResult: PWideChar; nSize: integer;
  data: PSTRING_DATA; pVoid: Pointer): BOOL; cdecl;
var
  WkStrSource: WideString;
  WkStrResult: WideString;

  Wkm_strTitle: WideString;
  Wkm_strData: WideString;
  WKm_strMacro: WideString;

  Wkm_cKind: Char;
  Wkm_cIcon: Char;

  Wkm_nMyID: Integer;
  Wkm_nParentID: Integer;

  Wkm_timeCreate2: TDateTime;
  Wkm_timeEdit2: TDateTime;

  WkpVoid: HWND;
begin
  try
    // strSourceはUNICODEなのでWideString型にコピーする
    WkStrSource := strSource;

    // data.m_cKindの保持
    Wkm_cKind := data.m_cKind;
    // data.m_cIconの保持
    Wkm_cIcon := data.m_cIcon;

    // data.m_nMyIDの保持
    Wkm_nMyID := data.m_nMyID;
    // data.m_nParentIDの保持
    Wkm_nParentID := data.m_nParentID;

    // data.m_timeCreateはCのtime_t型なのでTDateTime型に変換&コピー
    Wkm_timeCreate2 := time_tToDateTime(data.m_timeCreate);
    // data.m_timeEditはCのtime_t型なのでTDateTime型に変換&コピー
    Wkm_timeEdit2 := time_tToDateTime(data.m_timeEdit);

    // data.m_strTitleはUNICODEなのでWideString型にコピーする
    Wkm_strTitle := PWideChar(data.m_strTitle);
    // data.m_strDataはUNICODEなのでWideString型にコピーする
    Wkm_strData := PWideChar(data.m_strData);
    // data.m_strMacroはUNICODEなのでWideString型にコピーする
    Wkm_strMacro := PWideChar(data.m_strMacro);

    // pVoidをHWND型に変換する
    WkpVoid := HWND(pVoid^);

    //---------------ここからコードを書くといいです---------------------

    // strSourceがきちんと受け取れているかのテストコード
    // UNICODEなのでMessageBoxW使用
    MessageBoxW(0, PWideChar('strSource [' + WkStrSource + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // data.m_cKindがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('data.m_cKind [' + IntToStr(Integer(Wkm_cKind)) + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);
    // data.m_cIconがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('data.m_cIcon [' + IntToStr(Integer(Wkm_cIcon)) + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // data.m_nMyIDがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('data.m_nMyID [' + IntToStr(Wkm_nMyID) + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);
    // data.m_nParentIDがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('data.m_nParentID [' + IntToStr(Wkm_nParentID) + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // TDateTime型に変換されたdata.m_timeCreateがきちんと受け取れているかのテストコード
    MessageBox(0, PChar(
      'data.m_timeCreate(TDateTime) [' + DateTimeToStr(Wkm_timeCreate2) + ']'),
      '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);
    // TDateTime型に変換されたdata.m_timeEditがきちんと受け取れているかのテストコード
    MessageBox(0, PChar(
      'data.m_timeEdit(TDateTime) [' + DateTimeToStr(Wkm_timeEdit2) + ']'),
      '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // data.m_strTitleがきちんと受け取れているかのテストコード
    // UNICODEなのでMessageBoxW使用
    MessageBoxW(0, PWideChar('data.m_strTitle [' + Wkm_strTitle + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);
    // data.m_strDataがきちんと受け取れているかのテストコード
    // UNICODEなのでMessageBoxW使用
    MessageBoxW(0, PWideChar('data.m_strData [' + Wkm_strData + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);
    // data.m_strMacroがきちんと受け取れているかのテストコード
    // UNICODEなのでMessageBoxW使用
    MessageBoxW(0, PWideChar('data.m_strMacro [' + Wkm_strMacro + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // pVoidがきちんと受け取れているかのテストコード
    MessageBox(0, PChar('pVoid [' + IntToStr(WkpVoid) + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    // 受け取ったstrSourceの文字列を'<'で囲う加工をするテストコード
    WkStrResult := '<' +  WkStrSource + '>';

    // 加工後のWkStrResultを表示するテストコード
    // UNICODEなのでMessageBoxW使用
    MessageBoxW(0, PWideChar('strSource [' + WkStrResult + ']'), '情報',
      MB_OK or MB_ICONINFORMATION or MB_TASKMODAL);

    //---------------ここまでコードを書くといいです---------------------

    // 値は直接strResultに入れる
    WStrLCopy(strResult, PWideChar(WkStrResult), nSize);

    result := True;
  except
    result := False;
    Exit;
  end;
end;

exports
  CharuPlugIn;

end.