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
28 #define POCO_NO_UNWINDOWS 1
34 #include <Poco/Mutex.h>
35 #include <Poco/ScopedLock.h>
36 #include <Poco/RegularExpression.h>
40 #include "FileFilterMgr.h"
43 #include "Exceptions.h"
46 #include "Environment.h"
47 #include "FileFilter.h"
50 using Poco::RegularExpression;
51 using Poco::FastMutex;
52 using Poco::ScopedLock;
55 * @brief Category of transformation : define the transformation events
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
61 const wchar_t *TransformationCategories[] =
66 L"BUFFER_PACK_UNPACK",
68 L"FILE_FOLDER_PACK_UNPACK",
69 NULL, // last empty : necessary
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;
78 template<class T> struct AutoReleaser
80 AutoReleaser(T *ptr) : p(ptr) {}
81 ~AutoReleaser() { if (p) p->Release(); }
85 ////////////////////////////////////////////////////////////////////////////////
87 * @brief Check for the presence of Windows Script
89 * .sct plugins require this optional component
91 bool IsWindowsScriptThere()
94 if (!keyFile.QueryRegMachine(_T("SOFTWARE\\Classes\\scriptletfile\\AutoRegister")))
97 String filename = keyFile.ReadString(_T(""), _T("")).c_str();
102 return (paths_DoesPathExist(filename) == IS_EXISTING_FILE);
105 ////////////////////////////////////////////////////////////////////////////////
106 // scriptlet/activeX support for function names
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)
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)))
120 TYPEATTR *pTypeAttr=0;
121 if (SUCCEEDED(hr = piTypeInfo->GetTypeAttr(&pTypeAttr)))
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);
128 UINT iMaxFunc = pTypeAttr->cFuncs - 1;
129 for (UINT iFunc = 0 ; iFunc <= iMaxFunc ; ++iFunc)
131 UINT iFuncDesc = iMaxFunc - iFunc;
133 if (SUCCEEDED(hr = piTypeInfo->GetFuncDesc(iFuncDesc, &pFuncDesc)))
135 // exclude properties
136 // exclude IDispatch inherited methods
137 if (pFuncDesc->invkind & wantedKind && !(pFuncDesc->wFuncFlags & 1))
141 if (SUCCEEDED(hr = piTypeInfo->GetNames(pFuncDesc->memid,
142 &bstrName, 1, &cNames)))
144 IdArray[iValidFunc] = pFuncDesc->memid;
145 namesArray[iValidFunc] = ucr::toTString(bstrName);
148 SysFreeString(bstrName);
150 piTypeInfo->ReleaseFuncDesc(pFuncDesc);
153 piTypeInfo->ReleaseTypeAttr(pTypeAttr);
155 piTypeInfo->Release();
161 int GetMethodsFromScript(IDispatch *piDispatch, vector<String>& namesArray, vector<int> &IdArray)
163 return GetFunctionsFromScript(piDispatch, namesArray, IdArray, INVOKE_FUNC);
165 int GetPropertyGetsFromScript(IDispatch *piDispatch, vector<String>& namesArray, vector<int> &IdArray)
167 return GetFunctionsFromScript(piDispatch, namesArray, IdArray, INVOKE_PROPERTYGET);
171 // search a function name in a scriptlet or activeX dll
172 bool SearchScriptForMethodName(LPDISPATCH piDispatch, const wchar_t *functionName)
176 vector<String> namesArray;
178 int nFnc = GetMethodsFromScript(piDispatch, namesArray, IdArray);
180 String tfuncname = ucr::toTString(functionName);
182 for (iFnc = 0 ; iFnc < nFnc ; iFnc++)
184 if (namesArray[iFnc] == tfuncname)
190 // search a property name (with get interface) in a scriptlet or activeX dll
191 bool SearchScriptForDefinedProperties(IDispatch *piDispatch, const wchar_t *functionName)
195 vector<String> namesArray;
197 int nFnc = GetPropertyGetsFromScript(piDispatch, namesArray, IdArray);
199 String tfuncname = ucr::toTString(functionName);
201 for (iFnc = 0 ; iFnc < nFnc ; iFnc++)
203 if (namesArray[iFnc] == tfuncname)
210 int CountMethodsInScript(LPDISPATCH piDispatch)
212 vector<String> namesArray;
214 int nFnc = GetMethodsFromScript(piDispatch, namesArray, IdArray);
220 * @return ID of the function or -1 if no function with this index
222 int GetMethodIDInScript(LPDISPATCH piDispatch, int methodIndex)
226 vector<String> namesArray;
228 int nFnc = GetMethodsFromScript(piDispatch, namesArray, IdArray);
230 if (methodIndex < nFnc)
232 fncID = IdArray[methodIndex];
242 ////////////////////////////////////////////////////////////////////////////////
243 // find scripts/activeX for an event : each event is assigned to a subdirectory
247 * @brief Get a list of scriptlet file
249 * @return Returns an array of LPSTR
251 static void GetScriptletsAt(const String& sSearchPath, const String& extension, vector<String>& scriptlets )
254 String strFileSpec = paths_ConcatPath(sSearchPath, _T("*") + extension);
255 HANDLE hff = FindFirstFile(strFileSpec.c_str(), &ffi);
257 if ( hff != INVALID_HANDLE_VALUE )
261 if (!(ffi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
263 strFileSpec = paths_ConcatPath(sSearchPath, ffi.cFileName);
264 scriptlets.push_back(strFileSpec);
267 while (FindNextFile(hff, &ffi));
272 void PluginInfo::LoadFilterString()
276 String sLine(m_filtersText);
281 String::size_type pos = sLine.rfind(';');
282 sPiece = sLine.substr(pos+1);
283 if (pos == String::npos)
285 sLine = sLine.substr(0, pos);
289 sPiece = string_makeupper(string_trim_ws_begin(sPiece));
292 std::string regexString = ucr::toUTF8(sPiece);
293 re_opts |= RegularExpression::RE_UTF8;
296 m_filters.push_back(FileFilterElementPtr(new FileFilterElement(regexString, re_opts)));
306 bool PluginInfo::TestAgainstRegList(const String& szTest) const
308 if (m_filters.empty() || szTest.empty())
311 String sLine = szTest;
314 while(!sLine.empty())
316 String::size_type pos = sLine.rfind('|');
317 sPiece = sLine.substr(pos+1);
318 if (pos == String::npos)
320 sLine = sLine.substr(0, pos);
323 sPiece = string_makeupper(string_trim_ws_begin(sPiece));
325 if (::TestAgainstRegList(&m_filters, sPiece))
333 * @brief Log technical explanation, in English, of script error
336 ScriptletError(const String & scriptletFilepath, const wchar_t *transformationEvent, const TCHAR *szError)
338 String msg = _T("Plugin scriptlet error <")
341 + ucr::toTString(transformationEvent)
348 * @brief Tiny structure that remembers current scriptlet & event info for calling Log
352 ScriptInfo(const String & scriptletFilepath, const wchar_t *transformationEvent)
353 : m_scriptletFilepath(scriptletFilepath)
354 , m_transformationEvent(transformationEvent)
357 void Log(const TCHAR *szError)
359 ScriptletError(m_scriptletFilepath, m_transformationEvent, szError);
361 const String & m_scriptletFilepath;
362 const wchar_t *m_transformationEvent;
366 * @brief Try to load a plugin
368 * @return 1 if plugin handles this event, 0 if not, negatives for errors
370 int PluginInfo::LoadPlugin(const String & scriptletFilepath, const wchar_t *transformationEvent)
372 // set up object in case we need to log info
373 ScriptInfo scinfo(scriptletFilepath, transformationEvent);
375 // Search for the class "WinMergeScript"
376 LPDISPATCH lpDispatch = CreateDispatchBySource(scriptletFilepath.c_str(), L"WinMergeScript");
379 scinfo.Log(_T("WinMergeScript entry point not found"));
383 // Ensure that interface is released if any bad exit or exception
384 AutoReleaser<IDispatch> drv(lpDispatch);
386 // Is this plugin for this transformationEvent ?
388 // invoke mandatory method get PluginEvent
390 if (!SearchScriptForDefinedProperties(lpDispatch, L"PluginEvent"))
392 scinfo.Log(_T("PluginEvent method missing"));
395 HRESULT h = ::invokeW(lpDispatch, &ret, L"PluginEvent", opGet[0], NULL);
396 if (FAILED(h) || ret.vt != VT_BSTR)
398 scinfo.Log( _T("Error accessing PluginEvent method"));
401 if (wcscmp(ret.bstrVal, transformationEvent) != 0)
403 return 0; // doesn't handle this event
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
411 if (wcscmp(transformationEvent, L"BUFFER_PREDIFF") == 0)
413 bFound &= SearchScriptForMethodName(lpDispatch, L"PrediffBufferW");
415 else if (wcscmp(transformationEvent, L"FILE_PREDIFF") == 0)
417 bFound &= SearchScriptForMethodName(lpDispatch, L"PrediffFile");
419 else if (wcscmp(transformationEvent, L"BUFFER_PACK_UNPACK") == 0)
421 bFound &= SearchScriptForMethodName(lpDispatch, L"UnpackBufferA");
422 bFound &= SearchScriptForMethodName(lpDispatch, L"PackBufferA");
424 else if (wcscmp(transformationEvent, L"FILE_PACK_UNPACK") == 0)
426 bFound &= SearchScriptForMethodName(lpDispatch, L"UnpackFile");
427 bFound &= SearchScriptForMethodName(lpDispatch, L"PackFile");
429 else if (wcscmp(transformationEvent, L"FILE_FOLDER_PACK_UNPACK") == 0)
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");
439 // error (Plugin doesn't support the method as it claimed)
440 scinfo.Log(_T("Plugin doesn't support the method as it claimed"));
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)
448 m_nFreeFunctions = CountMethodsInScript(lpDispatch);
449 if (m_nFreeFunctions == 0)
450 // error (Plugin doesn't offer any method, what is this ?)
455 // get optional property PluginDescription
456 if (SearchScriptForDefinedProperties(lpDispatch, L"PluginDescription"))
458 h = ::invokeW(lpDispatch, &ret, L"PluginDescription", opGet[0], NULL);
459 if (FAILED(h) || ret.vt != VT_BSTR)
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)
464 m_description = ucr::toTString(ret.bstrVal);
468 // no description, use filename
469 m_description = paths_FindFileName(scriptletFilepath);
473 // get PluginFileFilters
474 bool hasPluginFileFilters = false;
475 if (SearchScriptForDefinedProperties(lpDispatch, L"PluginFileFilters"))
477 h = ::invokeW(lpDispatch, &ret, L"PluginFileFilters", opGet[0], NULL);
478 if (FAILED(h) || ret.vt != VT_BSTR)
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)
483 m_filtersText = ucr::toTString(ret.bstrVal);
484 hasPluginFileFilters = true;
488 m_bAutomatic = false;
489 m_filtersText = _T(".");
493 // get optional property PluginIsAutomatic
494 if (SearchScriptForDefinedProperties(lpDispatch, L"PluginIsAutomatic"))
496 h = ::invokeW(lpDispatch, &ret, L"PluginIsAutomatic", opGet[0], NULL);
497 if (FAILED(h) || ret.vt != VT_BOOL)
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)
502 m_bAutomatic = !!ret.boolVal;
506 if (hasPluginFileFilters)
508 scinfo.Log(_T("Plugin had PluginFileFilters property, but lacked PluginIsAutomatic property"));
509 // PluginIsAutomatic property is mandatory for Plugins with PluginFileFilters property
512 // default to false when Plugin doesn't have property
513 m_bAutomatic = false;
520 m_name = paths_FindFileName(scriptletFilepath);
522 // Clear the autorelease holder
525 m_lpDispatch = lpDispatch;
527 m_filepath = scriptletFilepath;
532 static void ReportPluginLoadFailure(const String & scriptletFilepath, const wchar_t *transformationEvent)
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()));
539 * @brief Guard call to LoadPlugin with Windows SEH to trap GPFs
541 * @return same as LoadPlugin (1=has event, 0=doesn't have event, errors are negative)
543 static int LoadPluginWrapper(PluginInfo & plugin, const String & scriptletFilepath, const wchar_t *transformationEvent)
548 return plugin.LoadPlugin(scriptletFilepath, transformationEvent);
550 catch (SE_Exception&)
552 ReportPluginLoadFailure(scriptletFilepath, transformationEvent);
558 * @brief Return list of all candidate plugins in module path
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)
563 static vector<String>& LoadTheScriptletList()
565 FastMutex::ScopedLock lock(scriptletsSem);
566 if (!scriptletsLoaded)
568 String path = paths_ConcatPath(env_GetProgPath(), _T("MergePlugins"));
570 if (IsWindowsScriptThere())
571 GetScriptletsAt(path, _T(".sct"), theScriptletList ); // VBS/JVS scriptlet
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;
578 // lock the *.sct to avoid them being deleted/moved away
580 for (i = 0 ; i < theScriptletList.size() ; i++)
582 String scriptlet = theScriptletList.at(i);
583 if (scriptlet.length() > 4 && string_compare_nocase(scriptlet.substr(scriptlet.length() - 4), _T(".sct")) != 0)
585 // don't need to lock this file
586 theScriptletHandleList.push_back(NULL);
591 hFile=CreateFile(scriptlet.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
593 if (hFile == INVALID_HANDLE_VALUE)
595 theScriptletList.erase(theScriptletList.begin() + i);
600 theScriptletHandleList.push_back(hFile);
604 return theScriptletList;
607 * @brief Delete the scriptlet list and delete locks to *.sct
609 * Allow to load it again
611 static void UnloadTheScriptletList()
613 FastMutex::ScopedLock lock(scriptletsSem);
614 if (scriptletsLoaded)
617 for (i = 0 ; i < theScriptletHandleList.size() ; i++)
619 HANDLE hFile = theScriptletHandleList.at(i);
624 theScriptletHandleList.clear();
625 theScriptletList.clear();
626 scriptletsLoaded = false;
631 * @brief Remove a candidate plugin from the cache
633 static void RemoveScriptletCandidate(const String &scriptletFilepath)
635 for (int i=0; i<theScriptletList.size(); ++i)
637 if (scriptletFilepath == theScriptletList[i])
639 HANDLE hFile = theScriptletHandleList.at(i);
643 theScriptletHandleList.erase(theScriptletHandleList.begin() + i);
644 theScriptletList.erase(theScriptletList.begin() + i);
651 * @brief Get available scriptlets for an event
653 * @return Returns an array of valid LPDISPATCH
655 static PluginArray * GetAvailableScripts( const wchar_t *transformationEvent, bool getScriptletsToo )
657 vector<String>& scriptlets = LoadTheScriptletList();
659 PluginArray * pPlugins = new PluginArray;
662 std::list<String> badScriptlets;
663 for (i = 0 ; i < scriptlets.size() ; i++)
665 // Note all the info about the plugin
666 PluginInfoPtr plugin(new PluginInfo);
668 String scriptletFilepath = scriptlets.at(i);
669 int rtn = LoadPluginWrapper(*plugin.get(), scriptletFilepath, transformationEvent);
672 // Plugin has this event
673 pPlugins->push_back(plugin);
678 badScriptlets.push_back(scriptletFilepath);
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())
686 RemoveScriptletCandidate(badScriptlets.front());
687 badScriptlets.pop_front();
693 static void FreeAllScripts(PluginArrayPtr& pArray)
699 ////////////////////////////////////////////////////////////////////////////////////
700 // class CScriptsOfThread : cache the interfaces during the thread life
702 CScriptsOfThread::CScriptsOfThread()
704 // count number of events
707 if (TransformationCategories[i] == NULL)
709 nTransformationEvents = i;
711 // initialize the thread data
712 m_nThreadId = GetCurrentThreadId();
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);
722 CScriptsOfThread::~CScriptsOfThread()
726 if (hrInitialize == S_OK || hrInitialize == S_FALSE)
730 bool CScriptsOfThread::bInMainThread()
732 return (CAllThreadsScripts::bInMainThread(this));
735 PluginArray * CScriptsOfThread::GetAvailableScripts(const wchar_t *transformationEvent)
738 for (i = 0 ; i < nTransformationEvents ; i ++)
739 if (wcscmp(transformationEvent, TransformationCategories[i]) == 0)
741 if (m_aPluginsByEvent[i] == NULL)
742 m_aPluginsByEvent[i].reset(::GetAvailableScripts(transformationEvent, bInMainThread()));
743 return m_aPluginsByEvent[i].get();
745 // return a pointer to an empty list
746 static PluginArray noPlugin;
752 void CScriptsOfThread::FreeAllScripts()
754 // release all the scripts of the thread
756 for (i = 0 ; i < nTransformationEvents ; i++)
757 if (m_aPluginsByEvent[i])
758 ::FreeAllScripts(m_aPluginsByEvent[i]);
760 // force to reload the scriptlet list
761 UnloadTheScriptletList();
764 void CScriptsOfThread::FreeScriptsForEvent(const wchar_t *transformationEvent)
767 for (i = 0 ; i < nTransformationEvents ; i ++)
768 if (wcscmp(transformationEvent, TransformationCategories[i]) == 0)
770 if (m_aPluginsByEvent[i])
771 ::FreeAllScripts(m_aPluginsByEvent[i]);
776 PluginInfo *CScriptsOfThread::GetAutomaticPluginByFilter(const wchar_t *transformationEvent, const String& filteredText)
778 PluginArray * piFileScriptArray = GetAvailableScripts(transformationEvent);
779 for (int step = 0 ; step < piFileScriptArray->size() ; step ++)
781 const PluginInfoPtr & plugin = piFileScriptArray->at(step);
782 if (plugin->m_bAutomatic == false)
784 if (plugin->TestAgainstRegList(filteredText) == false)
791 PluginInfo * CScriptsOfThread::GetPluginByName(const wchar_t *transformationEvent, const String& name)
794 for (i = 0 ; i < nTransformationEvents ; i ++)
795 if (wcscmp(transformationEvent, TransformationCategories[i]) == 0)
797 if (m_aPluginsByEvent[i] == NULL)
798 m_aPluginsByEvent[i].reset(::GetAvailableScripts(transformationEvent, bInMainThread()));
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();
807 PluginInfo * CScriptsOfThread::GetPluginInfo(LPDISPATCH piScript)
810 for (i = 0 ; i < nTransformationEvents ; i ++)
812 if (m_aPluginsByEvent[i] == NULL)
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();
823 ////////////////////////////////////////////////////////////////////////////////////
824 // class CAllThreadsScripts : array of CScriptsOfThread, one per active thread
826 std::vector<CScriptsOfThread *> CAllThreadsScripts::m_aAvailableThreads;
827 FastMutex m_aAvailableThreadsLock;
829 void CAllThreadsScripts::Add(CScriptsOfThread * scripts)
831 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
832 // add the thread in the array
834 // register in the array
835 m_aAvailableThreads.push_back(scripts);
838 void CAllThreadsScripts::Remove(CScriptsOfThread * scripts)
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)
846 m_aAvailableThreads.erase(it);
851 CScriptsOfThread * CAllThreadsScripts::GetActiveSet()
853 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
854 unsigned long nThreadId = GetCurrentThreadId();
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];
862 CScriptsOfThread * CAllThreadsScripts::GetActiveSetNoAssert()
864 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
865 unsigned long nThreadId = GetCurrentThreadId();
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];
873 bool CAllThreadsScripts::bInMainThread(CScriptsOfThread * scripts)
875 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
876 return (scripts == m_aAvailableThreads[0]);
879 ////////////////////////////////////////////////////////////////////////////////////
880 // class CAssureScriptsForThread : control creation/destruction of CScriptsOfThread
882 CAssureScriptsForThread::CAssureScriptsForThread()
884 CScriptsOfThread * scripts = CAllThreadsScripts::GetActiveSetNoAssert();
887 scripts = new CScriptsOfThread;
888 // insert the script in the repository
889 CAllThreadsScripts::Add(scripts);
893 CAssureScriptsForThread::~CAssureScriptsForThread()
895 CScriptsOfThread * scripts = CAllThreadsScripts::GetActiveSetNoAssert();
898 if (scripts->Unlock() == true)
900 CAllThreadsScripts::Remove(scripts);
905 ////////////////////////////////////////////////////////////////////////////////
906 // reallocation, take care of flag bWriteable
908 static void reallocBuffer(LPSTR & pszBuf, UINT & nOldSize, UINT nSize, bool bWriteable)
911 // alloc a new buffer
912 pszBuf = (LPSTR) malloc(nSize);
913 else if (nSize > nOldSize)
915 // free the previous buffer, alloc a new one (so we don't copy the old values)
917 pszBuf = (LPSTR) malloc(nSize);
920 // just truncate the buffer
921 pszBuf = (LPSTR) realloc(pszBuf, nSize);
924 static void reallocBuffer(LPWSTR & pszBuf, UINT & nOldSize, UINT nSize, bool bWriteable)
927 // alloc a new buffer
928 pszBuf = (LPWSTR) malloc(nSize*sizeof(WCHAR));
929 else if (nSize > nOldSize)
931 // free the previous buffer, alloc a new one (so we don't copy the old values)
933 pszBuf = (LPWSTR) malloc(nSize*sizeof(WCHAR));
936 // just truncate the buffer
937 pszBuf = (LPWSTR) realloc(pszBuf, nSize*sizeof(WCHAR));
942 ////////////////////////////////////////////////////////////////////////////////
943 // wrap invokes with error handlers
946 * @brief Display a message box with the plugin name and the error message
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.
952 static void ShowPluginErrorMessage(IDispatch *piScript, LPTSTR description)
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));
961 * @brief safe invoke helper (by ordinal)
963 * @note Free all variants passed to it (except ByRef ones)
965 static HRESULT safeInvokeA(LPDISPATCH pi, VARIANT *ret, DISPID id, LPCCH op, ...)
969 TCHAR errorText[500];
970 bool bExceptionCatched = false;
972 int nargs = LOBYTE((UINT_PTR)op);
973 vector<VARIANT> args(nargs);
976 for (vector<VARIANT>::iterator it = args.begin(); it != args.end(); ++it)
977 *it = va_arg(list, VARIANT);
984 h = invokeA(pi, ret, id, op, nargs == 0 ? NULL : &args[0]);
986 h = invokeA(pi, ret, id, op, (VARIANT *)(&op + 1));
989 catch(SE_Exception& e)
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;
999 // don't localize this as we do not localize the known exceptions
1000 _tcscpy(errorText, _T("Unknown C++ exception"));
1001 bExceptionCatched = true;
1004 if (bExceptionCatched)
1006 ShowPluginErrorMessage(pi, errorText);
1014 * @brief safe invoke helper (by function name)
1016 * @note Free all variants passed to it (except ByRef ones)
1018 static HRESULT safeInvokeW(LPDISPATCH pi, VARIANT *ret, LPCOLESTR silent, LPCCH op, ...)
1022 TCHAR errorText[500];
1023 bool bExceptionCatched = false;
1025 int nargs = LOBYTE((UINT_PTR)op);
1026 vector<VARIANT> args(nargs);
1029 for (vector<VARIANT>::iterator it = args.begin(); it != args.end(); ++it)
1030 *it = va_arg(list, VARIANT);
1037 h = invokeW(pi, ret, silent, op, nargs == 0 ? NULL : &args[0]);
1039 h = invokeW(pi, ret, silent, op, (VARIANT *)(&op + 1));
1042 catch(SE_Exception& e)
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;
1052 // don't localize this as we do not localize the known exceptions
1053 _tcscpy(errorText, _T("Unknown C++ exception"));
1054 bExceptionCatched = true;
1057 if (bExceptionCatched)
1059 ShowPluginErrorMessage(pi, errorText);
1067 ////////////////////////////////////////////////////////////////////////////////
1068 // invoke for plugins
1071 * ----- about VariantClear -----
1072 * VariantClear is done in safeInvokeW/safeInvokeA except for :
1073 * - the returned value
1075 * note : BYREF arguments don't need VariantClear if the refered value
1076 * is deleted in the function destructor. Example :
1080 * vValue.plVal = &vValue;
1084 bool InvokePrediffBuffer(BSTR & bstrBuf, int & nChanged, IDispatch *piScript)
1086 UINT nBufSize = SysStringLen(bstrBuf);
1088 // prepare the arguments
1089 // argument text buffer by reference
1091 vpbstrBuf.vt = VT_BYREF | VT_BSTR;
1092 vpbstrBuf.pbstrVal = &bstrBuf;
1093 // argument buffer size by reference
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;
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)
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));
1124 // clear the returned variant
1125 VariantClear(&vboolHandled);
1130 bool InvokeUnpackBuffer(VARIANT & array, int & nChanged, IDispatch *piScript, int & subcode)
1133 SafeArrayGetUBound(array.parray, 0, &nArraySize);
1136 // prepare the arguments
1137 // argument file buffer
1139 vparrayBuf.vt = VT_BYREF | VT_ARRAY | VT_UI1;
1140 vparrayBuf.pparray = &(array.parray);
1141 // argument buffer size by reference
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
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;
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)
1166 // remove trailing charracters if the array was not resized
1168 SafeArrayGetUBound(array.parray, 0, &nNewArraySize);
1171 if (nNewArraySize != nArraySize)
1173 SAFEARRAYBOUND sab = {nArraySize, 0};
1174 SafeArrayRedim(array.parray, &sab);
1179 // clear the returned variant
1180 VariantClear(&vboolHandled);
1185 bool InvokePackBuffer(VARIANT & array, int & nChanged, IDispatch *piScript, int subcode)
1188 SafeArrayGetUBound(array.parray, 0, &nArraySize);
1191 // prepare the arguments
1192 // argument file buffer
1194 vparrayBuf.vt = VT_BYREF | VT_ARRAY | VT_UI1;
1195 vparrayBuf.pparray = &(array.parray);
1196 // argument buffer size by reference
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;
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;
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)
1221 // remove trailing charracters if the array was not resized
1223 SafeArrayGetUBound(array.parray, 0, &nNewArraySize);
1226 if (nNewArraySize != nArraySize)
1228 SAFEARRAYBOUND sab = {nArraySize, 0};
1229 SafeArrayRedim(array.parray, &sab);
1233 // clear the returned variant
1234 VariantClear(&vboolHandled);
1240 static bool unpack(const wchar_t *method, const String& source, const String& dest, int & nChanged, IDispatch *piScript, int & subCode)
1244 vbstrSrc.vt = VT_BSTR;
1245 vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(source).c_str());
1246 // argument transformed text
1248 vbstrDst.vt = VT_BSTR;
1249 vbstrDst.bstrVal = SysAllocString(ucr::toUTF16(dest).c_str());
1250 // argument subcode by reference
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;
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)
1272 // clear the returned variant
1273 VariantClear(&vboolHandled);
1278 static bool pack(const wchar_t *method, const String& source, const String& dest, int & nChanged, IDispatch *piScript, int & subCode)
1282 vbstrSrc.vt = VT_BSTR;
1283 vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(source).c_str());
1284 // argument transformed text
1286 vbstrDst.vt = VT_BSTR;
1287 vbstrDst.bstrVal = SysAllocString(ucr::toUTF16(dest).c_str());
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;
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)
1310 // clear the returned variant
1311 VariantClear(&vboolHandled);
1316 bool InvokeUnpackFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript, int & subCode)
1318 return unpack(L"UnpackFile", fileSource, fileDest, nChanged, piScript, subCode);
1321 bool InvokePackFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript, int subCode)
1323 return pack(L"PackFile", fileSource, fileDest, nChanged, piScript, subCode);
1326 bool InvokeUnpackFolder(const String& fileSource, const String& folderDest, int & nChanged, IDispatch *piScript, int & subCode)
1328 return unpack(L"UnpackFolder", fileSource, folderDest, nChanged, piScript, subCode);
1331 bool InvokePackFolder(const String& folderSource, const String& fileDest, int & nChanged, IDispatch *piScript, int subCode)
1333 return pack(L"PackFolder", folderSource, fileDest, nChanged, piScript, subCode);
1336 bool InvokePrediffFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript)
1340 vbstrSrc.vt = VT_BSTR;
1341 vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(fileSource).c_str());
1342 // argument transformed text
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;
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)
1364 // clear the returned variant
1365 VariantClear(&vboolHandled);
1371 bool InvokeTransformText(String & text, int & changed, IDispatch *piScript, int fncId)
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;
1382 // invoke method by ordinal
1383 // BSTR customFunction(BSTR text)
1384 HRESULT h = ::safeInvokeA(piScript, &vTransformed, fncId, opFxn[1], pvPszBuf);
1388 text = ucr::toTString(vTransformed.bstrVal);
1394 // clear the returned variant
1395 VariantClear(&vTransformed);
1397 return (! FAILED(h));
1400 bool InvokeIsFolder(const String& path, IDispatch *piScript)
1404 vbstrPath.vt = VT_BSTR;
1405 vbstrPath.bstrVal = SysAllocString(ucr::toUTF16(path).c_str());
1407 VARIANT vboolHandled;
1408 vboolHandled.vt = VT_BOOL;
1409 vboolHandled.boolVal = false;
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;
1416 // clear the returned variant
1417 VariantClear(&vboolHandled);
1418 VariantClear(&vbstrPath);
1423 bool InvokeShowSettingsDialog(IDispatch *piScript)
1425 VARIANT vboolHandled;
1426 vboolHandled.vt = VT_BOOL;
1427 vboolHandled.boolVal = false;
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;
1434 // clear the returned variant
1435 VariantClear(&vboolHandled);