WinMergeのプラグインについて(開発者向け)

この記事は
https://github.com/WinMerge/winmerge/blob/master/Docs/Developers/Plugins.html
を自分用に翻訳したメモです。

実際にプラグインを作成する場合、
http://uktaniyama.web.fc2.com/winmerge/WinMergePlugins.html
が参考になるかもしれません。

----

なお、ドキュメント化されていないが、 2.14.0-jp-63からは
> `FILE_FOLDER_PACK_UNPACK`(ファイルを複数のファイルに展開)

というイベントが使用できるようで、WinMergeに同梱されているスクリプトレット(*.sct)のいくつかはこのAPIを使用している。

また、PrediffLineFilter.sctでは、`BUFFER_PREDIFF`が使用されているので、現在はスクリプトレットの制限は緩和されているようだ。

----

# Plugins

## Quick description

プラグインはActiveXインターフェースを使用します。 
プラグインは、このインターフェイスをサポートする任意の形式で記述できます。

使用可能な例:

- C++ COM component
- VB ActiveX dll
- Scriptlets (VBS, JS)
- Delphi

制限: Scriptletsは`EDITOR_SCRIPT` プラグインでのみ動作します。

Propertiesはプラグインに関する情報を表示するために使用します。

Methodsはデータを処理するために使用されます。 
メソッド名とSyntaxは、EventsとAPIに依ります（以下を参照）。

## Events

### `EDITOR_SCRIPT`

エディタービューで、現在の選択範囲に関数を適用します。

### `PREDIFF`

差分をとる前に、ファイルを前処理します。プラグインは、エディターに表示されるテキストには適用されません。 
これは、左右のテキストのコピーにのみ適用され、このコピーがスキャンされて差分リストが作成されます。 
今現在：

- 一つの列の削除や、変数名を変更できます。
- 行の追加/削除/移動はできません。

### `PACK_UNPACK`

表示可能な形式でファイルを変換します（例:ファイルを解凍します）。

- エディターは、解凍されたデータを表示します。
- ファイルが再圧縮されている場合があります（zip形式のファイル...）。 もちろん追加の関数が必要です。
- プラグインの作成者がこの関数を作成した場合、ファイルは再び圧縮形式で保存されます。
-  それ以外の場合、ファイルはテキスト形式でのみ保存できます。 問題を回避するため、変更されたファイルを保存するときにファイル名を変更することをお勧めします。


## API

一部のイベントには2つのAPIがあります。 1つは`BSTR`（メモリ）を介してデータを交換し、もう1つは入力/出力ファイルを介して交換します。

|                      |                                                                                                                         |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `FILE_PREDIFF`       | データは入力ファイルと出力ファイルを介して交換されます                                                                  |
| `BUFFER_PREDIFF`     | データは`BSTR`を介して交換されます                                                                                      |
| `FILE_PACK_UNPACK`   | データは入力ファイルと出力ファイルを介して交換されます                                                                  |
| `BUFFER_PACK_UNPACK` | データは `SafeArray`を介して交換されます (パックされたデータはテキストではない可能性があるため、`BSTR`は使用できません) |
| `EDITOR_SCRIPT`      | データは`BSTR`を介して交換されます                                                                                      |

イベントを処理するAPIを1つだけ定義する必要があります。 お好みのものを定義します。

## Properties

| 名前                | 必須                                     | Events                   |
| ------------------- | ---------------------------------------- | ------------------------ |
| `PluginEvent`       | yes                                      | all                      |
| `PluginDescription` | no                                       | all                      |
| `PluginFileFilters` | no                                       | `PACK_UNPACK`, `PREDIFF` |
| `PluginIsAutomatic` | `PluginFileFilters` が定義されている場合 | `PACK_UNPACK`, `PREDIFF` |

PluginIsAutomaticおよびPluginFileFiltersは自動モード用です:

- `PluginIsAutomatic`がfalseの場合、プラグインは自動モードで使用されません。
- `PluginIsAutomatic`がtrueの場合、`PluginFileFilters`は両方のファイルのファイル名と比較されます。 1つのファイルがフィルターに一致する場合、プラグインが適用されます。

## Methods

