1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
5 // SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
10 * @brief Support for VBS Scriptlets, VB ActiveX DLL, VC++ COM DLL
15 #define POCO_NO_UNWINDOWS 1
18 #include <unordered_set>
23 #include <Poco/Mutex.h>
24 #include <Poco/ScopedLock.h>
25 #include <Poco/RegularExpression.h>
29 #include "FileFilterMgr.h"
32 #include "Exceptions.h"
35 #include "Environment.h"
36 #include "FileFilter.h"
37 #include "coretools.h"
38 #include "OptionsMgr.h"
39 #include "OptionsDef.h"
40 #include "codepage_detect.h"
42 #include "WinMergePluginBase.h"
45 using Poco::RegularExpression;
46 using Poco::FastMutex;
47 using Poco::ScopedLock;
50 * @brief Category of transformation : define the transformation events
52 * @note USER categories are calls to scriptlets, or VB ActiveX DLL, or VC COM DLL
53 * Use text definition : if you add one, nothing to do ;
54 * if you change one, you just have change the dll/scripts for that event
56 const wchar_t *TransformationCategories[] =
61 L"BUFFER_PACK_UNPACK",
63 L"FILE_FOLDER_PACK_UNPACK",
64 nullptr, // last empty : necessary
67 static vector<String> theScriptletList;
68 /// Need to lock the *.sct so the user can't delete them
69 static vector<HANDLE> theScriptletHandleList;
70 static bool scriptletsLoaded=false;
71 static FastMutex scriptletsSem;
72 static std::unordered_map<String, String> customFiltersMap;
74 template<class T> struct AutoReleaser
76 explicit AutoReleaser(T *ptr) : p(ptr) {}
77 ~AutoReleaser() { if (p!=nullptr) p->Release(); }
81 ////////////////////////////////////////////////////////////////////////////////
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(""));
102 return (paths::DoesPathExist(filename) == paths::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)
112 if (piDispatch != nullptr)
114 ITypeInfo *piTypeInfo=nullptr;
115 unsigned iTInfo = 0; // 0 for type information of IDispatch itself
116 LCID lcid=0; // locale for localized method names (ignore if no localized names)
117 if (SUCCEEDED(piDispatch->GetTypeInfo(iTInfo, lcid, &piTypeInfo)))
119 TYPEATTR *pTypeAttr=nullptr;
120 if (SUCCEEDED(piTypeInfo->GetTypeAttr(&pTypeAttr)))
122 // allocate arrays for the returned structures
123 // the names array is NUL terminated
124 namesArray.resize(pTypeAttr->cFuncs+1);
125 IdArray.resize(pTypeAttr->cFuncs+1);
127 UINT iMaxFunc = pTypeAttr->cFuncs - 1;
128 for (UINT iFunc = 0 ; iFunc <= iMaxFunc ; ++iFunc)
130 UINT iFuncDesc = iMaxFunc - iFunc;
132 if (SUCCEEDED(piTypeInfo->GetFuncDesc(iFuncDesc, &pFuncDesc)))
134 // exclude properties
135 // exclude IDispatch inherited methods
136 if (pFuncDesc->invkind & wantedKind && !(pFuncDesc->wFuncFlags & 1))
140 if (SUCCEEDED(piTypeInfo->GetNames(pFuncDesc->memid,
141 &bstrName, 1, &cNames)))
143 IdArray[iValidFunc] = pFuncDesc->memid;
144 namesArray[iValidFunc] = ucr::toTString(bstrName);
147 SysFreeString(bstrName);
149 piTypeInfo->ReleaseFuncDesc(pFuncDesc);
152 piTypeInfo->ReleaseTypeAttr(pTypeAttr);
154 piTypeInfo->Release();
160 int GetMethodsFromScript(IDispatch *piDispatch, vector<String>& namesArray, vector<int> &IdArray)
162 return GetFunctionsFromScript(piDispatch, namesArray, IdArray, INVOKE_FUNC);
164 int GetPropertyGetsFromScript(IDispatch *piDispatch, vector<String>& namesArray, vector<int> &IdArray)
166 return GetFunctionsFromScript(piDispatch, namesArray, IdArray, INVOKE_PROPERTYGET);
170 // search a function name in a scriptlet or activeX dll
171 bool SearchScriptForMethodName(LPDISPATCH piDispatch, const wchar_t *functionName)
173 vector<String> namesArray;
175 const int nFnc = GetMethodsFromScript(piDispatch, namesArray, IdArray);
177 const String tfuncname = ucr::toTString(functionName);
178 for (int iFnc = 0 ; iFnc < nFnc ; iFnc++)
180 if (namesArray[iFnc] == tfuncname)
186 // search a property name (with get interface) in a scriptlet or activeX dll
187 bool SearchScriptForDefinedProperties(IDispatch *piDispatch, const wchar_t *functionName)
189 vector<String> namesArray;
191 const int nFnc = GetPropertyGetsFromScript(piDispatch, namesArray, IdArray);
193 const String tfuncname = ucr::toTString(functionName);
194 for (int iFnc = 0 ; iFnc < nFnc ; iFnc++)
196 if (namesArray[iFnc] == tfuncname)
203 int CountMethodsInScript(LPDISPATCH piDispatch)
205 vector<String> namesArray;
207 return GetMethodsFromScript(piDispatch, namesArray, IdArray);
211 * @return ID of the function or -1 if no function with this index
213 int GetMethodIDInScript(LPDISPATCH piDispatch, int methodIndex)
217 vector<String> namesArray;
219 const int nFnc = GetMethodsFromScript(piDispatch, namesArray, IdArray);
221 if (methodIndex < nFnc)
223 fncID = IdArray[methodIndex];
235 ////////////////////////////////////////////////////////////////////////////////
236 // find scripts/activeX for an event : each event is assigned to a subdirectory
240 * @brief Get a list of scriptlet file
242 * @return Returns an array of LPSTR
244 static void GetScriptletsAt(const String& sSearchPath, const String& extension, vector<String>& scriptlets )
247 String strFileSpec = paths::ConcatPath(sSearchPath, _T("*") + extension);
248 HANDLE hff = FindFirstFile(strFileSpec.c_str(), &ffi);
250 if ( hff != INVALID_HANDLE_VALUE )
254 if (!(ffi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
256 strFileSpec = paths::ConcatPath(sSearchPath, ffi.cFileName);
257 if (strFileSpec.substr(strFileSpec.length() - extension.length()) == extension) // excludes *.sct~ files
258 scriptlets.push_back(strFileSpec);
261 while (FindNextFile(hff, &ffi));
266 void PluginInfo::LoadFilterString()
270 String sLine(m_filtersText);
275 String::size_type pos = sLine.rfind(';');
276 sPiece = sLine.substr(pos+1);
277 if (pos == String::npos)
279 sLine = sLine.substr(0, pos);
283 sPiece = strutils::makeupper(strutils::trim_ws_begin(sPiece));
286 std::string regexString = ucr::toUTF8(sPiece);
287 re_opts |= RegularExpression::RE_UTF8;
290 m_filters.push_back(FileFilterElementPtr(new FileFilterElement(regexString, re_opts)));
300 bool PluginInfo::TestAgainstRegList(const String& szTest) const
302 if (m_filters.empty() || szTest.empty())
305 String sLine = szTest;
308 while(!sLine.empty())
310 String::size_type pos = sLine.rfind('|');
311 sPiece = sLine.substr(pos+1);
312 if (pos == String::npos)
314 sLine = sLine.substr(0, pos);
317 sPiece = strutils::makeupper(strutils::trim_ws_begin(sPiece));
319 if (::TestAgainstRegList(&m_filters, sPiece))
327 * @brief Log technical explanation, in English, of script error
330 ScriptletError(const String & scriptletFilepath, const TCHAR *szError)
332 String msg = _T("Plugin scriptlet error <")
339 static std::unordered_set<String> GetDisabledPluginList()
341 std::unordered_set<String> list;
342 std::basic_stringstream<TCHAR> ss(GetOptionsMgr()->GetString(OPT_PLUGINS_DISABLED_LIST));
344 while (std::getline(ss, name, _T('|')))
349 static std::unordered_map<String, String> GetCustomFiltersMap()
351 std::unordered_map<String, String> map;
352 std::basic_stringstream<TCHAR> ss(GetOptionsMgr()->GetString(OPT_PLUGINS_CUSTOM_FILTERS_LIST));
353 String nameAndFiltersText;
354 while (std::getline(ss, nameAndFiltersText, _T('\t')))
356 size_t pos = nameAndFiltersText.find_first_of(':');
357 if (pos != String::npos)
359 String name = nameAndFiltersText.substr(0, pos);
360 String filtersText = nameAndFiltersText.substr(pos + 1);
361 map.emplace(name, filtersText);
364 map.emplace(_T("||initialized||"), _T(""));
368 static String GetCustomFilters(const String& name, const String& filtersTextDefault)
370 FastMutex::ScopedLock lock(scriptletsSem);
371 if (customFiltersMap.empty())
372 customFiltersMap = GetCustomFiltersMap();
373 if (customFiltersMap.find(name) != customFiltersMap.end())
374 return customFiltersMap[name];
376 return filtersTextDefault;
379 class UnpackerGeneratedFromEditorScript: public WinMergePluginBase
382 UnpackerGeneratedFromEditorScript(IDispatch *pDispatch, const std::wstring funcname, int id)
383 : WinMergePluginBase(
385 strutils::format_string1(_T("Unpacker to execute %1 script (automatically generated)") , funcname),
387 , m_pDispatch(pDispatch)
390 m_pDispatch->AddRef();
393 virtual ~UnpackerGeneratedFromEditorScript()
395 m_pDispatch->Release();
398 static HRESULT ReadFile(const String& path, String& text)
401 if (!file.OpenReadOnly(path))
402 return E_ACCESSDENIED;
406 int iGuessEncodingType = GetOptionsMgr()->GetInt(OPT_CP_DETECT);
407 int64_t fileSize = file.GetFileSize();
408 FileTextEncoding encoding = codepage_detect::Guess(
409 paths::FindExtension(path), file.GetBase(), static_cast<size_t>(
410 fileSize < static_cast<int64_t>(codepage_detect::BufSize) ?
411 fileSize : static_cast<int64_t>(codepage_detect::BufSize)),
413 file.SetCodepage(encoding.m_codepage);
415 file.ReadStringAll(text);
420 static HRESULT WriteFile(const String& path, const String& text)
422 UniStdioFile fileOut;
423 if (!fileOut.Open(path, _T("wb")))
424 return E_ACCESSDENIED;
425 fileOut.SetUnicoding(ucr::UNICODESET::UTF8);
426 fileOut.SetBom(true);
428 fileOut.WriteString(text);
433 HRESULT STDMETHODCALLTYPE UnpackFile(BSTR fileSrc, BSTR fileDst, VARIANT_BOOL* pbChanged, INT* pSubcode, VARIANT_BOOL* pbSuccess) override
436 HRESULT hr = ReadFile(fileSrc, text);
440 if (!plugin::InvokeTransformText(text, changed, m_pDispatch, m_funcid))
442 hr = WriteFile(fileDst, text);
446 *pbChanged = VARIANT_TRUE;
447 *pbSuccess = VARIANT_TRUE;
452 IDispatch *m_pDispatch;
457 * @brief Tiny structure that remembers current scriptlet & event info for calling Log
461 ScriptInfo(const String & scriptletFilepath)
462 : m_scriptletFilepath(scriptletFilepath)
465 void Log(const TCHAR *szError)
467 ScriptletError(m_scriptletFilepath, szError);
469 const String & m_scriptletFilepath;
473 * @brief Try to load a plugin
475 * @return 1 if loaded plugin successfully, negatives for errors
477 int PluginInfo::MakeInfo(const String & scriptletFilepath, IDispatch *lpDispatch)
479 // set up object in case we need to log info
480 ScriptInfo scinfo(scriptletFilepath);
482 // Ensure that interface is released if any bad exit or exception
483 AutoReleaser<IDispatch> drv(lpDispatch);
485 // Is this plugin for this transformationEvent ?
487 // invoke mandatory method get PluginEvent
489 if (!plugin::SearchScriptForDefinedProperties(lpDispatch, L"PluginEvent"))
491 scinfo.Log(_T("PluginEvent method missing"));
494 HRESULT h = ::invokeW(lpDispatch, &ret, L"PluginEvent", opGet[0], nullptr);
495 if (FAILED(h) || ret.vt != VT_BSTR)
497 scinfo.Log( _T("Error accessing PluginEvent method"));
500 m_event = ucr::toTString(ret.bstrVal);
504 // plugins PREDIFF or PACK_UNPACK : functions names are mandatory
505 // Check that the plugin offers the requested functions
506 // set the mode for the events which uses it
508 if (m_event == _T("BUFFER_PREDIFF"))
510 bFound &= plugin::SearchScriptForMethodName(lpDispatch, L"PrediffBufferW");
512 else if (m_event == _T("FILE_PREDIFF"))
514 bFound &= plugin::SearchScriptForMethodName(lpDispatch, L"PrediffFile");
516 else if (m_event == _T("BUFFER_PACK_UNPACK"))
518 bFound &= plugin::SearchScriptForMethodName(lpDispatch, L"UnpackBufferA");
519 bFound &= plugin::SearchScriptForMethodName(lpDispatch, L"PackBufferA");
521 else if (m_event == _T("FILE_PACK_UNPACK"))
523 bFound &= plugin::SearchScriptForMethodName(lpDispatch, L"UnpackFile");
524 bFound &= plugin::SearchScriptForMethodName(lpDispatch, L"PackFile");
526 else if (m_event == _T("FILE_FOLDER_PACK_UNPACK"))
528 bFound &= plugin::SearchScriptForMethodName(lpDispatch, L"IsFolder");
529 bFound &= plugin::SearchScriptForMethodName(lpDispatch, L"UnpackFile");
530 bFound &= plugin::SearchScriptForMethodName(lpDispatch, L"PackFile");
531 bFound &= plugin::SearchScriptForMethodName(lpDispatch, L"UnpackFolder");
532 bFound &= plugin::SearchScriptForMethodName(lpDispatch, L"PackFolder");
536 // error (Plugin doesn't support the method as it claimed)
537 scinfo.Log(_T("Plugin doesn't support the method as it claimed"));
541 // plugins EDITOR_SCRIPT : functions names are free
542 // there may be several functions inside one script, count the number of functions
543 if (m_event == _T("EDITOR_SCRIPT"))
545 m_nFreeFunctions = plugin::CountMethodsInScript(lpDispatch);
546 if (m_nFreeFunctions == 0)
547 // error (Plugin doesn't offer any method, what is this ?)
552 // get optional property PluginDescription
553 if (plugin::SearchScriptForDefinedProperties(lpDispatch, L"PluginDescription"))
555 h = ::invokeW(lpDispatch, &ret, L"PluginDescription", opGet[0], nullptr);
556 if (FAILED(h) || ret.vt != VT_BSTR)
558 scinfo.Log(_T("Plugin had PluginDescription property, but error getting its value"));
559 return -60; // error (Plugin had PluginDescription property, but error getting its value)
561 m_description = ucr::toTString(ret.bstrVal);
565 // no description, use filename
566 m_description = paths::FindFileName(scriptletFilepath);
570 // get PluginFileFilters
571 bool hasPluginFileFilters = false;
572 if (plugin::SearchScriptForDefinedProperties(lpDispatch, L"PluginFileFilters"))
574 h = ::invokeW(lpDispatch, &ret, L"PluginFileFilters", opGet[0], nullptr);
575 if (FAILED(h) || ret.vt != VT_BSTR)
577 scinfo.Log(_T("Plugin had PluginFileFilters property, but error getting its value"));
578 return -70; // error (Plugin had PluginFileFilters property, but error getting its value)
580 m_filtersTextDefault = ucr::toTString(ret.bstrVal);
581 hasPluginFileFilters = true;
585 m_bAutomatic = false;
586 m_filtersTextDefault = _T(".");
590 // get optional property PluginIsAutomatic
591 if (plugin::SearchScriptForDefinedProperties(lpDispatch, L"PluginIsAutomatic"))
593 h = ::invokeW(lpDispatch, &ret, L"PluginIsAutomatic", opGet[0], nullptr);
594 if (FAILED(h) || ret.vt != VT_BOOL)
596 scinfo.Log(_T("Plugin had PluginIsAutomatic property, but error getting its value"));
597 return -80; // error (Plugin had PluginIsAutomatic property, but error getting its value)
599 m_bAutomatic = !!ret.boolVal;
603 if (hasPluginFileFilters)
605 scinfo.Log(_T("Plugin had PluginFileFilters property, but lacked PluginIsAutomatic property"));
606 // PluginIsAutomatic property is mandatory for Plugins with PluginFileFilters property
609 // default to false when Plugin doesn't have property
610 m_bAutomatic = false;
614 // get optional property PluginUnpackedFileExtenstion
615 if (plugin::SearchScriptForDefinedProperties(lpDispatch, L"PluginUnpackedFileExtension"))
617 h = ::invokeW(lpDispatch, &ret, L"PluginUnpackedFileExtension", opGet[0], nullptr);
618 if (FAILED(h) || ret.vt != VT_BSTR)
620 scinfo.Log(_T("Plugin had PluginUnpackedFileExtension property, but error getting its value"));
621 return -100; // error (Plugin had PluginUnpackedFileExtenstion property, but error getting its value)
623 m_ext = ucr::toTString(ret.bstrVal);
632 m_name = paths::FindFileName(scriptletFilepath);
634 m_filtersText = GetCustomFilters(m_name, m_filtersTextDefault);
637 // Clear the autorelease holder
640 m_lpDispatch = lpDispatch;
642 m_filepath = scriptletFilepath;
648 * @brief Try to load a plugin
650 * @return 1 if loaded plugin successfully, negatives for errors
652 int PluginInfo::LoadPlugin(const String & scriptletFilepath)
654 // set up object in case we need to log info
655 ScriptInfo scinfo(scriptletFilepath);
657 // Search for the class "WinMergeScript"
658 LPDISPATCH lpDispatch = CreateDispatchBySource(scriptletFilepath.c_str(), L"WinMergeScript");
659 if (lpDispatch == nullptr)
661 scinfo.Log(_T("WinMergeScript entry point not found"));
665 return MakeInfo(scriptletFilepath, lpDispatch);
668 static void ReportPluginLoadFailure(const String & scriptletFilepath)
670 AppErrorMessageBox(strutils::format(_T("Exception loading plugin\r\n%s"), scriptletFilepath));
674 * @brief Guard call to LoadPlugin with Windows SEH to trap GPFs
676 * @return same as LoadPlugin (1=has event, 0=doesn't have event, errors are negative)
678 static int LoadPluginWrapper(PluginInfo & plugin, const String & scriptletFilepath)
683 return plugin.LoadPlugin(scriptletFilepath);
685 catch (SE_Exception&)
687 ReportPluginLoadFailure(scriptletFilepath);
693 * @brief Return list of all candidate plugins in module path
695 * Computes list only the first time, and caches it.
696 * Lock the plugins *.sct (.ocx and .dll are locked when the interface is created)
698 static vector<String>& LoadTheScriptletList()
700 FastMutex::ScopedLock lock(scriptletsSem);
701 if (!scriptletsLoaded)
703 String path = paths::ConcatPath(env::GetProgPath(), _T("MergePlugins"));
705 if (plugin::IsWindowsScriptThere())
706 GetScriptletsAt(path, _T(".sct"), theScriptletList ); // VBS/JVS scriptlet
708 LogErrorString(_T("\n .sct plugins disabled (Windows Script Host not found)"));
709 GetScriptletsAt(path, _T(".ocx"), theScriptletList ); // VB COM object
710 GetScriptletsAt(path, _T(".dll"), theScriptletList ); // VC++ COM object
711 scriptletsLoaded = true;
713 // lock the *.sct to avoid them being deleted/moved away
714 for (size_t i = 0 ; i < theScriptletList.size() ; i++)
716 String scriptlet = theScriptletList.at(i);
717 if (scriptlet.length() > 4 && strutils::compare_nocase(scriptlet.substr(scriptlet.length() - 4), _T(".sct")) != 0)
719 // don't need to lock this file
720 theScriptletHandleList.push_back(nullptr);
725 hFile=CreateFile(scriptlet.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
727 if (hFile == INVALID_HANDLE_VALUE)
729 theScriptletList.erase(theScriptletList.begin() + i);
734 theScriptletHandleList.push_back(hFile);
738 return theScriptletList;
741 * @brief Delete the scriptlet list and delete locks to *.sct
743 * Allow to load it again
745 static void UnloadTheScriptletList()
747 FastMutex::ScopedLock lock(scriptletsSem);
748 if (scriptletsLoaded)
750 for (size_t i = 0 ; i < theScriptletHandleList.size() ; i++)
752 HANDLE hFile = theScriptletHandleList.at(i);
753 if (hFile != nullptr)
757 theScriptletHandleList.clear();
758 theScriptletList.clear();
759 scriptletsLoaded = false;
764 * @brief Remove a candidate plugin from the cache
766 static void RemoveScriptletCandidate(const String &scriptletFilepath)
768 for (size_t i=0; i<theScriptletList.size(); ++i)
770 if (scriptletFilepath == theScriptletList[i])
772 HANDLE hFile = theScriptletHandleList.at(i);
773 if (hFile != nullptr)
776 theScriptletHandleList.erase(theScriptletHandleList.begin() + i);
777 theScriptletList.erase(theScriptletList.begin() + i);
784 * @brief Get available scriptlets for an event
786 * @return Returns an array of valid LPDISPATCH
788 static std::map<String, PluginArrayPtr> GetAvailableScripts()
790 vector<String>& scriptlets = LoadTheScriptletList();
791 std::unordered_set<String> disabled_plugin_list = GetDisabledPluginList();
792 std::map<std::wstring, PluginArrayPtr> plugins;
794 std::list<String> badScriptlets;
795 for (size_t i = 0 ; i < scriptlets.size() ; i++)
797 // Note all the info about the plugin
798 PluginInfoPtr plugin(new PluginInfo);
800 String scriptletFilepath = scriptlets.at(i);
801 int rtn = LoadPluginWrapper(*plugin.get(), scriptletFilepath);
804 // Plugin has this event
805 plugin->m_disabled = (disabled_plugin_list.find(plugin->m_name) != disabled_plugin_list.end());
806 if (plugins.find(plugin->m_event) == plugins.end())
807 plugins[plugin->m_event].reset(new PluginArray);
808 plugins[plugin->m_event]->push_back(plugin);
813 badScriptlets.push_back(scriptletFilepath);
817 if (plugins.find(L"EDITOR_SCRIPT") != plugins.end())
819 for (auto plugin : *plugins[L"EDITOR_SCRIPT"])
821 std::vector<String> namesArray;
822 std::vector<int> idArray;
823 int validFuncs = plugin::GetMethodsFromScript(plugin->m_lpDispatch, namesArray, idArray);
824 for (int i = 0; i < validFuncs; ++i)
826 if (plugins.find(L"FILE_PACK_UNPACK") == plugins.end())
827 plugins[L"FILE_PACK_UNPACK"].reset(new PluginArray);
828 PluginInfoPtr pluginNew(new PluginInfo());
829 IDispatch *pDispatch = new UnpackerGeneratedFromEditorScript(plugin->m_lpDispatch, namesArray[i], idArray[i]);
831 pluginNew->MakeInfo(plugin->m_filepath + _T(":") + namesArray[i], pDispatch);
832 plugins[L"FILE_PACK_UNPACK"]->push_back(pluginNew);
837 // Remove any bad plugins from the cache
838 // This might be a good time to see if the user wants to abort or continue
839 while (!badScriptlets.empty())
841 RemoveScriptletCandidate(badScriptlets.front());
842 badScriptlets.pop_front();
848 static void FreeAllScripts(PluginArrayPtr& pArray)
854 ////////////////////////////////////////////////////////////////////////////////////
855 // class CScriptsOfThread : cache the interfaces during the thread life
857 CScriptsOfThread::CScriptsOfThread()
859 // count number of events
862 if (TransformationCategories[i] == nullptr)
864 nTransformationEvents = i;
866 // initialize the thread data
867 m_nThreadId = GetCurrentThreadId();
869 // initialize the plugins pointers
870 typedef PluginArray * LPPluginArray;
871 // CoInitialize the thread, keep the returned value for the destructor
872 hrInitialize = CoInitialize(nullptr);
873 assert(hrInitialize == S_OK || hrInitialize == S_FALSE);
876 CScriptsOfThread::~CScriptsOfThread()
880 if (hrInitialize == S_OK || hrInitialize == S_FALSE)
884 bool CScriptsOfThread::bInMainThread()
886 return (CAllThreadsScripts::bInMainThread(this));
889 PluginArray * CScriptsOfThread::GetAvailableScripts(const wchar_t *transformationEvent)
891 if (m_aPluginsByEvent.empty())
892 m_aPluginsByEvent = ::GetAvailableScripts();
893 if (auto it = m_aPluginsByEvent.find(transformationEvent); it != m_aPluginsByEvent.end())
894 return it->second.get();
895 // return a pointer to an empty list
896 static PluginArray noPlugin;
900 void CScriptsOfThread::SaveSettings()
902 std::vector<String> listDisabled;
903 std::vector<String> listCustomFilters;
904 if (m_aPluginsByEvent.empty())
905 m_aPluginsByEvent = ::GetAvailableScripts();
906 for (auto [key, pArray] : m_aPluginsByEvent)
908 for (size_t j = 0; j < pArray->size(); ++j)
910 const PluginInfoPtr & plugin = pArray->at(j);
911 if (plugin->m_disabled)
912 listDisabled.emplace_back(plugin->m_name);
913 if (plugin->m_filtersTextDefault != plugin->m_filtersText)
914 listCustomFilters.emplace_back(plugin->m_name + _T(":") + plugin->m_filtersText);
915 customFiltersMap.insert_or_assign(plugin->m_name, plugin->m_filtersText);
918 GetOptionsMgr()->SaveOption(OPT_PLUGINS_DISABLED_LIST, strutils::join(listDisabled.begin(), listDisabled.end(), _T("|")));
919 GetOptionsMgr()->SaveOption(OPT_PLUGINS_CUSTOM_FILTERS_LIST, strutils::join(listCustomFilters.begin(), listCustomFilters.end(), _T("\t")));
922 void CScriptsOfThread::FreeAllScripts()
924 // release all the scripts of the thread
925 for (auto [key, pArray] : m_aPluginsByEvent)
926 ::FreeAllScripts(pArray);
928 // force to reload the scriptlet list
929 UnloadTheScriptletList();
932 void CScriptsOfThread::FreeScriptsForEvent(const wchar_t *transformationEvent)
934 if (auto it = m_aPluginsByEvent.find(transformationEvent); it != m_aPluginsByEvent.end())
936 if (it->second != nullptr)
937 ::FreeAllScripts(it->second);
941 PluginInfo* CScriptsOfThread::GetUnpackerPluginByFilter(const String& filteredText)
943 PluginInfo *plugin = GetAutomaticPluginByFilter(L"FILE_PACK_UNPACK", filteredText);
944 if (plugin == nullptr)
945 plugin = GetAutomaticPluginByFilter(L"FILE_FOLDER_PACK_UNPACK", filteredText);
946 if (plugin == nullptr)
947 plugin = GetAutomaticPluginByFilter(L"BUFFER_PACK_UNPACK", filteredText);
951 PluginInfo *CScriptsOfThread::GetAutomaticPluginByFilter(const wchar_t *transformationEvent, const String& filteredText)
953 PluginArray * piFileScriptArray = GetAvailableScripts(transformationEvent);
954 for (size_t step = 0 ; step < piFileScriptArray->size() ; step ++)
956 const PluginInfoPtr & plugin = piFileScriptArray->at(step);
957 if (!plugin->m_bAutomatic || plugin->m_disabled)
959 if (!plugin->TestAgainstRegList(filteredText))
966 PluginInfo * CScriptsOfThread::GetPluginByName(const wchar_t *transformationEvent, const String& name)
968 if (m_aPluginsByEvent.empty())
969 m_aPluginsByEvent = ::GetAvailableScripts();
970 for (auto [key, pArray] : m_aPluginsByEvent)
972 if (!transformationEvent || key == transformationEvent)
974 for (size_t j = 0; j < pArray->size(); j++)
975 if (pArray->at(j)->m_name == name)
976 return pArray->at(j).get();
982 PluginInfo * CScriptsOfThread::GetPluginInfo(LPDISPATCH piScript)
984 for (auto [key, pArray] : m_aPluginsByEvent)
986 if (pArray == nullptr)
988 for (size_t j = 0 ; j < pArray->size() ; j++)
989 if ((*pArray)[j]->m_lpDispatch == piScript)
990 return (*pArray)[j].get();
996 ////////////////////////////////////////////////////////////////////////////////////
997 // class CAllThreadsScripts : array of CScriptsOfThread, one per active thread
999 std::vector<CScriptsOfThread *> CAllThreadsScripts::m_aAvailableThreads;
1000 FastMutex m_aAvailableThreadsLock;
1002 void CAllThreadsScripts::Add(CScriptsOfThread * scripts)
1004 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
1005 // add the thread in the array
1007 // register in the array
1008 m_aAvailableThreads.push_back(scripts);
1011 void CAllThreadsScripts::Remove(CScriptsOfThread * scripts)
1013 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
1014 // unregister from the list
1015 std::vector<CScriptsOfThread *>::iterator it;
1016 for (it = m_aAvailableThreads.begin(); it != m_aAvailableThreads.end(); ++it)
1017 if ((*it) == scripts)
1019 m_aAvailableThreads.erase(it);
1024 CScriptsOfThread * CAllThreadsScripts::GetActiveSet()
1026 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
1027 unsigned long nThreadId = GetCurrentThreadId();
1028 for (size_t i = 0 ; i < m_aAvailableThreads.size() ; i++)
1029 if (m_aAvailableThreads[i] && m_aAvailableThreads[i]->m_nThreadId == nThreadId)
1030 return m_aAvailableThreads[i];
1034 CScriptsOfThread * CAllThreadsScripts::GetActiveSetNoAssert()
1036 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
1037 unsigned long nThreadId = GetCurrentThreadId();
1038 for (size_t i = 0 ; i < m_aAvailableThreads.size() ; i++)
1039 if (m_aAvailableThreads[i] && m_aAvailableThreads[i]->m_nThreadId == nThreadId)
1040 return m_aAvailableThreads[i];
1044 bool CAllThreadsScripts::bInMainThread(CScriptsOfThread * scripts)
1046 FastMutex::ScopedLock lock(m_aAvailableThreadsLock);
1047 return (scripts == m_aAvailableThreads[0]);
1050 ////////////////////////////////////////////////////////////////////////////////////
1051 // class CAssureScriptsForThread : control creation/destruction of CScriptsOfThread
1053 CAssureScriptsForThread::CAssureScriptsForThread()
1055 CScriptsOfThread * scripts = CAllThreadsScripts::GetActiveSetNoAssert();
1056 if (scripts == nullptr)
1058 scripts = new CScriptsOfThread;
1059 // insert the script in the repository
1060 CAllThreadsScripts::Add(scripts);
1064 CAssureScriptsForThread::~CAssureScriptsForThread()
1066 CScriptsOfThread * scripts = CAllThreadsScripts::GetActiveSetNoAssert();
1067 if (scripts == nullptr)
1069 if (scripts->Unlock())
1071 CAllThreadsScripts::Remove(scripts);
1076 ////////////////////////////////////////////////////////////////////////////////
1077 // wrap invokes with error handlers
1080 * @brief Display a message box with the plugin name and the error message
1082 * @note Use MessageBox instead of AfxMessageBox so we can set the caption.
1083 * VB/VBS plugins has an internal error handler, and a message box with caption,
1084 * and we try to reproduce it for other plugins.
1086 static void ShowPluginErrorMessage(IDispatch *piScript, LPTSTR description)
1088 PluginInfo * pInfo = CAllThreadsScripts::GetActiveSet()->GetPluginInfo(piScript);
1089 assert(pInfo != nullptr);
1090 assert(description != nullptr);
1091 AppErrorMessageBox(strutils::format(_T("%s: %s"), pInfo->m_name, description));
1095 * @brief safe invoke helper (by ordinal)
1097 * @note Free all variants passed to it (except ByRef ones)
1099 static HRESULT safeInvokeA(LPDISPATCH pi, VARIANT *ret, DISPID id, LPCCH op, ...)
1103 TCHAR errorText[500];
1104 bool bExceptionCatched = false;
1106 int nargs = LOBYTE((UINT_PTR)op);
1107 vector<VARIANT> args(nargs);
1110 for (vector<VARIANT>::iterator it = args.begin(); it != args.end(); ++it)
1111 *it = va_arg(list, VARIANT);
1118 h = invokeA(pi, ret, id, op, nargs == 0 ? nullptr : &args[0]);
1120 h = invokeA(pi, ret, id, op, (VARIANT *)(&op + 1));
1123 catch(SE_Exception& e)
1125 // structured exception are catched here thanks to class SE_Exception
1126 if (!(e.GetErrorMessage(errorText, 500, nullptr)))
1127 // don't localize this as we do not localize the known exceptions
1128 _tcscpy_safe(errorText, _T("Unknown CException"));
1129 bExceptionCatched = true;
1133 // don't localize this as we do not localize the known exceptions
1134 _tcscpy_safe(errorText, _T("Unknown C++ exception"));
1135 bExceptionCatched = true;
1138 if (bExceptionCatched)
1140 ShowPluginErrorMessage(pi, errorText);
1148 * @brief safe invoke helper (by function name)
1150 * @note Free all variants passed to it (except ByRef ones)
1152 static HRESULT safeInvokeW(LPDISPATCH pi, VARIANT *ret, LPCOLESTR silent, LPCCH op, ...)
1156 TCHAR errorText[500];
1157 bool bExceptionCatched = false;
1159 int nargs = LOBYTE((UINT_PTR)op);
1160 vector<VARIANT> args(nargs);
1163 for (vector<VARIANT>::iterator it = args.begin(); it != args.end(); ++it)
1164 *it = va_arg(list, VARIANT);
1171 h = invokeW(pi, ret, silent, op, nargs == 0 ? nullptr : &args[0]);
1173 h = invokeW(pi, ret, silent, op, (VARIANT *)(&op + 1));
1176 catch(SE_Exception& e)
1178 // structured exception are catched here thanks to class SE_Exception
1179 if (!(e.GetErrorMessage(errorText, 500, nullptr)))
1180 // don't localize this as we do not localize the known exceptions
1181 _tcscpy_safe(errorText, _T("Unknown CException"));
1182 bExceptionCatched = true;
1186 // don't localize this as we do not localize the known exceptions
1187 _tcscpy_safe(errorText, _T("Unknown C++ exception"));
1188 bExceptionCatched = true;
1191 if (bExceptionCatched)
1193 ShowPluginErrorMessage(pi, errorText);
1201 ////////////////////////////////////////////////////////////////////////////////
1202 // invoke for plugins
1208 * ----- about VariantClear -----
1209 * VariantClear is done in safeInvokeW/safeInvokeA except for :
1210 * - the returned value
1212 * note : BYREF arguments don't need VariantClear if the refered value
1213 * is deleted in the function destructor. Example :
1217 * vValue.plVal = &vValue;
1221 bool InvokePrediffBuffer(BSTR & bstrBuf, int & nChanged, IDispatch *piScript)
1223 UINT nBufSize = SysStringLen(bstrBuf);
1225 // prepare the arguments
1226 // argument text buffer by reference
1228 vpbstrBuf.vt = VT_BYREF | VT_BSTR;
1229 vpbstrBuf.pbstrVal = &bstrBuf;
1230 // argument buffer size by reference
1232 vpiSize.vt = VT_BYREF | VT_I4;
1233 vpiSize.plVal = (long*) &nBufSize;
1234 // argument flag changed (VT_BOOL is short)
1235 VARIANT_BOOL changed = 0;
1236 VARIANT vpboolChanged;
1237 vpboolChanged.vt = VT_BYREF | VT_BOOL;
1238 vpboolChanged.pboolVal = &changed;
1239 // argument return value (VT_BOOL is short)
1240 VARIANT vboolHandled;
1241 vboolHandled.vt = VT_BOOL;
1242 vboolHandled.boolVal = false;
1244 // invoke method by name, reverse order for arguments
1245 // for VC, if the invoked function changes the buffer address,
1246 // it must free the old buffer with SysFreeString
1247 // VB does it automatically
1248 // VARIANT_BOOL DiffingPreprocessW(BSTR * buffer, UINT * nSize, VARIANT_BOOL * bChanged)
1249 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"PrediffBufferW", opFxn[3],
1250 vpboolChanged, vpiSize, vpbstrBuf);
1251 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1252 if (bSuccess && changed)
1254 // remove trailing charracters in the rare case that bstrBuf was not resized
1255 if (SysStringLen(bstrBuf) != nBufSize)
1256 bSuccess = !FAILED(SysReAllocStringLen(&bstrBuf, bstrBuf, nBufSize));
1261 // clear the returned variant
1262 VariantClear(&vboolHandled);
1267 bool InvokeUnpackBuffer(VARIANT & array, int & nChanged, IDispatch *piScript, int & subcode)
1270 SafeArrayGetUBound(array.parray, 0, &nArraySize);
1273 // prepare the arguments
1274 // argument file buffer
1276 vparrayBuf.vt = VT_BYREF | VT_ARRAY | VT_UI1;
1277 vparrayBuf.pparray = &(array.parray);
1278 // argument buffer size by reference
1280 vpiSize.vt = VT_BYREF | VT_I4;
1281 vpiSize.plVal = (long*) &nArraySize;
1282 // argument flag changed (VT_BOOL is short)
1283 VARIANT_BOOL changed = 0;
1284 VARIANT vpboolChanged;
1285 vpboolChanged.vt = VT_BYREF | VT_BOOL;
1286 vpboolChanged.pboolVal = &changed;
1287 // argument subcode by reference
1289 viSubcode.vt = VT_BYREF | VT_I4;
1290 viSubcode.plVal = (long*) &subcode;
1291 // argument return value (VT_BOOL is short)
1292 VARIANT vboolHandled;
1293 vboolHandled.vt = VT_BOOL;
1294 vboolHandled.boolVal = false;
1296 // invoke method by name, reverse order for arguments
1297 // VARIANT_BOOL UnpackBufferA(SAFEARRAY * array, UINT * nSize, VARIANT_BOOL * bChanged, UINT * subcode)
1298 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"UnpackBufferA", opFxn[4],
1299 viSubcode, vpboolChanged, vpiSize, vparrayBuf);
1300 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1301 if (bSuccess && changed)
1303 // remove trailing charracters if the array was not resized
1305 SafeArrayGetUBound(array.parray, 0, &nNewArraySize);
1308 if (nNewArraySize != nArraySize)
1310 SAFEARRAYBOUND sab = {static_cast<ULONG>(nArraySize), 0};
1311 SafeArrayRedim(array.parray, &sab);
1316 // clear the returned variant
1317 VariantClear(&vboolHandled);
1322 bool InvokePackBuffer(VARIANT & array, int & nChanged, IDispatch *piScript, int subcode)
1325 SafeArrayGetUBound(array.parray, 0, &nArraySize);
1328 // prepare the arguments
1329 // argument file buffer
1331 vparrayBuf.vt = VT_BYREF | VT_ARRAY | VT_UI1;
1332 vparrayBuf.pparray = &(array.parray);
1333 // argument buffer size by reference
1335 vpiSize.vt = VT_BYREF | VT_I4;
1336 vpiSize.plVal = (long*) &nArraySize;
1337 // argument flag changed (VT_BOOL is short)
1338 VARIANT_BOOL changed = 0;
1339 VARIANT vpboolChanged;
1340 vpboolChanged.vt = VT_BYREF | VT_BOOL;
1341 vpboolChanged.pboolVal = &changed;
1344 viSubcode.vt = VT_I4;
1345 viSubcode.lVal = subcode;
1346 // argument return value (VT_BOOL is short)
1347 VARIANT vboolHandled;
1348 vboolHandled.vt = VT_BOOL;
1349 vboolHandled.boolVal = false;
1351 // invoke method by name, reverse order for arguments
1352 // VARIANT_BOOL PackBufferA(SAFEARRAY * array, UINT * nSize, VARIANT_BOOL * bChanged, UINT subcode)
1353 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"PackBufferA", opFxn[4],
1354 viSubcode, vpboolChanged, vpiSize, vparrayBuf);
1355 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1356 if (bSuccess && changed)
1358 // remove trailing charracters if the array was not resized
1360 SafeArrayGetUBound(array.parray, 0, &nNewArraySize);
1363 if (nNewArraySize != nArraySize)
1365 SAFEARRAYBOUND sab = {static_cast<ULONG>(nArraySize), 0};
1366 SafeArrayRedim(array.parray, &sab);
1370 // clear the returned variant
1371 VariantClear(&vboolHandled);
1377 static bool unpack(const wchar_t *method, const String& source, const String& dest, int & nChanged, IDispatch *piScript, int & subCode)
1381 vbstrSrc.vt = VT_BSTR;
1382 vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(source).c_str());
1383 // argument transformed text
1385 vbstrDst.vt = VT_BSTR;
1386 vbstrDst.bstrVal = SysAllocString(ucr::toUTF16(dest).c_str());
1387 // argument subcode by reference
1389 vpiSubcode.vt = VT_BYREF | VT_I4;
1390 vpiSubcode.plVal = (long*) &subCode;
1391 // argument flag changed (VT_BOOL is short)
1392 VARIANT_BOOL changed = 0;
1393 VARIANT vpboolChanged;
1394 vpboolChanged.vt = VT_BYREF | VT_BOOL;
1395 vpboolChanged.pboolVal = &changed;
1396 // argument return value (VT_BOOL is short)
1397 VARIANT vboolHandled;
1398 vboolHandled.vt = VT_BOOL;
1399 vboolHandled.boolVal = false;
1401 // invoke method by name, reverse order for arguments
1402 // VARIANT_BOOL UnpackFile(BSTR fileSrc, BSTR fileDst, VARIANT_BOOL * bChanged, INT * bSubcode)
1403 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, method, opFxn[4],
1404 vpiSubcode, vpboolChanged, vbstrDst, vbstrSrc);
1405 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1406 if (bSuccess && changed)
1409 // clear the returned variant
1410 VariantClear(&vboolHandled);
1415 static bool pack(const wchar_t *method, const String& source, const String& dest, int & nChanged, IDispatch *piScript, int & subCode)
1419 vbstrSrc.vt = VT_BSTR;
1420 vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(source).c_str());
1421 // argument transformed text
1423 vbstrDst.vt = VT_BSTR;
1424 vbstrDst.bstrVal = SysAllocString(ucr::toUTF16(dest).c_str());
1427 viSubcode.vt = VT_I4;
1428 viSubcode.lVal = subCode;
1429 // argument flag changed (VT_BOOL is short)
1430 VARIANT_BOOL changed = 0;
1431 VARIANT vpboolChanged;
1432 vpboolChanged.vt = VT_BYREF | VT_BOOL;
1433 vpboolChanged.pboolVal = &changed;
1434 // argument return value (VT_BOOL is short)
1435 VARIANT vboolHandled;
1436 vboolHandled.vt = VT_BOOL;
1437 vboolHandled.boolVal = false;
1439 // invoke method by name, reverse order for arguments
1440 // VARIANT_BOOL PackFile(BSTR fileSrc, BSTR fileDst, VARIANT_BOOL * bChanged, INT bSubcode)
1441 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, method, opFxn[4],
1442 viSubcode, vpboolChanged, vbstrDst, vbstrSrc);
1443 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1444 if (bSuccess && changed)
1447 // clear the returned variant
1448 VariantClear(&vboolHandled);
1453 bool InvokeUnpackFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript, int & subCode)
1455 return unpack(L"UnpackFile", fileSource, fileDest, nChanged, piScript, subCode);
1458 bool InvokePackFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript, int subCode)
1460 return pack(L"PackFile", fileSource, fileDest, nChanged, piScript, subCode);
1463 bool InvokeUnpackFolder(const String& fileSource, const String& folderDest, int & nChanged, IDispatch *piScript, int & subCode)
1465 return unpack(L"UnpackFolder", fileSource, folderDest, nChanged, piScript, subCode);
1468 bool InvokePackFolder(const String& folderSource, const String& fileDest, int & nChanged, IDispatch *piScript, int subCode)
1470 return pack(L"PackFolder", folderSource, fileDest, nChanged, piScript, subCode);
1473 bool InvokePrediffFile(const String& fileSource, const String& fileDest, int & nChanged, IDispatch *piScript)
1477 vbstrSrc.vt = VT_BSTR;
1478 vbstrSrc.bstrVal = SysAllocString(ucr::toUTF16(fileSource).c_str());
1479 // argument transformed text
1481 vbstrDst.vt = VT_BSTR;
1482 vbstrDst.bstrVal = SysAllocString(ucr::toUTF16(fileDest).c_str());
1483 // argument flag changed (VT_BOOL is short)
1484 VARIANT_BOOL changed = 0;
1485 VARIANT vpboolChanged;
1486 vpboolChanged.vt = VT_BYREF | VT_BOOL;
1487 vpboolChanged.pboolVal = &changed;
1488 // argument return value (VT_BOOL is short)
1489 VARIANT vboolHandled;
1490 vboolHandled.vt = VT_BOOL;
1491 vboolHandled.boolVal = false;
1493 // invoke method by name, reverse order for arguments
1494 // VARIANT_BOOL PrediffFile(BSTR fileSrc, BSTR fileDst, VARIANT_BOOL * bChanged)
1495 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"PrediffFile", opFxn[3],
1496 vpboolChanged, vbstrDst, vbstrSrc);
1497 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1498 if (bSuccess && changed)
1501 // clear the returned variant
1502 VariantClear(&vboolHandled);
1508 bool InvokeTransformText(String & text, int & changed, IDispatch *piScript, int fncId)
1512 pvPszBuf.vt = VT_BSTR;
1513 pvPszBuf.bstrVal = SysAllocString(ucr::toUTF16(text).c_str());
1514 // argument transformed text
1515 VARIANT vTransformed;
1516 vTransformed.vt = VT_BSTR;
1517 vTransformed.bstrVal = nullptr;
1519 // invoke method by ordinal
1520 // BSTR customFunction(BSTR text)
1521 HRESULT h = ::safeInvokeA(piScript, &vTransformed, fncId, opFxn[1], pvPszBuf);
1523 if (! FAILED(h) && vTransformed.bstrVal)
1525 text = ucr::toTString(vTransformed.bstrVal);
1531 // clear the returned variant
1532 VariantClear(&vTransformed);
1534 return (! FAILED(h));
1537 bool InvokeIsFolder(const String& path, IDispatch *piScript)
1541 vbstrPath.vt = VT_BSTR;
1542 vbstrPath.bstrVal = SysAllocString(ucr::toUTF16(path).c_str());
1544 VARIANT vboolHandled;
1545 vboolHandled.vt = VT_BOOL;
1546 vboolHandled.boolVal = false;
1548 // invoke method by name, reverse order for arguments
1549 // VARIANT_BOOL ShowSettingsDialog()
1550 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"IsFolder", opFxn[1], vbstrPath);
1551 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1553 // clear the returned variant
1554 VariantClear(&vboolHandled);
1559 bool InvokeShowSettingsDialog(IDispatch *piScript)
1561 VARIANT vboolHandled;
1562 vboolHandled.vt = VT_BOOL;
1563 vboolHandled.boolVal = false;
1565 // invoke method by name, reverse order for arguments
1566 // VARIANT_BOOL ShowSettingsDialog()
1567 HRESULT h = ::safeInvokeW(piScript, &vboolHandled, L"ShowSettingsDialog", opFxn[0]);
1568 bool bSuccess = ! FAILED(h) && vboolHandled.boolVal;
1570 // clear the returned variant
1571 VariantClear(&vboolHandled);