1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
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.
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.
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.
20 /////////////////////////////////////////////////////////////////////////////
24 * @brief Support for VBS Scriptlets, VB ActiveX DLL, VC++ COM DLL
26 // ID line follows -- this is updated by SVN
27 // $Id: Plugins.cpp 7052 2009-12-22 17:45:22Z kimmov $
30 #define POCO_NO_UNWINDOWS 1
36 #include <Poco/Mutex.h>
37 #include <Poco/ScopedLock.h>
38 #include <Poco/RegularExpression.h>
41 #include "FileTransform.h"
42 #include "FileFilterMgr.h"
45 #include "Exceptions.h"
48 #include "Environment.h"
49 #include "FileFilter.h"
52 using Poco::RegularExpression;
53 using Poco::FastMutex;
54 using Poco::ScopedLock;
56 static vector<String> theScriptletList;
57 /// Need to lock the *.sct so the user can't delete them
58 static vector<HANDLE> theScriptletHandleList;
59 static bool scriptletsLoaded=false;
60 static FastMutex scriptletsSem;
62 template<class T> struct AutoReleaser
64 AutoReleaser(T *ptr) : p(ptr) {}
65 ~AutoReleaser() { if (p) p->Release(); }
69 ////////////////////////////////////////////////////////////////////////////////
71 * @brief Check for the presence of Windows Script
73 * .sct plugins require this optional component
75 bool IsWindowsScriptThere()
78 if (!keyFile.QueryRegMachine(_T("SOFTWARE\\Classes\\scriptletfile\\AutoRegister")))
81 String filename = keyFile.ReadString(_T(""), _T("")).c_str();
86 return (paths_DoesPathExist(filename) == IS_EXISTING_FILE);
89 ////////////////////////////////////////////////////////////////////////////////
90 // scriptlet/activeX support for function names
92 // list the function IDs and names in a script or activeX dll
93 int GetFunctionsFromScript(IDispatch *piDispatch, vector<String>& namesArray, vector<int>& IdArray, INVOKEKIND wantedKind)
99 ITypeInfo *piTypeInfo=0;
100 unsigned iTInfo = 0; // 0 for type information of IDispatch itself
101 LCID lcid=0; // locale for localized method names (ignore if no localized names)
102 if (SUCCEEDED(hr = piDispatch->GetTypeInfo(iTInfo, lcid, &piTypeInfo)))
104 TYPEATTR *pTypeAttr=0;
105 if (SUCCEEDED(hr = piTypeInfo->GetTypeAttr(&pTypeAttr)))
107 // allocate arrays for the returned structures
108 // the names array is NULL terminated
109 namesArray.resize(pTypeAttr->cFuncs+1);
110 IdArray.resize(pTypeAttr->cFuncs+1);
112 UINT iMaxFunc = pTypeAttr->cFuncs - 1;
113 for (UINT iFunc = 0 ; iFunc <= iMaxFunc ; ++iFunc)
115 UINT iFuncDesc = iMaxFunc - iFunc;
117 if (SUCCEEDED(hr = piTypeInfo->GetFuncDesc(iFuncDesc, &pFuncDesc)))
119 // exclude properties
120 // exclude IDispatch inherited methods
121 if (pFuncDesc->invkind & wantedKind && !(pFuncDesc->wFuncFlags & 1))
125 if (SUCCEEDED(hr = piTypeInfo->GetNames(pFuncDesc->memid,
126 &bstrName, 1, &cNames)))
128 IdArray[iValidFunc] = pFuncDesc->memid;
129 namesArray[iValidFunc] = ucr::toTString(bstrName);
132 SysFreeString(bstrName);
134 piTypeInfo->ReleaseFuncDesc(pFuncDesc);
137 piTypeInfo->ReleaseTypeAttr(pTypeAttr);
139 piTypeInfo->Release();
145 int GetMethodsFromScript(IDispatch *piDispatch, vector<String>& namesArray, vector<int> &IdArray)
147 return GetFunctionsFromScript(piDispatch, namesArray, IdArray, INVOKE_FUNC);
149 int GetPropertyGetsFromScript(IDispatch *piDispatch, vector<String>& namesArray, vector<int> &IdArray)
151 return GetFunctionsFromScript(piDispatch, namesArray, IdArray, INVOKE_PROPERTYGET);
155 // search a function name in a scriptlet or activeX dll
156 bool SearchScriptForMethodName(LPDISPATCH piDispatch, const wchar_t *functionName)
160 vector<String> namesArray;
162 int nFnc = GetMethodsFromScript(piDispatch, namesArray, IdArray);
164 String tfuncname = ucr::toTString(functionName);
166 for (iFnc = 0 ; iFnc < nFnc ; iFnc++)
168 if (namesArray[iFnc] == tfuncname)
174 // search a property name (with get interface) in a scriptlet or activeX dll
175 bool SearchScriptForDefinedProperties(IDispatch *piDispatch, const wchar_t *functionName)
179 vector<String> namesArray;
181 int nFnc = GetPropertyGetsFromScript(piDispatch, namesArray, IdArray);
183 String tfuncname = ucr::toTString(functionName);
185 for (iFnc = 0 ; iFnc < nFnc ; iFnc++)
187 if (namesArray[iFnc] == tfuncname)
194 int CountMethodsInScript(LPDISPATCH piDispatch)
196 vector<String> namesArray;
198 int nFnc = GetMethodsFromScript(piDispatch, namesArray, IdArray);
204 * @return ID of the function or -1 if no function with this index
206 int GetMethodIDInScript(LPDISPATCH piDispatch, int methodIndex)
210 vector<String> namesArray;
212 int nFnc = GetMethodsFromScript(piDispatch, namesArray, IdArray);
214 if (methodIndex < nFnc)
216 fncID = IdArray[methodIndex];
226 ////////////////////////////////////////////////////////////////////////////////
227 // find scripts/activeX for an event : each event is assigned to a subdirectory
231 * @brief Get a list of scriptlet file
233 * @return Returns an array of LPSTR
235 static void GetScriptletsAt(const String& sSearchPath, const String& extension, vector<String>& scriptlets )
238 String strFileSpec = paths_ConcatPath(sSearchPath, _T("*") + extension);
239 HANDLE hff = FindFirstFile(strFileSpec.c_str(), &ffi);
241 if ( hff != INVALID_HANDLE_VALUE )
245 if (!(ffi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
247 strFileSpec = paths_ConcatPath(sSearchPath, ffi.cFileName);
248 scriptlets.push_back(strFileSpec);
251 while (FindNextFile(hff, &ffi));
256 void PluginInfo::LoadFilterString()
260 String sLine(m_filtersText);
265 String::size_type pos = sLine.rfind(';');
266 sPiece = sLine.substr(pos+1);
267 if (pos == String::npos)
269 sLine = sLine.substr(0, pos);
273 sPiece = string_makeupper(string_trim_ws_begin(sPiece));
276 std::string regexString = ucr::toUTF8(sPiece);
277 re_opts |= RegularExpression::RE_UTF8;
280 m_filters.push_back(FileFilterElementPtr(new FileFilterElement(regexString, re_opts)));
290 bool PluginInfo::TestAgainstRegList(const String& szTest)
292 if (m_filters.empty() || szTest.empty())
295 String sLine = szTest;
298 while(!sLine.empty())
300 String::size_type pos = sLine.rfind('|');
301 sPiece = sLine.substr(pos+1);
302 if (pos == String::npos)
304 sLine = sLine.substr(0, pos);
307 sPiece = string_makeupper(string_trim_ws_begin(sPiece));
309 if (::TestAgainstRegList(&m_filters, sPiece))
317 * @brief Log technical explanation, in English, of script error
320 ScriptletError(const String & scriptletFilepath, const wchar_t *transformationEvent, const TCHAR *szError)
322 String msg = _T("Plugin scriptlet error <")
325 + ucr::toTString(transformationEvent)
332 * @brief Tiny structure that remembers current scriptlet & event info for calling Log
336 ScriptInfo(const String & scriptletFilepath, const wchar_t *transformationEvent)
337 : m_scriptletFilepath(scriptletFilepath)
338 , m_transformationEvent(transformationEvent)
341 void Log(const TCHAR *szError)
343 ScriptletError(m_scriptletFilepath, m_transformationEvent, szError);
345 const String & m_scriptletFilepath;
346 const wchar_t *m_transformationEvent;
350 * @brief Try to load a plugin
352 * @return 1 if plugin handles this event, 0 if not, negatives for errors
354 int PluginInfo::LoadPlugin(const String & scriptletFilepath, const wchar_t *transformationEvent)
356 // set up object in case we need to log info
357 ScriptInfo scinfo(scriptletFilepath, transformationEvent);
359 // Search for the class "WinMergeScript"
360 LPDISPATCH lpDispatch = CreateDispatchBySource(scriptletFilepath.c_str(), L"WinMergeScript");
363 scinfo.Log(_T("WinMergeScript entry point not found"));
367 // Ensure that interface is released if any bad exit or exception
368 AutoReleaser<IDispatch> drv(lpDispatch);
370 // Is this plugin for this transformationEvent ?
372 // invoke mandatory method get PluginEvent
374 if (!SearchScriptForDefinedProperties(lpDispatch, L"PluginEvent"))
376 scinfo.Log(_T("PluginEvent method missing"));
379 HRESULT h = ::invokeW(lpDispatch, &ret, L"PluginEvent", opGet[0], NULL);
380 if (FAILED(h) || ret.vt != VT_BSTR)
382 scinfo.Log( _T("Error accessing PluginEvent method"));
385 if (wcscmp(ret.bstrVal, transformationEvent) != 0)
387 return 0; // doesn't handle this event
391 // plugins PREDIFF or PACK_UNPACK : functions names are mandatory
392 // Check that the plugin offers the requested functions
393 // set the mode for the events which uses it
395 if (wcscmp(transformationEvent, L"BUFFER_PREDIFF") == 0)
397 bFound &= SearchScriptForMethodName(lpDispatch, L"PrediffBufferW");
399 else if (wcscmp(transformationEvent, L"FILE_PREDIFF") == 0)
401 bFound &= SearchScriptForMethodName(lpDispatch, L"PrediffFile");
403 else if (wcscmp(transformationEvent, L"BUFFER_PACK_UNPACK") == 0)
405 bFound &= SearchScriptForMethodName(lpDispatch, L"UnpackBufferA");
406 bFound &= SearchScriptForMethodName(lpDispatch, L"PackBufferA");
408 else if (wcscmp(transformationEvent, L"FILE_PACK_UNPACK") == 0)
410 bFound &= SearchScriptForMethodName(lpDispatch, L"UnpackFile");
411 bFound &= SearchScriptForMethodName(lpDispatch, L"PackFile");
415 // error (Plugin doesn't support the method as it claimed)
416 scinfo.Log(_T("Plugin doesn't support the method as it claimed"));
420 // plugins EDITOR_SCRIPT : functions names are free
421 // there may be several functions inside one script, count the number of functions
422 if (wcscmp(transformationEvent, L"EDITOR_SCRIPT") == 0)
424 m_nFreeFunctions = CountMethodsInScript(lpDispatch);
425 if (m_nFreeFunctions == 0)
426 // error (Plugin doesn't offer any method, what is this ?)
431 // get optional property PluginDescription
432 if (SearchScriptForDefinedProperties(lpDispatch, L"PluginDescription"))
434 h = ::invokeW(lpDispatch, &ret, L"PluginDescription", opGet[0], NULL);
435 if (FAILED(h) || ret.vt != VT_BSTR)
437 scinfo.Log(_T("Plugin had PluginDescription property, but error getting its value"));
438 return -60; // error (Plugin had PluginDescription property, but error getting its value)
440 m_description = ucr::toTString(ret.bstrVal);
444 // no description, use filename
445 m_description = paths_FindFileName(scriptletFilepath);
449 // get PluginFileFilters
450 bool hasPluginFileFilters = false;
451 if (SearchScriptForDefinedProperties(lpDispatch, L"PluginFileFilters"))
453 h = ::invokeW(lpDispatch, &ret, L"PluginFileFilters", opGet[0], NULL);
454 if (FAILED(h) || ret.vt != VT_BSTR)
456 scinfo.Log(_T("Plugin had PluginFileFilters property, but error getting its value"));
457 return -70; // error (Plugin had PluginFileFilters property, but error getting its value)
459 m_filtersText = ucr::toTString(ret.bstrVal);
460 hasPluginFileFilters = true;
464 m_bAutomatic = false;
465 m_filtersText = _T(".");
469 // get optional property PluginIsAutomatic
470 if (SearchScriptForDefinedProperties(lpDispatch, L"PluginIsAutomatic"))
472 h = ::invokeW(lpDispatch, &ret, L"PluginIsAutomatic", opGet[0], NULL);
473 if (FAILED(h) || ret.vt != VT_BOOL)
475 scinfo.Log(_T("Plugin had PluginIsAutomatic property, but error getting its value"));
476 return -80; // error (Plugin had PluginIsAutomatic property, but error getting its value)
478 m_bAutomatic = !!ret.boolVal;
482 if (hasPluginFileFilters)
484 scinfo.Log(_T("Plugin had PluginFileFilters property, but lacked PluginIsAutomatic property"));
485 // PluginIsAutomatic property is mandatory for Plugins with PluginFileFilters property
488 // default to false when Plugin doesn't have property
489 m_bAutomatic = false;
496 m_name = paths_FindFileName(scriptletFilepath);
498 // Clear the autorelease holder
501 m_lpDispatch = lpDispatch;
503 m_filepath = scriptletFilepath;
508 static void ReportPluginLoadFailure(const String & scriptletFilepath, const wchar_t *transformationEvent)
510 String sEvent = ucr::toTString(transformationEvent);
511 AppErrorMessageBox(string_format(_T("Exception loading plugin for event: %s\r\n%s"), sEvent.c_str(), scriptletFilepath.c_str()));
515 * @brief Guard call to LoadPlugin with Windows SEH to trap GPFs
517 * @return same as LoadPlugin (1=has event, 0=doesn't have event, errors are negative)
519 static int LoadPluginWrapper(PluginInfo & plugin, const String & scriptletFilepath, const wchar_t *transformationEvent)
524 return plugin.LoadPlugin(scriptletFilepath, transformationEvent);
526 catch (SE_Exception&)
528 ReportPluginLoadFailure(scriptletFilepath, transformationEvent);
534 * @brief Return list of all candidate plugins in module path
536 * Computes list only the first time, and caches it.
537 * Lock the plugins *.sct (.ocx and .dll are locked when the interface is created)
539 static vector<String>& LoadTheScriptletList()
541 FastMutex::ScopedLock lock(scriptletsSem);
542 if (!scriptletsLoaded)
544 String path = paths_ConcatPath(env_GetProgPath(), _T("MergePlugins"));
546 if (IsWindowsScriptThere())
547 GetScriptletsAt(path, _T(".sct"), theScriptletList ); // VBS/JVS scriptlet
549 LogErrorString(_T("\n .sct plugins disabled (Windows Script Host not found)"));
550 GetScriptletsAt(path, _T(".ocx"), theScriptletList ); // VB COM object
551 GetScriptletsAt(path, _T(".dll"), theScriptletList ); // VC++ COM object
552 scriptletsLoaded = true;
554 // lock the *.sct to avoid them being deleted/moved away
556 for (i = 0 ; i < theScriptletList.size() ; i++)
558 String scriptlet = theScriptletList.at(i);
559 if (scriptlet.length() > 4 && string_compare_nocase(scriptlet.substr(scriptlet.length() - 4), _T(".sct")) != 0)
561 // don't need to lock this file
562 theScriptletHandleList.push_back(NULL);
567 hFile=CreateFile(scriptlet.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
569 if (hFile == INVALID_HANDLE_VALUE)
571 theScriptletList.erase(theScriptletList.begin() + i);
576 theScriptletHandleList.push_back(hFile);
580 return theScriptletList;
583 * @brief Delete the scriptlet list and delete locks to *.sct
585 * Allow to load it again
587 static void UnloadTheScriptletList()
589 FastMutex::ScopedLock lock(scriptletsSem);
590 if (scriptletsLoaded)
593 for (i = 0 ; i < theScriptletHandleList.size() ; i++)
595 HANDLE hFile = theScriptletHandleList.at(i);
600 theScriptletHandleList.clear();
601 theScriptletList.clear();
602 scriptletsLoaded = false;
607 * @brief Remove a candidate plugin from the cache
609 static void RemoveScriptletCandidate(const String &scriptletFilepath)
611 for (int i=0; i<theScriptletList.size(); ++i)
613 if (scriptletFilepath == theScriptletList[i])
615 HANDLE hFile = theScriptletHandleList.at(i);
619 theScriptletHandleList.erase(theScriptletHandleList.begin() + i);
620 theScriptletList.erase(theScriptletList.begin() + i);
627 * @brief Get available scriptlets for an event
629 * @return Returns an array of valid LPDISPATCH
631 static PluginArray * GetAvailableScripts( const wchar_t *transformationEvent, bool getScriptletsToo )
633 vector<String>& scriptlets = LoadTheScriptletList();
635 PluginArray * pPlugins = new PluginArray;
638 std::list<String> badScriptlets;
639 for (i = 0 ; i < scriptlets.size() ; i++)
641 // Note all the info about the plugin
642 PluginInfoPtr plugin(new PluginInfo);
644 String scriptletFilepath = scriptlets.at(i);
645 int rtn = LoadPluginWrapper(*plugin.get(), scriptletFilepath, transformationEvent);
648 // Plugin has this event
649 pPlugins->push_back(plugin);
654 badScriptlets.push_back(scriptletFilepath);
658 // Remove any bad plugins from the cache
659 // This might be a good time to see if the user wants to abort or continue
660 while (!badScriptlets.empty())
662 RemoveScriptletCandidate(badScriptlets.front());
663 badScriptlets.pop_front();
669 static void FreeAllScripts(PluginArrayPtr& pArray)
675 ////////////////////////////////////////////////////////////////////////////////////
676 // class CScriptsOfThread : cache the interfaces during the thread life
678 CScriptsOfThread::CScriptsOfThread()
680 // count number of events
683 if (TransformationCategories[i] == NULL)
685 nTransformationEvents = i;
687 // initialize the thread data
688 m_nThreadId = GetCurrentThreadId();
690 // initialize the plugins pointers
691 typedef PluginArray * LPPluginArray;
692 m_aPluginsByEvent.resize(nTransformationEvents);
693 // CoInitialize the thread, keep the returned value for the destructor
694 hrInitialize = CoInitialize(NULL);
695 assert(hrInitialize == S_OK || hrInitialize == S_FALSE);
698 CScriptsOfThread::~CScriptsOfThread()
700 if (hrInitialize == S_OK || hrInitialize == S_FALSE)
706 bool CScriptsOfThread::bInMainThread()
708 return (CAllThreadsScripts::bInMainThread(this));
711 PluginArray * CScriptsOfThread::GetAvailableScripts(const wchar_t *transformationEvent)
714 for (i = 0 ; i < nTransformationEvents ; i ++)
715 if (wcscmp(transformationEvent, TransformationCategories[i]) == 0)
717 if (m_aPluginsByEvent[i] == NULL)
718 m_aPluginsByEvent[i].reset(::GetAvailableScripts(transformationEvent, bInMainThread()));
719 return m_aPluginsByEvent[i].get();
721 // return a pointer to an empty list
722 static PluginArray noPlugin;
728 void CScriptsOfThread::FreeAllScripts()
730 // release all the scripts of the thread
732 for (i = 0 ; i < nTransformationEvents ; i++)
733 if (m_aPluginsByEvent[i])
734 ::FreeAllScripts(m_aPluginsByEvent[i]);
736 // force to reload the scriptlet list
737 UnloadTheScriptletList();
740 void CScriptsOfThread::FreeScriptsForEvent(const wchar_t *transformationEvent)
743 for (i = 0 ; i < nTransformationEvents ; i ++)
744 if (wcscmp(transformationEvent, TransformationCategories[i]) == 0)
746 if (m_aPluginsByEvent[i])
747 ::FreeAllScripts(m_aPluginsByEvent[i]);
753 PluginInfo * CScriptsOfThread::GetPluginByName(const wchar_t *transformationEvent, const String& name)
756 for (i = 0 ; i < nTransformationEvents ; i ++)
757 if (wcscmp(transformationEvent, TransformationCategories[i]) == 0)
759 if (m_aPluginsByEvent[i] == NULL)
760 m_aPluginsByEvent[i].reset(::GetAvailableScripts(transformationEvent, bInMainThread()));
762 for (int j = 0 ; j < m_aPluginsByEvent[i]->size() ; j++)
763 if (m_aPluginsByEvent[i]->at(j)->m_name == name)
764 return m_aPluginsByEvent[i]->at(j).get();
769 PluginInfo * CScriptsOfThread::GetPluginInfo(LPDISPATCH piScript)
772 for (i = 0 ; i < nTransformationEvents ; i ++)
774 if (m_aPluginsByEvent[i] == NULL)
776 const PluginArrayPtr& pArray = m_aPluginsByEvent[i];
777 for (j = 0 ; j < pArray->size() ; j++)
778 if ((*pArray)[j]->m_lpDispatch == piScript)
779 return (*pArray)[j].get();
785 ////////////////////////////////////////////////////////////////////////////////////
786 // class CAllThreadsScripts : array of CScriptsOfThread, one per active thread
788 std::vector<CScriptsOfThread *> CAllThreadsScripts::m_aAvailableThreads;
789 FastMutex m_aAvailableThreadsLock;
791 void CAllThreadsScripts::Add(CScriptsOfThread * scripts)
793 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
794 // add the thread in the array
796 // register in the array
797 m_aAvailableThreads.push_back(scripts);
800 void CAllThreadsScripts::Remove(CScriptsOfThread * scripts)
802 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
803 // unregister from the list
804 std::vector<CScriptsOfThread *>::iterator it;
805 for (it = m_aAvailableThreads.begin(); it != m_aAvailableThreads.end(); ++it)
806 if ((*it) == scripts)
808 m_aAvailableThreads.erase(it);
813 CScriptsOfThread * CAllThreadsScripts::GetActiveSet()
815 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
816 unsigned long nThreadId = GetCurrentThreadId();
818 for (i = 0 ; i < m_aAvailableThreads.size() ; i++)
819 if (m_aAvailableThreads[i] && m_aAvailableThreads[i]->m_nThreadId == nThreadId)
820 return m_aAvailableThreads[i];
824 CScriptsOfThread * CAllThreadsScripts::GetActiveSetNoAssert()
826 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
827 unsigned long nThreadId = GetCurrentThreadId();
829 for (i = 0 ; i < m_aAvailableThreads.size() ; i++)
830 if (m_aAvailableThreads[i] && m_aAvailableThreads[i]->m_nThreadId == nThreadId)
831 return m_aAvailableThreads[i];
835 bool CAllThreadsScripts::bInMainThread(CScriptsOfThread * scripts)
837 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
838 return (scripts == m_aAvailableThreads[0]);
841 ////////////////////////////////////////////////////////////////////////////////////
842 // class CAssureScriptsForThread : control creation/destruction of CScriptsOfThread
844 CAssureScriptsForThread::CAssureScriptsForThread()
846 CScriptsOfThread * scripts = CAllThreadsScripts::GetActiveSetNoAssert();
849 scripts = new CScriptsOfThread;
850 // insert the script in the repository
851 CAllThreadsScripts::Add(scripts);
855 CAssureScriptsForThread::~CAssureScriptsForThread()
857 CScriptsOfThread * scripts = CAllThreadsScripts::GetActiveSetNoAssert();
860 if (scripts->Unlock() == true)
862 CAllThreadsScripts::Remove(scripts);
867 ////////////////////////////////////////////////////////////////////////////////
868 // reallocation, take care of flag bWriteable
870 static void reallocBuffer(LPSTR & pszBuf, UINT & nOldSize, UINT nSize, bool bWriteable)
873 // alloc a new buffer
874 pszBuf = (LPSTR) malloc(nSize);
875 else if (nSize > nOldSize)
877 // free the previous buffer, alloc a new one (so we don't copy the old values)
879 pszBuf = (LPSTR) malloc(nSize);
882 // just truncate the buffer
883 pszBuf = (LPSTR) realloc(pszBuf, nSize);
886 static void reallocBuffer(LPWSTR & pszBuf, UINT & nOldSize, UINT nSize, bool bWriteable)
889 // alloc a new buffer
890 pszBuf = (LPWSTR) malloc(nSize*sizeof(WCHAR));
891 else if (nSize > nOldSize)
893 // free the previous buffer, alloc a new one (so we don't copy the old values)
895 pszBuf = (LPWSTR) malloc(nSize*sizeof(WCHAR));
898 // just truncate the buffer
899 pszBuf = (LPWSTR) realloc(pszBuf, nSize*sizeof(WCHAR));
904 ////////////////////////////////////////////////////////////////////////////////
905 // wrap invokes with error handlers
908 * @brief Display a message box with the plugin name and the error message
910 * @note Use MessageBox instead of AfxMessageBox so we can set the caption.
911 * VB/VBS plugins has an internal error handler, and a message box with caption,
912 * and we try to reproduce it for other plugins.
914 static void ShowPluginErrorMessage(IDispatch *piScript, LPTSTR description)
916 PluginInfo * pInfo = CAllThreadsScripts::GetActiveSet()->GetPluginInfo(piScript);
917 assert(pInfo != NULL);
918 assert(description != NULL);
919 AppErrorMessageBox(string_format(_T("%s: %s"), pInfo->m_name.c_str(), description));
923 * @brief safe invoke helper (by ordinal)
925 * @note Free all variants passed to it (except ByRef ones)
927 static HRESULT safeInvokeA(LPDISPATCH pi, VARIANT *ret, DISPID id, LPCCH op, ...)
931 TCHAR errorText[500];
932 bool bExceptionCatched = false;
934 int nargs = LOBYTE((UINT_PTR)op);
935 vector<VARIANT> args(nargs);
938 for (vector<VARIANT>::iterator it = args.begin(); it != args.end(); ++it)
939 *it = va_arg(list, VARIANT);
946 h = invokeA(pi, ret, id, op, nargs == 0 ? NULL : &args[0]);
948 h = invokeA(pi, ret, id, op, (VARIANT *)(&op + 1));
951 catch(SE_Exception& e)
953 // structured exception are catched here thanks to class SE_Exception
954 if (!(e.GetErrorMessage(errorText, 500, NULL)))
955 // don't localize this as we do not localize the known exceptions
956 _tcscpy(errorText, _T("Unknown CException"));
957 bExceptionCatched = true;
961 // don't localize this as we do not localize the known exceptions
962 _tcscpy(errorText, _T("Unknown C++ exception"));
963 bExceptionCatched = true;
966 if (bExceptionCatched)
968 ShowPluginErrorMessage(pi, errorText);
976 * @brief safe invoke helper (by function name)
978 * @note Free all variants passed to it (except ByRef ones)
980 static HRESULT safeInvokeW(LPDISPATCH pi, VARIANT *ret, LPCOLESTR silent, LPCCH op, ...)
984 TCHAR errorText[500];
985 bool bExceptionCatched = false;
987 int nargs = LOBYTE((UINT_PTR)op);
988 vector<VARIANT> args(nargs);
991 for (vector<VARIANT>::iterator it = args.begin(); it != args.end(); ++it)
992 *it = va_arg(list, VARIANT);
999 h = invokeW(pi, ret, silent, op, nargs == 0 ? NULL : &args[0]);
1001 h = invokeW(pi, ret, silent, op, (VARIANT *)(&op + 1));
1004 catch(SE_Exception& e)
1006 // structured exception are catched here thanks to class SE_Exception
1007 if (!(e.GetErrorMessage(errorText, 500, NULL)))
1008 // don't localize this as we do not localize the known exceptions
1009 _tcscpy(errorText, _T("Unknown CException"));
1010 bExceptionCatched = true;
1014 // don't localize this as we do not localize the known exceptions
1015 _tcscpy(errorText, _T("Unknown C++ exception"));
1016 bExceptionCatched = true;
1019 if (bExceptionCatched)
1021 ShowPluginErrorMessage(pi, errorText);
1029 ////////////////////////////////////////////////////////////////////////////////
1030 // invoke for plugins
1033 * ----- about VariantClear -----
1034 * VariantClear is done in safeInvokeW/safeInvokeA except for :
1035 * - the returned value
1037 * note : BYREF arguments don't need VariantClear if the refered value
1038 * is deleted in the function destructor. Example :
1042 * vValue.plVal = &vValue;
1046 bool InvokePrediffBuffer(BSTR & bstrBuf, int & nChanged, IDispatch *piScript)
1048 UINT nBufSize = SysStringLen(bstrBuf);
1050 // prepare the arguments
1051 // argument text buffer by reference
1053 vpbstrBuf.vt = VT_BYREF | VT_BSTR;
1054 vpbstrBuf.pbstrVal = &bstrBuf;
1055 // argument buffer size by reference
1057 vpiSize.vt = VT_BYREF | VT_I4;
1058 vpiSize.plVal = (long*) &nBufSize;
1059 // argument flag changed (VT_BOOL is short)
1060 VARIANT_BOOL changed = 0;
1061 VARIANT vpboolChanged;
1062 vpboolChanged.vt = VT_BYREF | VT_BOOL;
1063 vpboolChanged.pboolVal = &changed;
1064 // argument return value (VT_BOOL is short)
1065 VARIANT vboolHandled;
1066 vboolHandled.vt = VT_BOOL;
1067 vboolHandled.boolVal = false;
1069 // invoke method by name, reverse order for arguments
1070 // for VC, if the invoked function changes the buffer address,
1071 // it must free the old buffer with SysFreeString
1072 // VB does it automatically
1073 // VARIANT_BOOL DiffingPreprocessW(BSTR * buffer, UINT * nSize, VARIANT_BOOL * bChanged)
1074 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"PrediffBufferW", opFxn[3],
1075 vpboolChanged, vpiSize, vpbstrBuf);
1076 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1077 if (bSuccess && changed)
1079 // remove trailing charracters in the rare case that bstrBuf was not resized
1080 if (SysStringLen(bstrBuf) != nBufSize)
1081 bSuccess = !FAILED(SysReAllocStringLen(&bstrBuf, bstrBuf, nBufSize));
1086 // clear the returned variant
1087 VariantClear(&vboolHandled);
1092 bool InvokeUnpackBuffer(VARIANT & array, int & nChanged, IDispatch *piScript, int & subcode)
1095 SafeArrayGetUBound(array.parray, 0, &nArraySize);
1098 // prepare the arguments
1099 // argument file buffer
1101 vparrayBuf.vt = VT_BYREF | VT_ARRAY | VT_UI1;
1102 vparrayBuf.pparray = &(array.parray);
1103 // argument buffer size by reference
1105 vpiSize.vt = VT_BYREF | VT_I4;
1106 vpiSize.plVal = (long*) &nArraySize;
1107 // argument flag changed (VT_BOOL is short)
1108 VARIANT_BOOL changed = 0;
1109 VARIANT vpboolChanged;
1110 vpboolChanged.vt = VT_BYREF | VT_BOOL;
1111 vpboolChanged.pboolVal = &changed;
1112 // argument subcode by reference
1114 viSubcode.vt = VT_BYREF | VT_I4;
1115 viSubcode.plVal = (long*) &subcode;
1116 // argument return value (VT_BOOL is short)
1117 VARIANT vboolHandled;
1118 vboolHandled.vt = VT_BOOL;
1119 vboolHandled.boolVal = false;
1121 // invoke method by name, reverse order for arguments
1122 // VARIANT_BOOL UnpackBufferA(SAFEARRAY * array, UINT * nSize, VARIANT_BOOL * bChanged, UINT * subcode)
1123 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"UnpackBufferA", opFxn[4],
1124 viSubcode, vpboolChanged, vpiSize, vparrayBuf);
1125 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1126 if (bSuccess && changed)
1128 // remove trailing charracters if the array was not resized
1130 SafeArrayGetUBound(array.parray, 0, &nNewArraySize);
1133 if (nNewArraySize != nArraySize)
1135 SAFEARRAYBOUND sab = {nArraySize, 0};
1136 SafeArrayRedim(array.parray, &sab);
1141 // clear the returned variant
1142 VariantClear(&vboolHandled);
1147 bool InvokePackBuffer(VARIANT & array, int & nChanged, IDispatch *piScript, int subcode)
1150 SafeArrayGetUBound(array.parray, 0, &nArraySize);
1153 // prepare the arguments
1154 // argument file buffer
1156 vparrayBuf.vt = VT_BYREF | VT_ARRAY | VT_UI1;
1157 vparrayBuf.pparray = &(array.parray);
1158 // argument buffer size by reference
1160 vpiSize.vt = VT_BYREF | VT_I4;
1161 vpiSize.plVal = (long*) &nArraySize;
1162 // argument flag changed (VT_BOOL is short)
1163 VARIANT_BOOL changed = 0;
1164 VARIANT vpboolChanged;
1165 vpboolChanged.vt = VT_BYREF | VT_BOOL;
1166 vpboolChanged.pboolVal = &changed;
1169 viSubcode.vt = VT_I4;
1170 viSubcode.lVal = subcode;
1171 // argument return value (VT_BOOL is short)
1172 VARIANT vboolHandled;
1173 vboolHandled.vt = VT_BOOL;
1174 vboolHandled.boolVal = false;
1176 // invoke method by name, reverse order for arguments
1177 // VARIANT_BOOL PackBufferA(SAFEARRAY * array, UINT * nSize, VARIANT_BOOL * bChanged, UINT subcode)
1178 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"PackBufferA", opFxn[4],
1179 viSubcode, vpboolChanged, vpiSize, vparrayBuf);
1180 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1181 if (bSuccess && changed)
1183 // remove trailing charracters if the array was not resized
1185 SafeArrayGetUBound(array.parray, 0, &nNewArraySize);
1188 if (nNewArraySize != nArraySize)
1190 SAFEARRAYBOUND sab = {nArraySize, 0};
1191 SafeArrayRedim(array.parray, &sab);
1195 // clear the returned variant
1196 VariantClear(&vboolHandled);
1202 bool InvokeUnpackFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript, int & subCode)
1206 vbstrSrc.vt = VT_BSTR;
1207 vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(fileSource).c_str());
1208 // argument transformed text
1210 vbstrDst.vt = VT_BSTR;
1211 vbstrDst.bstrVal = SysAllocString(ucr::toUTF16(fileDest).c_str());
1212 // argument subcode by reference
1214 vpiSubcode.vt = VT_BYREF | VT_I4;
1215 vpiSubcode.plVal = (long*) &subCode;
1216 // argument flag changed (VT_BOOL is short)
1217 VARIANT_BOOL changed = 0;
1218 VARIANT vpboolChanged;
1219 vpboolChanged.vt = VT_BYREF | VT_BOOL;
1220 vpboolChanged.pboolVal = &changed;
1221 // argument return value (VT_BOOL is short)
1222 VARIANT vboolHandled;
1223 vboolHandled.vt = VT_BOOL;
1224 vboolHandled.boolVal = false;
1226 // invoke method by name, reverse order for arguments
1227 // VARIANT_BOOL UnpackFile(BSTR fileSrc, BSTR fileDst, VARIANT_BOOL * bChanged, INT * bSubcode)
1228 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"UnpackFile", opFxn[4],
1229 vpiSubcode, vpboolChanged, vbstrDst, vbstrSrc);
1230 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1231 if (bSuccess && changed)
1234 // clear the returned variant
1235 VariantClear(&vboolHandled);
1240 bool InvokePackFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript, int subCode)
1244 vbstrSrc.vt = VT_BSTR;
1245 vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(fileSource).c_str());
1246 // argument transformed text
1248 vbstrDst.vt = VT_BSTR;
1249 vbstrDst.bstrVal = SysAllocString(ucr::toUTF16(fileDest).c_str());
1252 viSubcode.vt = VT_I4;
1253 viSubcode.lVal = 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;
1264 // invoke method by name, reverse order for arguments
1265 // VARIANT_BOOL PackFile(BSTR fileSrc, BSTR fileDst, VARIANT_BOOL * bChanged, INT bSubcode)
1266 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"PackFile", opFxn[4],
1267 viSubcode, vpboolChanged, vbstrDst, vbstrSrc);
1268 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1269 if (bSuccess && changed)
1272 // clear the returned variant
1273 VariantClear(&vboolHandled);
1278 bool InvokePrediffFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript)
1282 vbstrSrc.vt = VT_BSTR;
1283 vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(fileSource).c_str());
1284 // argument transformed text
1286 vbstrDst.vt = VT_BSTR;
1287 vbstrDst.bstrVal = SysAllocString(ucr::toUTF16(fileDest).c_str());
1288 // argument flag changed (VT_BOOL is short)
1289 VARIANT_BOOL changed = 0;
1290 VARIANT vpboolChanged;
1291 vpboolChanged.vt = VT_BYREF | VT_BOOL;
1292 vpboolChanged.pboolVal = &changed;
1293 // argument return value (VT_BOOL is short)
1294 VARIANT vboolHandled;
1295 vboolHandled.vt = VT_BOOL;
1296 vboolHandled.boolVal = false;
1298 // invoke method by name, reverse order for arguments
1299 // VARIANT_BOOL PrediffFile(BSTR fileSrc, BSTR fileDst, VARIANT_BOOL * bChanged)
1300 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"PrediffFile", opFxn[3],
1301 vpboolChanged, vbstrDst, vbstrSrc);
1302 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1303 if (bSuccess && changed)
1306 // clear the returned variant
1307 VariantClear(&vboolHandled);
1313 bool InvokeTransformText(String & text, int & changed, IDispatch *piScript, int fncId)
1317 pvPszBuf.vt = VT_BSTR;
1318 pvPszBuf.bstrVal = SysAllocString(ucr::toUTF16(text).c_str());
1319 // argument transformed text
1320 VARIANT vTransformed;
1321 vTransformed.vt = VT_BSTR;
1322 vTransformed.bstrVal = NULL;
1324 // invoke method by ordinal
1325 // BSTR customFunction(BSTR text)
1326 HRESULT h = ::safeInvokeA(piScript, &vTransformed, fncId, opFxn[1], pvPszBuf);
1330 text = ucr::toTString(vTransformed.bstrVal);
1336 // clear the returned variant
1337 VariantClear(&vTransformed);
1339 return (! FAILED(h));