| API                | Method name                                                                       |
| ------------------ | --------------------------------------------------------------------------------- |
| EDITOR_SCRIPT      | 関数名は自由です<br>注:1つのEDITOR_SCRIPTプラグインでいくつかの関数を定義できます |
| BUFFER_PREDIFF     | `PrediffBufferW`                                                                  |
| FILE_PREDIFF       | `PrediffFile`                                                                     |
| BUFFER_PACK_UNPACK | `UnpackBufferA`<br>`PackBufferA`                                                  |
| FILE_PACK_UNPACK   | `UnpackFile`<br>`PackFile`                                                        |

注: PACK_UNPACK関数は追加のパラメーターを使用します。 値はUnpackBuffer時に設定できます。 ファイルが変更されると、値は`PackBuffer`に転送されます。 目標は、`UnpackBuffer`から`PackBuffer`にパラメーターを渡すことです。
たとえば、プラグインはいくつかの圧縮形式を処理し、この値を使用して元の形式のファイルを再圧縮します。 このパラメーターは、関数のSyntaxに必須です。 ただし、使用しない場合は値を設定する必要はありません。

## Syntax

## Properties syntax

### PluginEvent

|     |                                                              |
| --- | ------------------------------------------------------------ |
| C++ | `STDMETHODIMP CWinMergeScript::get_PluginEvent(BSTR * pVal)` |
| VB  | `Public Property Get PluginEvent() As String`                |
| VBS | `Function get_PluginEvent()`                                 |

### PluginDescription

|     |                                                                    |
| --- | ------------------------------------------------------------------ |
| C++ | `STDMETHODIMP CWinMergeScript::get_PluginDescription(BSTR * pVal)` |
| VB  | `Public Property Get PluginDescription() As String`                |
| VBS | `Function get_PluginDescription()`                                 |

### PluginFileFilters

";"で区切られたfileFiltersで構成される文字列

|     |                                                                    |
| --- | ------------------------------------------------------------------ |
| C++ | `STDMETHODIMP CWinMergeScript::get_PluginFileFilters(BSTR * pVal)` |
| VB  | `Public Property Get PluginFileFilters() As String`                |

### PluginIsAutomatic

|     |                                                                            |
| --- | -------------------------------------------------------------------------- |
| C++ | `STDMETHODIMP CWinMergeScript::get_PluginIsAutomatic(VARIANT_BOOL * pVal)` |
| VB  | `Public Property Get PluginIsAutomatic() As Boolean`                       |

## Methods syntax

### EDITOR_SCRIPT

|     | 関数パラメーター(関数名は自由)                                                  |
| --- | ------------------------------------------------------------------------------- |
| C++ | `STDMETHOD(MakeUpperVB)([in] BSTR inputText, [out, retval] BSTR * outputText);` |
| VB  | `Public Function MakeUpperVB(text As String)`                                   |
| VBS | `Function MakeUpperVBS(Text)`                                                   |

### FILE_PREDIFF

|     | 関数名                        | 関数パラメーター                                                                                                           |
| --- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| C++ | `STDMETHOD(PrediffFile)`      | `([in] BSTR fileSrc, [in] BSTR fileDst, VARIANT_BOOL * pbChanged, INT * pSubcode, [out, retval] VARIANT_BOOL * pbSuccess)` |
| VB  | `Public Function PrediffFile` | `(BSTR fileSrc, BSTR fileDst, ByRef bChanged As Boolean, ByRef subcode As Long) As Boolean`                                |

### BUFFER_PREDIFF

|     | 関数名                           | 関数パラメーター                                                                                                |
| --- | -------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| C++ | `STDMETHOD(PrediffBufferW)`      | `([in] BSTR * pText, [in] INT * pSize, [in] VARIANT_BOOL * pbChanged, [out, retval] VARIANT_BOOL * pbHandled);` |
| VB  | `Public Function PrediffBufferW` | `(ByRef text As String, ByRef size As Long, ByRef bChanged As Boolean) As Boolean`                              |

### FILE_PACK_UNPACK

