OSDN Git Service

Merge with stable
[winmerge-jp/winmerge-jp.git] / Src / Plugins.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    WinMerge:  an interactive diff/merge utility
3 //    Copyright (C) 1997-2000  Thingamahoochie Software
4 //    Author: Dean Grimm
5 //
6 //    This program is free software; you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation; either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    This program is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License
17 //    along with this program; if not, write to the Free Software
18 //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 //
20 /////////////////////////////////////////////////////////////////////////////
21 /**
22  *  @file Plugins.cpp
23  *
24  *  @brief Support for VBS Scriptlets, VB ActiveX DLL, VC++ COM DLL
25  */ 
26
27 #include "Plugins.h"
28 #define POCO_NO_UNWINDOWS 1
29 #include <vector>
30 #include <list>
31 #include <algorithm>
32 #include <cstdarg>
33 #include <cassert>
34 #include <Poco/Mutex.h>
35 #include <Poco/ScopedLock.h>
36 #include <Poco/RegularExpression.h>
37 #include <windows.h>
38 #include "MergeApp.h"
39 #include "unicoder.h"
40 #include "FileFilterMgr.h"
41 #include "lwdisp.h"
42 #include "resource.h"
43 #include "Exceptions.h"
44 #include "RegKey.h"
45 #include "paths.h"
46 #include "Environment.h"
47 #include "FileFilter.h"
48
49 using std::vector;
50 using Poco::RegularExpression;
51 using Poco::FastMutex;
52 using Poco::ScopedLock;
53
54 /**
55  * @brief Category of transformation : define the transformation events
56  *
57  * @note USER categories are calls to scriptlets, or VB ActiveX DLL, or VC COM DLL
58  * Use text definition : if you add one, nothing to do ; 
59  * if you change one, you just have change the dll/scripts for that event
60  */
61 const wchar_t *TransformationCategories[] = 
62 {
63         L"BUFFER_PREDIFF",
64         L"FILE_PREDIFF",
65         L"EDITOR_SCRIPT",
66         L"BUFFER_PACK_UNPACK",
67         L"FILE_PACK_UNPACK",
68         L"FILE_FOLDER_PACK_UNPACK",
69         NULL,           // last empty : necessary
70 };
71
72 static vector<String> theScriptletList;
73 /// Need to lock the *.sct so the user can't delete them
74 static vector<HANDLE> theScriptletHandleList;
75 static bool scriptletsLoaded=false;
76 static FastMutex scriptletsSem;
77
78 template<class T> struct AutoReleaser
79 {
80         AutoReleaser(T *ptr) : p(ptr) {}
81         ~AutoReleaser() { if (p) p->Release(); }
82         T *p;
83 };
84
85 ////////////////////////////////////////////////////////////////////////////////
86 /**
87  * @brief Check for the presence of Windows Script
88  *
89  * .sct plugins require this optional component
90  */
91 bool IsWindowsScriptThere()
92 {
93         CRegKeyEx keyFile;
94         if (!keyFile.QueryRegMachine(_T("SOFTWARE\\Classes\\scriptletfile\\AutoRegister")))
95                 return false;
96
97         String filename = keyFile.ReadString(_T(""), _T("")).c_str();
98         keyFile.Close();
99         if (filename.empty())
100                 return false;
101
102         return (paths_DoesPathExist(filename) == IS_EXISTING_FILE);
103 }
104
105 ////////////////////////////////////////////////////////////////////////////////
106 // scriptlet/activeX support for function names
107
108 // list the function IDs and names in a script or activeX dll
109 int GetFunctionsFromScript(IDispatch *piDispatch, vector<String>& namesArray, vector<int>& IdArray, INVOKEKIND wantedKind)
110 {
111         HRESULT hr;
112         UINT iValidFunc = 0;
113         if (piDispatch)
114         {
115                 ITypeInfo *piTypeInfo=0;
116                 unsigned  iTInfo = 0; // 0 for type information of IDispatch itself
117                 LCID  lcid=0; // locale for localized method names (ignore if no localized names)
118                 if (SUCCEEDED(hr = piDispatch->GetTypeInfo(iTInfo, lcid, &piTypeInfo)))
119                 {
120                         TYPEATTR *pTypeAttr=0;
121                         if (SUCCEEDED(hr = piTypeInfo->GetTypeAttr(&pTypeAttr)))
122                         {
123                                 // allocate arrays for the returned structures
124                                 // the names array is NULL terminated
125                                 namesArray.resize(pTypeAttr->cFuncs+1);
126                                 IdArray.resize(pTypeAttr->cFuncs+1);
127
128                                 UINT iMaxFunc = pTypeAttr->cFuncs - 1;
129                                 for (UINT iFunc = 0 ; iFunc <= iMaxFunc ; ++iFunc)
130                                 {
131                                         UINT iFuncDesc = iMaxFunc - iFunc;
132                                         FUNCDESC *pFuncDesc;
133                                         if (SUCCEEDED(hr = piTypeInfo->GetFuncDesc(iFuncDesc, &pFuncDesc)))
134                                         {
135                                                 // exclude properties
136                                                 // exclude IDispatch inherited methods
137                                                 if (pFuncDesc->invkind & wantedKind && !(pFuncDesc->wFuncFlags & 1))
138                                                 {
139                                                         BSTR bstrName;
140                                                         UINT cNames;
141                                                         if (SUCCEEDED(hr = piTypeInfo->GetNames(pFuncDesc->memid,
142                                                                 &bstrName, 1, &cNames)))
143                                                         {
144                                                                 IdArray[iValidFunc] = pFuncDesc->memid;
145                                                                 namesArray[iValidFunc] = ucr::toTString(bstrName);
146                                                                 iValidFunc ++;
147                                                         }
148                                                         SysFreeString(bstrName);
149                                                 }
150                                                 piTypeInfo->ReleaseFuncDesc(pFuncDesc);
151                                         }
152                                 }
153                                 piTypeInfo->ReleaseTypeAttr(pTypeAttr);
154                         }
155                         piTypeInfo->Release();
156                 }
157         }
158         return iValidFunc;
159 }
160
161 int GetMethodsFromScript(IDispatch *piDispatch, vector<String>& namesArray, vector<int> &IdArray)
162 {
163         return GetFunctionsFromScript(piDispatch, namesArray, IdArray, INVOKE_FUNC);
164 }
165 int GetPropertyGetsFromScript(IDispatch *piDispatch, vector<String>& namesArray, vector<int> &IdArray)
166 {
167         return GetFunctionsFromScript(piDispatch, namesArray, IdArray, INVOKE_PROPERTYGET);
168 }
169
170
171 // search a function name in a scriptlet or activeX dll
172 bool SearchScriptForMethodName(LPDISPATCH piDispatch, const wchar_t *functionName)
173 {
174         bool bFound = false;
175
176         vector<String> namesArray;
177         vector<int> IdArray;
178         int nFnc = GetMethodsFromScript(piDispatch, namesArray, IdArray);
179
180         String tfuncname = ucr::toTString(functionName);
181         int iFnc;
182         for (iFnc = 0 ; iFnc < nFnc ; iFnc++)
183         {
184                 if (namesArray[iFnc] == tfuncname)
185                         bFound = true;
186         }
187         return bFound;
188 }
189
190 // search a property name (with get interface) in a scriptlet or activeX dll
191 bool SearchScriptForDefinedProperties(IDispatch *piDispatch, const wchar_t *functionName)
192 {
193         bool bFound = false;
194
195         vector<String> namesArray;
196         vector<int> IdArray;
197         int nFnc = GetPropertyGetsFromScript(piDispatch, namesArray, IdArray);
198
199         String tfuncname = ucr::toTString(functionName);
200         int iFnc;
201         for (iFnc = 0 ; iFnc < nFnc ; iFnc++)
202         {
203                 if (namesArray[iFnc] == tfuncname)
204                         bFound = true;
205         }
206         return bFound;
207 }
208
209
210 int CountMethodsInScript(LPDISPATCH piDispatch)
211 {
212         vector<String> namesArray;
213         vector<int> IdArray;
214         int nFnc = GetMethodsFromScript(piDispatch, namesArray, IdArray);
215
216         return nFnc;
217 }
218
219 /** 
220  * @return ID of the function or -1 if no function with this index
221  */
222 int GetMethodIDInScript(LPDISPATCH piDispatch, int methodIndex)
223 {
224         int fncID;
225
226         vector<String> namesArray;
227         vector<int> IdArray;
228         int nFnc = GetMethodsFromScript(piDispatch, namesArray, IdArray);
229
230         if (methodIndex < nFnc)
231         {
232                 fncID = IdArray[methodIndex];
233         }
234         else
235         {
236                 fncID = -1;
237         }
238         
239         return fncID;
240 }
241
242 ////////////////////////////////////////////////////////////////////////////////
243 // find scripts/activeX for an event : each event is assigned to a subdirectory 
244
245
246 /**
247  * @brief Get a list of scriptlet file
248  *
249  * @return Returns an array of LPSTR
250  */
251 static void GetScriptletsAt(const String& sSearchPath, const String& extension, vector<String>& scriptlets )
252 {
253         WIN32_FIND_DATA ffi;
254         String strFileSpec = paths_ConcatPath(sSearchPath, _T("*") + extension);
255         HANDLE hff = FindFirstFile(strFileSpec.c_str(), &ffi);
256         
257         if (  hff != INVALID_HANDLE_VALUE )
258         {
259                 do
260                 {
261                         if (!(ffi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
262                         {
263                                 strFileSpec = paths_ConcatPath(sSearchPath, ffi.cFileName);
264                                 scriptlets.push_back(strFileSpec);  
265                         }
266                 }
267                 while (FindNextFile(hff, &ffi));
268                 FindClose(hff);
269         }
270 }
271
272 void PluginInfo::LoadFilterString()
273 {
274         m_filters.clear();
275
276         String sLine(m_filtersText);
277         String sPiece;
278
279         while(1)
280         {
281                 String::size_type pos = sLine.rfind(';');
282                 sPiece = sLine.substr(pos+1);
283                 if (pos == String::npos)
284                         pos = 0;
285                 sLine = sLine.substr(0, pos);
286                 if (sPiece.empty())
287                         break;
288
289                 sPiece = string_makeupper(string_trim_ws_begin(sPiece));
290
291                 int re_opts = 0;
292                 std::string regexString = ucr::toUTF8(sPiece);
293                 re_opts |= RegularExpression::RE_UTF8;
294                 try
295                 {
296                         m_filters.push_back(FileFilterElementPtr(new FileFilterElement(regexString, re_opts)));
297                 }
298                 catch (...)
299                 {
300                         // TODO:
301                 }
302         };
303 }
304
305
306 bool PluginInfo::TestAgainstRegList(const String& szTest) const
307 {
308         if (m_filters.empty() || szTest.empty())
309                 return false;
310
311         String sLine = szTest;
312         String sPiece;
313
314         while(!sLine.empty())
315         {
316                 String::size_type pos = sLine.rfind('|');
317                 sPiece = sLine.substr(pos+1);
318                 if (pos == String::npos)
319                         pos = 0;
320                 sLine = sLine.substr(0, pos);
321                 if (sPiece.empty())
322                         continue;
323                 sPiece = string_makeupper(string_trim_ws_begin(sPiece));
324
325                 if (::TestAgainstRegList(&m_filters, sPiece))
326                         return true;
327         };
328
329         return false;
330 }
331
332 /**
333  * @brief Log technical explanation, in English, of script error
334  */
335 static void
336 ScriptletError(const String & scriptletFilepath, const wchar_t *transformationEvent, const TCHAR *szError)
337 {
338         String msg = _T("Plugin scriptlet error <")
339                 + scriptletFilepath
340                 + _T("> [")
341                 + ucr::toTString(transformationEvent)
342                 + _T("] ")
343                 + szError;
344     LogErrorString(msg);
345 }
346
347 /**
348  * @brief Tiny structure that remembers current scriptlet & event info for calling Log
349  */
350 struct ScriptInfo
351 {
352         ScriptInfo(const String & scriptletFilepath, const wchar_t *transformationEvent)
353                 : m_scriptletFilepath(scriptletFilepath)
354                 , m_transformationEvent(transformationEvent)
355         {
356         }
357         void Log(const TCHAR *szError)
358         {
359                 ScriptletError(m_scriptletFilepath, m_transformationEvent, szError);
360         }
361         const String & m_scriptletFilepath;
362         const wchar_t *m_transformationEvent;
363 };
364
365 /**
366  * @brief Try to load a plugin
367  *
368  * @return 1 if plugin handles this event, 0 if not, negatives for errors
369  */
370 int PluginInfo::LoadPlugin(const String & scriptletFilepath, const wchar_t *transformationEvent)
371 {
372         // set up object in case we need to log info
373         ScriptInfo scinfo(scriptletFilepath, transformationEvent);
374
375         // Search for the class "WinMergeScript"
376         LPDISPATCH lpDispatch = CreateDispatchBySource(scriptletFilepath.c_str(), L"WinMergeScript");
377         if (lpDispatch == 0)
378         {
379                 scinfo.Log(_T("WinMergeScript entry point not found"));
380                 return -10; // error
381         }
382
383         // Ensure that interface is released if any bad exit or exception
384         AutoReleaser<IDispatch> drv(lpDispatch);
385
386         // Is this plugin for this transformationEvent ?
387         VARIANT ret;
388         // invoke mandatory method get PluginEvent
389         VariantInit(&ret);
390         if (!SearchScriptForDefinedProperties(lpDispatch, L"PluginEvent"))
391         {
392                 scinfo.Log(_T("PluginEvent method missing"));
393                 return -20; // error
394         }
395         HRESULT h = ::invokeW(lpDispatch, &ret, L"PluginEvent", opGet[0], NULL);
396         if (FAILED(h) || ret.vt != VT_BSTR)
397         {
398                 scinfo.Log(     _T("Error accessing PluginEvent method"));
399                 return -30; // error
400         }
401         if (wcscmp(ret.bstrVal, transformationEvent) != 0)
402         {
403                 return 0; // doesn't handle this event
404         }
405         VariantClear(&ret);
406
407         // plugins PREDIFF or PACK_UNPACK : functions names are mandatory
408         // Check that the plugin offers the requested functions
409         // set the mode for the events which uses it
410         bool bFound = true;
411         if (wcscmp(transformationEvent, L"BUFFER_PREDIFF") == 0)
412         {
413                 bFound &= SearchScriptForMethodName(lpDispatch, L"PrediffBufferW");
414         }
415         else if (wcscmp(transformationEvent, L"FILE_PREDIFF") == 0)
416         {
417                 bFound &= SearchScriptForMethodName(lpDispatch, L"PrediffFile");
418         }
419         else if (wcscmp(transformationEvent, L"BUFFER_PACK_UNPACK") == 0)
420         {
421                 bFound &= SearchScriptForMethodName(lpDispatch, L"UnpackBufferA");
422                 bFound &= SearchScriptForMethodName(lpDispatch, L"PackBufferA");
423         }
424         else if (wcscmp(transformationEvent, L"FILE_PACK_UNPACK") == 0)
425         {
426                 bFound &= SearchScriptForMethodName(lpDispatch, L"UnpackFile");
427                 bFound &= SearchScriptForMethodName(lpDispatch, L"PackFile");
428         }
429         else if (wcscmp(transformationEvent, L"FILE_FOLDER_PACK_UNPACK") == 0)
430         {
431                 bFound &= SearchScriptForMethodName(lpDispatch, L"IsFolder");
432                 bFound &= SearchScriptForMethodName(lpDispatch, L"UnpackFile");
433                 bFound &= SearchScriptForMethodName(lpDispatch, L"PackFile");
434                 bFound &= SearchScriptForMethodName(lpDispatch, L"UnpackFolder");
435                 bFound &= SearchScriptForMethodName(lpDispatch, L"PackFolder");
436         }
437         if (!bFound)
438         {
439                 // error (Plugin doesn't support the method as it claimed)
440                 scinfo.Log(_T("Plugin doesn't support the method as it claimed"));
441                 return -40; 
442         }
443
444         // plugins EDITOR_SCRIPT : functions names are free
445         // there may be several functions inside one script, count the number of functions
446         if (wcscmp(transformationEvent, L"EDITOR_SCRIPT") == 0)
447         {
448                 m_nFreeFunctions = CountMethodsInScript(lpDispatch);
449                 if (m_nFreeFunctions == 0)
450                         // error (Plugin doesn't offer any method, what is this ?)
451                         return -50;
452         }
453
454
455         // get optional property PluginDescription
456         if (SearchScriptForDefinedProperties(lpDispatch, L"PluginDescription"))
457         {
458                 h = ::invokeW(lpDispatch, &ret, L"PluginDescription", opGet[0], NULL);
459                 if (FAILED(h) || ret.vt != VT_BSTR)
460                 {
461                         scinfo.Log(_T("Plugin had PluginDescription property, but error getting its value"));
462                         return -60; // error (Plugin had PluginDescription property, but error getting its value)
463                 }
464                 m_description = ucr::toTString(ret.bstrVal);
465         }
466         else
467         {
468                 // no description, use filename
469                 m_description = paths_FindFileName(scriptletFilepath);
470         }
471         VariantClear(&ret);
472
473         // get PluginFileFilters
474         bool hasPluginFileFilters = false;
475         if (SearchScriptForDefinedProperties(lpDispatch, L"PluginFileFilters"))
476         {
477                 h = ::invokeW(lpDispatch, &ret, L"PluginFileFilters", opGet[0], NULL);
478                 if (FAILED(h) || ret.vt != VT_BSTR)
479                 {
480                         scinfo.Log(_T("Plugin had PluginFileFilters property, but error getting its value"));
481                         return -70; // error (Plugin had PluginFileFilters property, but error getting its value)
482                 }
483                 m_filtersText = ucr::toTString(ret.bstrVal);
484                 hasPluginFileFilters = true;
485         }
486         else
487         {
488                 m_bAutomatic = false;
489                 m_filtersText = _T(".");
490         }
491         VariantClear(&ret);
492
493         // get optional property PluginIsAutomatic
494         if (SearchScriptForDefinedProperties(lpDispatch, L"PluginIsAutomatic"))
495         {
496                 h = ::invokeW(lpDispatch, &ret, L"PluginIsAutomatic", opGet[0], NULL);
497                 if (FAILED(h) || ret.vt != VT_BOOL)
498                 {
499                         scinfo.Log(_T("Plugin had PluginIsAutomatic property, but error getting its value"));
500                         return -80; // error (Plugin had PluginIsAutomatic property, but error getting its value)
501                 }
502                 m_bAutomatic = !!ret.boolVal;
503         }
504         else
505         {
506                 if (hasPluginFileFilters)
507                 {
508                         scinfo.Log(_T("Plugin had PluginFileFilters property, but lacked PluginIsAutomatic property"));
509                         // PluginIsAutomatic property is mandatory for Plugins with PluginFileFilters property
510                         return -90;
511                 }
512                 // default to false when Plugin doesn't have property
513                 m_bAutomatic = false;
514         }
515         VariantClear(&ret);
516
517         LoadFilterString();
518
519         // keep the filename
520         m_name = paths_FindFileName(scriptletFilepath);
521
522         // Clear the autorelease holder
523         drv.p = NULL;
524
525         m_lpDispatch = lpDispatch;
526
527         m_filepath = scriptletFilepath;
528
529         return 1;
530 }
531
532 static void ReportPluginLoadFailure(const String & scriptletFilepath, const wchar_t *transformationEvent)
533 {
534         String sEvent = ucr::toTString(transformationEvent);
535         AppErrorMessageBox(string_format(_T("Exception loading plugin for event: %s\r\n%s"), sEvent.c_str(), scriptletFilepath.c_str()));
536 }
537
538 /**
539  * @brief Guard call to LoadPlugin with Windows SEH to trap GPFs
540  *
541  * @return same as LoadPlugin (1=has event, 0=doesn't have event, errors are negative)
542  */
543 static int LoadPluginWrapper(PluginInfo & plugin, const String & scriptletFilepath, const wchar_t *transformationEvent)
544 {
545         SE_Handler seh;
546         try
547         {
548                 return plugin.LoadPlugin(scriptletFilepath, transformationEvent);
549         }
550         catch (SE_Exception&)
551         {
552                 ReportPluginLoadFailure(scriptletFilepath, transformationEvent);
553         }
554         return false;
555 }
556
557 /**
558  * @brief Return list of all candidate plugins in module path
559  *
560  * Computes list only the first time, and caches it.
561  * Lock the plugins *.sct (.ocx and .dll are locked when the interface is created)
562  */
563 static vector<String>& LoadTheScriptletList()
564 {
565         FastMutex::ScopedLock lock(scriptletsSem);
566         if (!scriptletsLoaded)
567         {
568                 String path = paths_ConcatPath(env_GetProgPath(), _T("MergePlugins"));
569
570                 if (IsWindowsScriptThere())
571                         GetScriptletsAt(path, _T(".sct"), theScriptletList );           // VBS/JVS scriptlet
572                 else
573                         LogErrorString(_T("\n  .sct plugins disabled (Windows Script Host not found)"));
574                 GetScriptletsAt(path, _T(".ocx"), theScriptletList );           // VB COM object
575                 GetScriptletsAt(path, _T(".dll"), theScriptletList );           // VC++ COM object
576                 scriptletsLoaded = true;
577
578                 // lock the *.sct to avoid them being deleted/moved away
579                 int i;
580                 for (i = 0 ; i < theScriptletList.size() ; i++)
581                 {
582                         String scriptlet = theScriptletList.at(i);
583                         if (scriptlet.length() > 4 && string_compare_nocase(scriptlet.substr(scriptlet.length() - 4), _T(".sct")) != 0)
584                         {
585                                 // don't need to lock this file
586                                 theScriptletHandleList.push_back(NULL);
587                                 continue;
588                         }
589
590                         HANDLE hFile;
591                         hFile=CreateFile(scriptlet.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
592                                 0, NULL);
593                         if (hFile == INVALID_HANDLE_VALUE)
594                         {
595                                 theScriptletList.erase(theScriptletList.begin() + i);
596                                 i --;
597                         }
598                         else
599                         {
600                                 theScriptletHandleList.push_back(hFile);
601                         }
602                 }
603         }
604         return theScriptletList;
605 }
606 /**
607  * @brief Delete the scriptlet list and delete locks to *.sct
608  *
609  * Allow to load it again
610  */
611 static void UnloadTheScriptletList()
612 {
613         FastMutex::ScopedLock lock(scriptletsSem);
614         if (scriptletsLoaded)
615         {
616                 int i;
617                 for (i = 0 ; i < theScriptletHandleList.size() ; i++)
618                 {
619                         HANDLE hFile = theScriptletHandleList.at(i);
620                         if (hFile != 0)
621                                 CloseHandle(hFile);
622                 }
623
624                 theScriptletHandleList.clear();
625                 theScriptletList.clear();
626                 scriptletsLoaded = false;
627         }
628 }
629
630 /**
631  * @brief Remove a candidate plugin from the cache
632  */
633 static void RemoveScriptletCandidate(const String &scriptletFilepath)
634 {
635         for (int i=0; i<theScriptletList.size(); ++i)
636         {
637                 if (scriptletFilepath == theScriptletList[i])
638                 {
639                         HANDLE hFile = theScriptletHandleList.at(i);
640                         if (hFile != 0)
641                                 CloseHandle(hFile);
642
643                         theScriptletHandleList.erase(theScriptletHandleList.begin() + i);
644                         theScriptletList.erase(theScriptletList.begin() + i);
645                         return;
646                 }
647         }
648 }
649
650 /** 
651  * @brief Get available scriptlets for an event
652  *
653  * @return Returns an array of valid LPDISPATCH
654  */
655 static PluginArray * GetAvailableScripts( const wchar_t *transformationEvent, bool getScriptletsToo ) 
656 {
657         vector<String>& scriptlets = LoadTheScriptletList();
658
659         PluginArray * pPlugins = new PluginArray;
660
661         int i;
662         std::list<String> badScriptlets;
663         for (i = 0 ; i < scriptlets.size() ; i++)
664         {
665                 // Note all the info about the plugin
666                 PluginInfoPtr plugin(new PluginInfo);
667
668                 String scriptletFilepath = scriptlets.at(i);
669                 int rtn = LoadPluginWrapper(*plugin.get(), scriptletFilepath, transformationEvent);
670                 if (rtn == 1)
671                 {
672                         // Plugin has this event
673                         pPlugins->push_back(plugin);
674                 }
675                 else if (rtn < 0)
676                 {
677                         // Plugin is bad
678                         badScriptlets.push_back(scriptletFilepath);
679                 }
680         }
681
682         // Remove any bad plugins from the cache
683         // This might be a good time to see if the user wants to abort or continue
684         while (!badScriptlets.empty())
685         {
686                 RemoveScriptletCandidate(badScriptlets.front());
687                 badScriptlets.pop_front();
688         }
689
690         return pPlugins;
691 }
692
693 static void FreeAllScripts(PluginArrayPtr& pArray) 
694 {
695         pArray->clear();
696         pArray.reset();
697 }
698
699 ////////////////////////////////////////////////////////////////////////////////////
700 // class CScriptsOfThread : cache the interfaces during the thread life
701
702 CScriptsOfThread::CScriptsOfThread()
703 {
704         // count number of events
705         int i;
706         for (i = 0 ;  ; i ++)
707                 if (TransformationCategories[i] == NULL)
708                         break;
709         nTransformationEvents = i;
710
711         // initialize the thread data
712         m_nThreadId = GetCurrentThreadId();
713         m_nLocks = 0;
714         // initialize the plugins pointers
715         typedef PluginArray * LPPluginArray;
716         m_aPluginsByEvent.resize(nTransformationEvents);
717         // CoInitialize the thread, keep the returned value for the destructor 
718         hrInitialize = CoInitialize(NULL);
719         assert(hrInitialize == S_OK || hrInitialize == S_FALSE);
720 }
721
722 CScriptsOfThread::~CScriptsOfThread()
723 {
724         FreeAllScripts();
725
726         if (hrInitialize == S_OK || hrInitialize == S_FALSE)
727                 CoUninitialize();
728 }
729
730 bool CScriptsOfThread::bInMainThread()
731 {
732         return (CAllThreadsScripts::bInMainThread(this));
733 }
734
735 PluginArray * CScriptsOfThread::GetAvailableScripts(const wchar_t *transformationEvent)
736 {
737         int i;
738         for (i = 0 ; i < nTransformationEvents ; i ++)
739                 if (wcscmp(transformationEvent, TransformationCategories[i]) == 0)
740                 {
741                         if (m_aPluginsByEvent[i] == NULL)
742                                 m_aPluginsByEvent[i].reset(::GetAvailableScripts(transformationEvent, bInMainThread()));
743                         return m_aPluginsByEvent[i].get();
744                 }
745         // return a pointer to an empty list
746         static PluginArray noPlugin;
747         return &noPlugin;
748 }
749
750
751
752 void CScriptsOfThread::FreeAllScripts()
753 {
754         // release all the scripts of the thread
755         int i;
756         for (i = 0 ; i < nTransformationEvents ; i++)
757                 if (m_aPluginsByEvent[i])
758                         ::FreeAllScripts(m_aPluginsByEvent[i]);
759
760         // force to reload the scriptlet list
761         UnloadTheScriptletList();
762 }
763
764 void CScriptsOfThread::FreeScriptsForEvent(const wchar_t *transformationEvent)
765 {
766         int i;
767         for (i = 0 ; i < nTransformationEvents ; i ++)
768                 if (wcscmp(transformationEvent, TransformationCategories[i]) == 0)
769                 {
770                         if (m_aPluginsByEvent[i])
771                                 ::FreeAllScripts(m_aPluginsByEvent[i]);
772                         return;
773                 }
774 }
775
776 PluginInfo *CScriptsOfThread::GetAutomaticPluginByFilter(const wchar_t *transformationEvent, const String& filteredText)
777 {
778         PluginArray * piFileScriptArray = GetAvailableScripts(transformationEvent);
779         for (int step = 0 ; step < piFileScriptArray->size() ; step ++)
780         {
781                 const PluginInfoPtr & plugin = piFileScriptArray->at(step);
782                 if (plugin->m_bAutomatic == false)
783                         continue;
784                 if (plugin->TestAgainstRegList(filteredText) == false)
785                         continue;
786                 return plugin.get();
787         }
788         return NULL;
789 }
790
791 PluginInfo * CScriptsOfThread::GetPluginByName(const wchar_t *transformationEvent, const String& name)
792 {
793         int i;
794         for (i = 0 ; i < nTransformationEvents ; i ++)
795                 if (wcscmp(transformationEvent, TransformationCategories[i]) == 0)
796                 {
797                         if (m_aPluginsByEvent[i] == NULL)
798                                 m_aPluginsByEvent[i].reset(::GetAvailableScripts(transformationEvent, bInMainThread()));
799
800                         for (int j = 0 ; j <  m_aPluginsByEvent[i]->size() ; j++)
801                                 if (m_aPluginsByEvent[i]->at(j)->m_name == name)
802                                         return m_aPluginsByEvent[i]->at(j).get();
803                 }
804         return NULL;
805 }
806
807 PluginInfo *  CScriptsOfThread::GetPluginInfo(LPDISPATCH piScript)
808 {
809         int i, j;
810         for (i = 0 ; i < nTransformationEvents ; i ++) 
811         {
812                 if (m_aPluginsByEvent[i] == NULL)
813                         continue;
814                 const PluginArrayPtr& pArray = m_aPluginsByEvent[i];
815                 for (j = 0 ; j < pArray->size() ; j++)
816                         if ((*pArray)[j]->m_lpDispatch == piScript)
817                                 return (*pArray)[j].get();
818         }
819
820         return NULL;
821 }
822
823 ////////////////////////////////////////////////////////////////////////////////////
824 // class CAllThreadsScripts : array of CScriptsOfThread, one per active thread
825
826 std::vector<CScriptsOfThread *> CAllThreadsScripts::m_aAvailableThreads;
827 FastMutex m_aAvailableThreadsLock;
828
829 void CAllThreadsScripts::Add(CScriptsOfThread * scripts)
830 {
831         FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
832         // add the thread in the array
833
834         // register in the array
835         m_aAvailableThreads.push_back(scripts);
836 }
837
838 void CAllThreadsScripts::Remove(CScriptsOfThread * scripts)
839 {
840         FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
841         // unregister from the list
842         std::vector<CScriptsOfThread *>::iterator it;
843         for (it =  m_aAvailableThreads.begin(); it != m_aAvailableThreads.end(); ++it)
844                 if ((*it) == scripts)
845                 {
846                         m_aAvailableThreads.erase(it);
847                         break;
848                 }
849 }
850
851 CScriptsOfThread * CAllThreadsScripts::GetActiveSet()
852 {
853         FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
854         unsigned long nThreadId = GetCurrentThreadId();
855         int i;
856         for (i = 0 ; i < m_aAvailableThreads.size() ; i++)
857                 if (m_aAvailableThreads[i] && m_aAvailableThreads[i]->m_nThreadId == nThreadId)
858                         return m_aAvailableThreads[i];
859         assert(0);
860         return NULL;
861 }
862 CScriptsOfThread * CAllThreadsScripts::GetActiveSetNoAssert()
863 {
864         FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
865         unsigned long nThreadId = GetCurrentThreadId();
866         int i;
867         for (i = 0 ; i < m_aAvailableThreads.size() ; i++)
868                 if (m_aAvailableThreads[i] && m_aAvailableThreads[i]->m_nThreadId == nThreadId)
869                         return m_aAvailableThreads[i];
870         return NULL;
871 }
872
873 bool CAllThreadsScripts::bInMainThread(CScriptsOfThread * scripts)
874 {
875         FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
876         return (scripts == m_aAvailableThreads[0]);
877 }
878
879 ////////////////////////////////////////////////////////////////////////////////////
880 // class CAssureScriptsForThread : control creation/destruction of CScriptsOfThread
881
882 CAssureScriptsForThread::CAssureScriptsForThread()
883 {
884         CScriptsOfThread * scripts = CAllThreadsScripts::GetActiveSetNoAssert();
885         if (scripts == NULL)
886         {
887                 scripts = new CScriptsOfThread;
888                 // insert the script in the repository
889                 CAllThreadsScripts::Add(scripts);
890         }
891         scripts->Lock();
892 }
893 CAssureScriptsForThread::~CAssureScriptsForThread()
894 {
895         CScriptsOfThread * scripts = CAllThreadsScripts::GetActiveSetNoAssert();
896         if (scripts == NULL)
897                 return;
898         if (scripts->Unlock() == true)
899         {
900                 CAllThreadsScripts::Remove(scripts);
901                 delete scripts;
902         }
903 }
904
905 ////////////////////////////////////////////////////////////////////////////////
906 // reallocation, take care of flag bWriteable
907
908 static void reallocBuffer(LPSTR & pszBuf, UINT & nOldSize, UINT nSize, bool bWriteable)
909 {
910         if (!bWriteable)
911                 // alloc a new buffer
912                 pszBuf = (LPSTR) malloc(nSize);
913         else if (nSize > nOldSize) 
914         {
915                 // free the previous buffer, alloc a new one (so we don't copy the old values)
916                 free(pszBuf);
917                 pszBuf = (LPSTR) malloc(nSize);
918         }
919         else
920                 // just truncate the buffer
921                 pszBuf = (LPSTR) realloc(pszBuf, nSize);
922         nOldSize = nSize;
923 }
924 static void reallocBuffer(LPWSTR & pszBuf, UINT & nOldSize, UINT nSize, bool bWriteable)
925 {
926         if (!bWriteable)
927                 // alloc a new buffer
928                 pszBuf = (LPWSTR) malloc(nSize*sizeof(WCHAR));
929         else if (nSize > nOldSize) 
930         {
931                 // free the previous buffer, alloc a new one (so we don't copy the old values)
932                 free(pszBuf);
933                 pszBuf = (LPWSTR) malloc(nSize*sizeof(WCHAR));
934         }
935         else
936                 // just truncate the buffer
937                 pszBuf = (LPWSTR) realloc(pszBuf, nSize*sizeof(WCHAR));
938         nOldSize = nSize;
939 }
940
941
942 ////////////////////////////////////////////////////////////////////////////////
943 // wrap invokes with error handlers
944
945 /**
946  * @brief Display a message box with the plugin name and the error message
947  *
948  * @note Use MessageBox instead of AfxMessageBox so we can set the caption.
949  * VB/VBS plugins has an internal error handler, and a message box with caption,
950  * and we try to reproduce it for other plugins.
951  */
952 static void ShowPluginErrorMessage(IDispatch *piScript, LPTSTR description)
953 {
954         PluginInfo * pInfo = CAllThreadsScripts::GetActiveSet()->GetPluginInfo(piScript);
955         assert(pInfo != NULL);
956         assert(description != NULL);    
957         AppErrorMessageBox(string_format(_T("%s: %s"), pInfo->m_name.c_str(), description));
958 }
959
960 /**
961  * @brief safe invoke helper (by ordinal)
962  *
963  * @note Free all variants passed to it (except ByRef ones) 
964  */
965 static HRESULT safeInvokeA(LPDISPATCH pi, VARIANT *ret, DISPID id, LPCCH op, ...)
966 {
967         HRESULT h;
968         SE_Handler seh;
969         TCHAR errorText[500];
970         bool bExceptionCatched = false; 
971 #ifdef WIN64
972         int nargs = LOBYTE((UINT_PTR)op);
973         vector<VARIANT> args(nargs);
974         va_list list;
975         va_start(list, op);
976         for (vector<VARIANT>::iterator it = args.begin(); it != args.end(); ++it)
977                 *it = va_arg(list, VARIANT);
978         va_end(list);
979 #endif
980
981         try 
982         {
983 #ifdef WIN64
984                 h = invokeA(pi, ret, id, op, nargs == 0 ? NULL : &args[0]);
985 #else
986                 h = invokeA(pi, ret, id, op, (VARIANT *)(&op + 1));
987 #endif
988         }
989         catch(SE_Exception& e) 
990         {
991                 // structured exception are catched here thanks to class SE_Exception
992                 if (!(e.GetErrorMessage(errorText, 500, NULL)))
993                         // don't localize this as we do not localize the known exceptions
994                         _tcscpy(errorText, _T("Unknown CException"));
995                 bExceptionCatched = true;
996         }
997         catch(...) 
998         {
999                 // don't localize this as we do not localize the known exceptions
1000                 _tcscpy(errorText, _T("Unknown C++ exception"));
1001                 bExceptionCatched = true;
1002         }
1003
1004         if (bExceptionCatched)
1005         {
1006                 ShowPluginErrorMessage(pi, errorText);
1007                 // set h to FAILED
1008                 h = E_FAIL;
1009         }
1010
1011         return h;
1012 }
1013 /**
1014  * @brief safe invoke helper (by function name)
1015  *
1016  * @note Free all variants passed to it (except ByRef ones) 
1017  */
1018 static HRESULT safeInvokeW(LPDISPATCH pi, VARIANT *ret, LPCOLESTR silent, LPCCH op, ...)
1019 {
1020         HRESULT h;
1021         SE_Handler seh;
1022         TCHAR errorText[500];
1023         bool bExceptionCatched = false;
1024 #ifdef WIN64
1025         int nargs = LOBYTE((UINT_PTR)op);
1026         vector<VARIANT> args(nargs);
1027         va_list list;
1028         va_start(list, op);
1029         for (vector<VARIANT>::iterator it = args.begin(); it != args.end(); ++it)
1030                 *it = va_arg(list, VARIANT);
1031         va_end(list);
1032 #endif
1033         
1034         try 
1035         {
1036 #ifdef WIN64
1037                 h = invokeW(pi, ret, silent, op, nargs == 0 ? NULL : &args[0]);
1038 #else
1039                 h = invokeW(pi, ret, silent, op, (VARIANT *)(&op + 1));
1040 #endif
1041         }
1042         catch(SE_Exception& e) 
1043         {
1044                 // structured exception are catched here thanks to class SE_Exception
1045                 if (!(e.GetErrorMessage(errorText, 500, NULL)))
1046                         // don't localize this as we do not localize the known exceptions
1047                         _tcscpy(errorText, _T("Unknown CException"));
1048                 bExceptionCatched = true;
1049         }
1050         catch(...) 
1051         {
1052                 // don't localize this as we do not localize the known exceptions
1053                 _tcscpy(errorText, _T("Unknown C++ exception"));
1054                 bExceptionCatched = true;
1055         }
1056
1057         if (bExceptionCatched)
1058         {
1059                 ShowPluginErrorMessage(pi, errorText);
1060                 // set h to FAILED
1061                 h = E_FAIL;
1062         }
1063
1064         return h;
1065 }
1066
1067 ////////////////////////////////////////////////////////////////////////////////
1068 // invoke for plugins
1069
1070 /*
1071  * ----- about VariantClear -----
1072  * VariantClear is done in safeInvokeW/safeInvokeA except for :
1073  * - the returned value
1074  * - BYREF arguments
1075  * note : BYREF arguments don't need VariantClear if the refered value
1076  * is deleted in the function destructor. Example :
1077  * {
1078  *   int Value;
1079  *   VARIANT vValue;
1080  *   vValue.plVal = &vValue;
1081  *   ...
1082  */
1083
1084 bool InvokePrediffBuffer(BSTR & bstrBuf, int & nChanged, IDispatch *piScript)
1085 {
1086         UINT nBufSize = SysStringLen(bstrBuf);
1087
1088         // prepare the arguments
1089         // argument text buffer by reference
1090         VARIANT vpbstrBuf;
1091         vpbstrBuf.vt = VT_BYREF | VT_BSTR;
1092         vpbstrBuf.pbstrVal = &bstrBuf;
1093         // argument buffer size by reference
1094         VARIANT vpiSize;
1095         vpiSize.vt = VT_BYREF | VT_I4;
1096         vpiSize.plVal = (long*) &nBufSize;
1097         // argument flag changed (VT_BOOL is short)
1098         VARIANT_BOOL changed = 0;
1099         VARIANT vpboolChanged;
1100         vpboolChanged.vt = VT_BYREF | VT_BOOL;
1101         vpboolChanged.pboolVal = &changed;
1102         // argument return value (VT_BOOL is short)
1103         VARIANT vboolHandled;
1104         vboolHandled.vt = VT_BOOL;
1105         vboolHandled.boolVal = false;
1106
1107         // invoke method by name, reverse order for arguments
1108         // for VC, if the invoked function changes the buffer address, 
1109         // it must free the old buffer with SysFreeString
1110         // VB does it automatically
1111         // VARIANT_BOOL DiffingPreprocessW(BSTR * buffer, UINT * nSize, VARIANT_BOOL * bChanged)
1112         HRESULT h = ::safeInvokeW(piScript,     &vboolHandled, L"PrediffBufferW", opFxn[3], 
1113                             vpboolChanged, vpiSize, vpbstrBuf);
1114         bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1115         if (bSuccess && changed)
1116         {
1117                 // remove trailing charracters in the rare case that bstrBuf was not resized 
1118                 if (SysStringLen(bstrBuf) != nBufSize)
1119                         bSuccess = !FAILED(SysReAllocStringLen(&bstrBuf, bstrBuf, nBufSize));
1120                 if (bSuccess)
1121                         nChanged ++;
1122         }
1123
1124         // clear the returned variant
1125         VariantClear(&vboolHandled);
1126
1127         return  (bSuccess);
1128 }
1129
1130 bool InvokeUnpackBuffer(VARIANT & array, int & nChanged, IDispatch *piScript, int & subcode)
1131 {
1132         LONG nArraySize;
1133         SafeArrayGetUBound(array.parray, 0, &nArraySize);
1134         ++nArraySize;
1135
1136         // prepare the arguments
1137         // argument file buffer
1138         VARIANT vparrayBuf;
1139         vparrayBuf.vt = VT_BYREF | VT_ARRAY | VT_UI1;
1140         vparrayBuf.pparray = &(array.parray);
1141         // argument buffer size by reference
1142         VARIANT vpiSize;
1143         vpiSize.vt = VT_BYREF | VT_I4;
1144         vpiSize.plVal = (long*) &nArraySize;
1145         // argument flag changed (VT_BOOL is short)
1146         VARIANT_BOOL changed = 0;
1147         VARIANT vpboolChanged;
1148         vpboolChanged.vt = VT_BYREF | VT_BOOL;
1149         vpboolChanged.pboolVal = &changed;
1150         // argument subcode by reference
1151         VARIANT viSubcode;
1152         viSubcode.vt = VT_BYREF | VT_I4;
1153         viSubcode.plVal = (long*) &subcode;
1154         // argument return value (VT_BOOL is short)
1155         VARIANT vboolHandled;
1156         vboolHandled.vt = VT_BOOL;
1157         vboolHandled.boolVal = false;
1158
1159         // invoke method by name, reverse order for arguments
1160         // VARIANT_BOOL UnpackBufferA(SAFEARRAY * array, UINT * nSize, VARIANT_BOOL * bChanged, UINT * subcode)
1161         HRESULT h = ::safeInvokeW(piScript,     &vboolHandled, L"UnpackBufferA", opFxn[4], 
1162                             viSubcode, vpboolChanged, vpiSize, vparrayBuf);
1163         bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1164         if (bSuccess && changed)
1165         {
1166                 // remove trailing charracters if the array was not resized
1167                 LONG nNewArraySize;
1168                 SafeArrayGetUBound(array.parray, 0, &nNewArraySize);
1169                 ++nNewArraySize;
1170
1171                 if (nNewArraySize != nArraySize)
1172                 {
1173                         SAFEARRAYBOUND sab = {nArraySize, 0};
1174                         SafeArrayRedim(array.parray, &sab);
1175                 }
1176                 nChanged ++;
1177         }
1178
1179         // clear the returned variant
1180         VariantClear(&vboolHandled);
1181
1182         return  (bSuccess);
1183 }
1184
1185 bool InvokePackBuffer(VARIANT & array, int & nChanged, IDispatch *piScript, int subcode)
1186 {
1187         LONG nArraySize;
1188         SafeArrayGetUBound(array.parray, 0, &nArraySize);
1189         ++nArraySize;
1190
1191         // prepare the arguments
1192         // argument file buffer
1193         VARIANT vparrayBuf;
1194         vparrayBuf.vt = VT_BYREF | VT_ARRAY | VT_UI1;
1195         vparrayBuf.pparray = &(array.parray);
1196         // argument buffer size by reference
1197         VARIANT vpiSize;
1198         vpiSize.vt = VT_BYREF | VT_I4;
1199         vpiSize.plVal = (long*) &nArraySize;
1200         // argument flag changed (VT_BOOL is short)
1201         VARIANT_BOOL changed = 0;
1202         VARIANT vpboolChanged;
1203         vpboolChanged.vt = VT_BYREF | VT_BOOL;
1204         vpboolChanged.pboolVal = &changed;
1205         // argument subcode
1206         VARIANT viSubcode;
1207         viSubcode.vt = VT_I4;
1208         viSubcode.lVal = subcode;
1209         // argument return value (VT_BOOL is short)
1210         VARIANT vboolHandled;
1211         vboolHandled.vt = VT_BOOL;
1212         vboolHandled.boolVal = false;
1213
1214         // invoke method by name, reverse order for arguments
1215         // VARIANT_BOOL PackBufferA(SAFEARRAY * array, UINT * nSize, VARIANT_BOOL * bChanged, UINT subcode)
1216         HRESULT h = ::safeInvokeW(piScript,     &vboolHandled, L"PackBufferA", opFxn[4], 
1217                             viSubcode, vpboolChanged, vpiSize, vparrayBuf);
1218         bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1219         if (bSuccess && changed)
1220         {
1221                 // remove trailing charracters if the array was not resized
1222                 LONG nNewArraySize;
1223                 SafeArrayGetUBound(array.parray, 0, &nNewArraySize);
1224                 ++nNewArraySize;
1225
1226                 if (nNewArraySize != nArraySize)
1227                 {
1228                         SAFEARRAYBOUND sab = {nArraySize, 0};
1229                         SafeArrayRedim(array.parray, &sab);
1230                 }
1231         }
1232
1233         // clear the returned variant
1234         VariantClear(&vboolHandled);
1235
1236         return  (bSuccess);
1237 }
1238
1239
1240 static bool unpack(const wchar_t *method, const String& source, const String& dest, int & nChanged, IDispatch *piScript, int & subCode)
1241 {
1242         // argument text  
1243         VARIANT vbstrSrc;
1244         vbstrSrc.vt = VT_BSTR;
1245         vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(source).c_str());
1246         // argument transformed text 
1247         VARIANT vbstrDst;
1248         vbstrDst.vt = VT_BSTR;
1249         vbstrDst.bstrVal = SysAllocString(ucr::toUTF16(dest).c_str());
1250         // argument subcode by reference
1251         VARIANT vpiSubcode;
1252         vpiSubcode.vt = VT_BYREF | VT_I4;
1253         vpiSubcode.plVal = (long*) &subCode;
1254         // argument flag changed (VT_BOOL is short)
1255         VARIANT_BOOL changed = 0;
1256         VARIANT vpboolChanged;
1257         vpboolChanged.vt = VT_BYREF | VT_BOOL;
1258         vpboolChanged.pboolVal = &changed;
1259         // argument return value (VT_BOOL is short)
1260         VARIANT vboolHandled;
1261         vboolHandled.vt = VT_BOOL;
1262         vboolHandled.boolVal = false;
1263
1264         // invoke method by name, reverse order for arguments
1265         // VARIANT_BOOL UnpackFile(BSTR fileSrc, BSTR fileDst, VARIANT_BOOL * bChanged, INT * bSubcode)
1266         HRESULT h = ::safeInvokeW(piScript,     &vboolHandled, method, opFxn[4], 
1267                                   vpiSubcode, vpboolChanged, vbstrDst, vbstrSrc);
1268         bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1269         if (bSuccess && changed)
1270                 nChanged ++;
1271
1272         // clear the returned variant
1273         VariantClear(&vboolHandled);
1274
1275         return  (bSuccess);
1276 }
1277
1278 static bool pack(const wchar_t *method, const String& source, const String& dest, int & nChanged, IDispatch *piScript, int & subCode)
1279 {
1280         // argument text  
1281         VARIANT vbstrSrc;
1282         vbstrSrc.vt = VT_BSTR;
1283         vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(source).c_str());
1284         // argument transformed text 
1285         VARIANT vbstrDst;
1286         vbstrDst.vt = VT_BSTR;
1287         vbstrDst.bstrVal = SysAllocString(ucr::toUTF16(dest).c_str());
1288         // argument subcode
1289         VARIANT viSubcode;
1290         viSubcode.vt = VT_I4;
1291         viSubcode.lVal = subCode;
1292         // argument flag changed (VT_BOOL is short)
1293         VARIANT_BOOL changed = 0;
1294         VARIANT vpboolChanged;
1295         vpboolChanged.vt = VT_BYREF | VT_BOOL;
1296         vpboolChanged.pboolVal = &changed;
1297         // argument return value (VT_BOOL is short)
1298         VARIANT vboolHandled;
1299         vboolHandled.vt = VT_BOOL;
1300         vboolHandled.boolVal = false;
1301
1302         // invoke method by name, reverse order for arguments
1303         // VARIANT_BOOL PackFile(BSTR fileSrc, BSTR fileDst, VARIANT_BOOL * bChanged, INT bSubcode)
1304         HRESULT h = ::safeInvokeW(piScript,     &vboolHandled, method, opFxn[4], 
1305                                   viSubcode, vpboolChanged, vbstrDst, vbstrSrc);
1306         bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1307         if (bSuccess && changed)
1308                 nChanged ++;
1309
1310         // clear the returned variant
1311         VariantClear(&vboolHandled);
1312
1313         return  (bSuccess);
1314 }
1315
1316 bool InvokeUnpackFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript, int & subCode)
1317 {
1318         return unpack(L"UnpackFile", fileSource, fileDest, nChanged, piScript, subCode);
1319 }
1320
1321 bool InvokePackFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript, int subCode)
1322 {
1323         return pack(L"PackFile", fileSource, fileDest, nChanged, piScript, subCode);
1324 }
1325
1326 bool InvokeUnpackFolder(const String& fileSource, const String& folderDest, int & nChanged, IDispatch *piScript, int & subCode)
1327 {
1328         return unpack(L"UnpackFolder", fileSource, folderDest, nChanged, piScript, subCode);
1329 }
1330
1331 bool InvokePackFolder(const String& folderSource, const String& fileDest, int & nChanged, IDispatch *piScript, int subCode)
1332 {
1333         return pack(L"PackFolder", folderSource, fileDest, nChanged, piScript, subCode);
1334 }
1335
1336 bool InvokePrediffFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript)
1337 {
1338         // argument text  
1339         VARIANT vbstrSrc;
1340         vbstrSrc.vt = VT_BSTR;
1341         vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(fileSource).c_str());
1342         // argument transformed text 
1343         VARIANT vbstrDst;
1344         vbstrDst.vt = VT_BSTR;
1345         vbstrDst.bstrVal = SysAllocString(ucr::toUTF16(fileDest).c_str());
1346         // argument flag changed (VT_BOOL is short)
1347         VARIANT_BOOL changed = 0;
1348         VARIANT vpboolChanged;
1349         vpboolChanged.vt = VT_BYREF | VT_BOOL;
1350         vpboolChanged.pboolVal = &changed;
1351         // argument return value (VT_BOOL is short)
1352         VARIANT vboolHandled;
1353         vboolHandled.vt = VT_BOOL;
1354         vboolHandled.boolVal = false;
1355
1356         // invoke method by name, reverse order for arguments
1357         // VARIANT_BOOL PrediffFile(BSTR fileSrc, BSTR fileDst, VARIANT_BOOL * bChanged)
1358         HRESULT h = ::safeInvokeW(piScript,     &vboolHandled, L"PrediffFile", opFxn[3], 
1359                             vpboolChanged, vbstrDst, vbstrSrc);
1360         bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1361         if (bSuccess && changed)
1362                 nChanged ++;
1363
1364         // clear the returned variant
1365         VariantClear(&vboolHandled);
1366
1367         return  (bSuccess);
1368 }
1369
1370
1371 bool InvokeTransformText(String & text, int & changed, IDispatch *piScript, int fncId)
1372 {
1373         // argument text  
1374         VARIANT pvPszBuf;
1375         pvPszBuf.vt = VT_BSTR;
1376         pvPszBuf.bstrVal = SysAllocString(ucr::toUTF16(text).c_str());
1377         // argument transformed text 
1378         VARIANT vTransformed;
1379         vTransformed.vt = VT_BSTR;
1380         vTransformed.bstrVal = NULL;
1381
1382         // invoke method by ordinal
1383         // BSTR customFunction(BSTR text)
1384         HRESULT h = ::safeInvokeA(piScript, &vTransformed, fncId, opFxn[1], pvPszBuf);
1385
1386         if (! FAILED(h))
1387         {
1388                 text = ucr::toTString(vTransformed.bstrVal);
1389                 changed = true;
1390         }
1391         else
1392                 changed = false;
1393
1394         // clear the returned variant
1395         VariantClear(&vTransformed);
1396
1397         return (! FAILED(h));
1398 }
1399
1400 bool InvokeIsFolder(const String& path, IDispatch *piScript)
1401 {
1402         // argument text  
1403         VARIANT vbstrPath;
1404         vbstrPath.vt = VT_BSTR;
1405         vbstrPath.bstrVal = SysAllocString(ucr::toUTF16(path).c_str());
1406
1407         VARIANT vboolHandled;
1408         vboolHandled.vt = VT_BOOL;
1409         vboolHandled.boolVal = false;
1410
1411         // invoke method by name, reverse order for arguments
1412         // VARIANT_BOOL ShowSettingsDialog()
1413         HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"IsFolder", opFxn[1], vbstrPath);
1414         bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1415
1416         // clear the returned variant
1417         VariantClear(&vboolHandled);
1418         VariantClear(&vbstrPath);
1419
1420         return (bSuccess);
1421 }
1422
1423 bool InvokeShowSettingsDialog(IDispatch *piScript)
1424 {
1425         VARIANT vboolHandled;
1426         vboolHandled.vt = VT_BOOL;
1427         vboolHandled.boolVal = false;
1428
1429         // invoke method by name, reverse order for arguments
1430         // VARIANT_BOOL ShowSettingsDialog()
1431         HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"ShowSettingsDialog", opFxn[0]);
1432         bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1433
1434         // clear the returned variant
1435         VariantClear(&vboolHandled);
1436
1437         return (bSuccess);
1438 }