## Plugins

### Table of Contents

- Quick description
- Events
- API
- Properties
- Methods
- Syntax
  - Properties syntax
  - Methods syntax
- How to write plugins quickly ?
- How to debug VC++ plugins ?

### Quick description

Plugins use an ActiveX interface. Plugins may be written in any format that supports this interface.

Examples are available in :

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

Limitation : Scriptlets only work for `EDITOR_SCRIPT` plugins.

Properties are used to present information concerning the plugin.
Methods are used to process the data. Method names and syntax depends on events and API (see below).

### Events

`EDITOR_SCRIPT`

  In editor view, apply a function to the current selection.

`PREDIFF`

  Preprocess file before diffing : the plugin is not apply to the text displayed in the editor. It is applied only to a copy of the left and right texts, and this copy are then scanned to create the difference list. As now :

  - you may delete one column, change the names of variables...
  - you may not add/delete/move lines.
`PACK_UNPACK`

  Transform a file in a viewable format (for example, decompress a file...)

  - The editor displays the unpacked data.
  - Sometimes files may be packed again (zipped files...). An additional function is of course necessary.
  - If the author of the plugin created this function, file may be saved again in the compressed format.
  - Else the file can only be saved in a text format. To avoid problems, you are proposed to change the filename when saving a changed file.

### API

Some events have two API. One to exchange the data through a `BSTR` (memory) and one through input/ouput files.

`FILE_PREDIFF` | data are exchanged through an input and an output file
`BUFFER_PREDIFF` | data are exchanged through a `BSTR`
`FILE_PACK_UNPACK` | data are exchanged through an input and an output file
`BUFFER_PACK_UNPACK` | data are exchanged through a `SafeArray` (`BSTR` not available as the packed data are possibly not text)
`EDITOR_SCRIPT` | data are exchanged through a `BSTR`

You need to define only one API to handle an event. Define the one you prefer.

### Properties

Name | Mandatory | Events
--- | --- | ---
`PluginEvent` | yes | all
`PluginDescription` | no | all
`PluginFileFilters` | no | `PACK_UNPACK`, `PREDIFF`
`PluginIsAutomatic` | if `PluginFileFilters` is defined | `PACK_UNPACK`, `PREDIFF`

`PluginIsAutomatic` and `PluginFileFilters` are for automatic mode :

- When `PluginIsAutomatic` is `false`, the plugin is never used in automatic mode.
- When `PluginIsAutomatic` is `true`, `PluginFileFilters` is compared to the filename of both files. If one file matches the filter, the plugin is applied.

### Methods

API | Method name
--- | ---
`EDITOR_SCRIPT` | function name is free Note : several functions may be defined in one `EDITOR_SCRIPT` plugin
`BUFFER_PREDIFF` | `PrediffBufferW`
`FILE_PREDIFF` | `PrediffFile`
`BUFFER_PACK_UNPACK` | `UnpackBufferA` `PackBufferA`
`FILE_PACK_UNPACK` | `UnpackFile` `PackFile`

Note : `PACK_UNPACK` functions use an additional parameter. The value may be set during `UnpackBuffer`. When file is changed, the value is forwarded to `PackBuffer`. The goal is to pass a parameter from `UnpackBuffer` to `PackBuffer`.
For example, the plugin may handle several compressed formats, and use this value to recompress a file in the format of the original. This parameter is mandatory for the function's syntax. But you don't have to set its value when you don't use it.

### 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`

String formed of fileFilters, separated with "`;`"

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`

| Functions parameters (function names are free)
--- | ---
C++ | `STDMETHOD(MakeUpperVB)([in] BSTR inputText, [out, retval] BSTR * outputText);`
VB | `Public Function MakeUpperVB(text As String)`
VBS | `Function MakeUpperVBS(Text)`

#### `FILE_PREDIFF`

| Functions names | Functions parameters
--- | --- | ---
VC++ | `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`

| Functions names | Functions parameters
--- | --- | ---
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`

| Functions names | Functions parameters
--- | --- | ---
VC++ | `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`

| Functions names | Functions parameters
--- | --- | ---
VC++ | `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`

### How to write plugins quickly ?

Easiest plugins are scriptlets.

Just VBscript (or JavaScript probably) with an additional section `<implement>`. See examples.
But they are difficult to debug. And valid only for `EDITOR_SCRIPT` events.

#### VC++ plugins :

The most difficult to write when you do it from scratch. See in `Plugins/syntax.txt`, there are three additional steps from normal COM dll.

But easy to write from an existing plugin.

1. Select a C++ plugin with the same API
2. Rename the files `cpp,def,dsp,idl,rc` : replace [name of old plugin] with [name of your plugin]
3. In all the files, replace all instances of [name of old plugin] with [name of your plugin]
4. Write your custom code : `WinMergeScript.cpp` holds all the important functions.
5. Generate new GUIDs and add to the `.idl` file.

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

- do not register the dll : delete everything in 'settings'->'custom build'
- do not register the dll : delete the file `.rgs`, and the registry section in the file `.rc`
- do not register the dll : add `typeinfoex.h` + and make 3 changes in `WinMergeScript.h` (see commented lines)
- `SAFEARRAY` : replace the interface in `.idl` :
  - `SAFEARRAY *` `SAFEARRAY(unsigned char)`
  - `SAFEARRAY **` `SAFEARRAY(unsigned char) *`

### How to debug VC++ plugins ?

Easy with Visual Studio after you installed WinMerge source.

#### `EDITOR_SCRIPT` :

1. Set a breakpoint at the beginning of `safeInvokeA` in `Plugins.cpp`.
2. Run WinMerge
3. Do all you need (open file, menu...) to call the plugin.
4. The breakpoint is triggered. The plugin interface is loaded at this moment. Open the file `WinMergeScript.cpp` source of your plugin in the debugging session.
5. Set a breakpoint in this file at the beginning of your function.
6. `F5`. The breakpoint in your function is triggered.

#### `PREDIFF`, `PACK_UNPACK` :

Same steps, point #1 only differs:

1. Set a breakpoint at the beginning of `safeInvokeW` in `Plugins.cpp`.
   Note : `safeInvokeW` instead of `safeInvokeA`.