|     | 関数名                       | 関数パラメーター                                                                                                           |
| --- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| C++ | `STDMETHOD(UnpackFile)`      | `([in] BSTR fileSrc, [in] BSTR fileDst, VARIANT_BOOL * pbChanged, INT * pSubcode, [out, retval] VARIANT_BOOL * pbSuccess)` |
|     | `STDMETHOD(PackFile)`        | `([in] BSTR fileSrc, [in] BSTR fileDst, VARIANT_BOOL * pbChanged, INT pSubcode, [out, retval] VARIANT_BOOL * pbSuccess)`   |
| VB  | `Public Function UnpackFile` | `(BSTR fileSrc, BSTR fileDst, ByRef bChanged As Boolean, ByRef subcode As Long) As Boolean`                                |
|     | `Public Function PackFile`   | `(BSTR fileSrc, BSTR fileDst, ByRef bChanged As Boolean, subcode As Long) As Boolean`                                      |

### BUFFER_PACK_UNPACK

|     | 関数名                          | 関数パラメーター                                                                                                                            |
| --- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| C++ | `STDMETHOD(UnpackBufferA)`      | `([in] SAFEARRAY ** pBuffer, [in] INT * pSize, [in] VARIANT_BOOL * pbChanged, [in] INT * pSubcode, [out, retval] VARIANT_BOOL * pbSuccess)` |
|     | `STDMETHOD(PackBufferA)`        | `([in] SAFEARRAY ** pBuffer, [in] INT * pSize, [in] VARIANT_BOOL * pbChanged, [in] INT subcode, [out, retval] VARIANT_BOOL * pbSuccess)`    |
| VB  | `Public Function UnpackBufferA` | `(ByRef buffer() As Byte, ByRef size As Long, ByRef bChanged As Boolean, ByRef subcode As Long) As Boolean`                                 |
|     | `Public Function PackBufferA`   | `(ByRef buffer() As Byte, ByRef size As Long, ByRef bChanged As Boolean, subcode As Long) As Boolean`                                       |

## 素早くプラグインを書くには?

最も簡単なプラグインはScriptletsです。追加のセクション\<implement\>があるVBscript（またはJavaScript）だけです。
例を参照してください。
ただし、デバッグは困難です。また、`EDITOR_SCRIPT`イベントにのみ有効です。

### VC++ plugins :

スクラッチから書くのが最も難しい。
 Plugins/syntax.txtを参照してください。通常のCOM dllに比べ、3つの追加手順があります。

 しかし、既存のプラグインから簡単に記述できます。

1. 同じAPIのC ++プラグインを選択する。
2. cpp,def,dsp,idl,rcファイルの名前を変更します。[古いプラグインの名前]を[プラグインの名前]に置き換えます。
3. すべてのファイルで、[古いプラグインの名前]のすべてのインスタンスを[プラグインの名前]に置き換えます。
4. カスタムコードを記述します。WinMergeScript.cppにはすべての重要な機能が含まれています。
5. 新しいGUIDを生成し、.idlファイルに追加します。

### Additional steps to write a plugin in C++

- dllを登録しないでください: '設定'-> 'カスタムビルド'のすべてを削除します。
- dllを登録しないでください: .rgsファイルおよび.rcファイルのレジストリセクションを削除します。
- dllを登録しないでください: typeinfoex.h +を追加し、WinMergeScript.hで3つの変更を行います（コメント行を参照）
- `SAFEARRAY`：.idlのインターフェースを置き換えます：
  - `SAFEARRAY *	SAFEARRAY(unsigned char)`
  - `SAFEARRAY **	SAFEARRAY(unsigned char) *`

## VC++ pluginsをデバッグするには?

### `EDITOR_SCRIPT` :

1. Plugins.cppのsafeInvokeAの先頭にブレークポイントを設定します。
2. WinMergeを実行する。
3. プラグインを呼び出すために必要なすべての操作（ファイルを開く、メニュー...）を行います。
4. ブレークポイントがトリガーされます。 プラグインインターフェイスはこの時点でロードされます。 デバッグセッションでプラグインのWinMergeScript.cppソースファイルを開きます。
5.  このファイルのあなたの関数の先頭にブレークポイントを設定します。
6.  F5を押します。関数のブレークポイントがトリガーされます

### `PREDIFF`, `PACK_UNPACK` :

同じ手順、ポイント1のみが異なります。

1. Plugins.cppのsafeInvokeWの先頭にブレークポイントを設定します。

注：safeInvokeAではなくsafeInvokeWです。