Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1076 lines
29 KiB

  1. // Copyright (c) 1999 Microsoft Corporation. All rights reserved.
  2. //
  3. // Implementation of CActiveScriptManager.
  4. //
  5. #include "stdinc.h"
  6. #include "activescript.h"
  7. #include "dll.h"
  8. #include "oleaut.h"
  9. #include "dmscript.h"
  10. #include "authelper.h"
  11. #include "packexception.h"
  12. #include <objsafe.h>
  13. //////////////////////////////////////////////////////////////////////
  14. // Global constants
  15. const LCID lcidUSEnglish = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
  16. const WCHAR g_wszGlobalDispatch[] = L"DirectMusic";
  17. //////////////////////////////////////////////////////////////////////
  18. // Static variables
  19. SmartRef::Vector<CActiveScriptManager::ThreadContextPair> CActiveScriptManager::ms_svecContext;
  20. //////////////////////////////////////////////////////////////////////
  21. // ScriptNames
  22. HRESULT
  23. ScriptNames::Init(bool fUseOleAut, DWORD cNames)
  24. {
  25. m_prgbstr = new BSTR[cNames];
  26. if (!m_prgbstr)
  27. return E_OUTOFMEMORY;
  28. ZeroMemory(m_prgbstr, sizeof(BSTR) * cNames);
  29. m_fUseOleAut = fUseOleAut;
  30. m_dwSize = cNames;
  31. return S_OK;
  32. }
  33. void
  34. ScriptNames::Clear()
  35. {
  36. if (m_prgbstr)
  37. {
  38. for (DWORD i = 0; i < m_dwSize; ++i)
  39. {
  40. DMS_SysFreeString(m_fUseOleAut, m_prgbstr[i]);
  41. }
  42. }
  43. delete[] m_prgbstr;
  44. }
  45. //////////////////////////////////////////////////////////////////////
  46. // Public functions
  47. CActiveScriptManager::CActiveScriptManager(
  48. bool fUseOleAut,
  49. const WCHAR *pwszLanguage,
  50. const WCHAR *pwszSource,
  51. CDirectMusicScript *pParentScript,
  52. HRESULT *phr,
  53. DMUS_SCRIPT_ERRORINFO *pErrorInfo)
  54. : m_cRef(1),
  55. m_pParentScript(pParentScript),
  56. m_fUseOleAut(fUseOleAut),
  57. m_pActiveScript(NULL),
  58. m_pDispatchScript(NULL),
  59. m_bstrErrorSourceComponent(NULL),
  60. m_bstrErrorDescription(NULL),
  61. m_bstrErrorSourceLineText(NULL),
  62. m_bstrHelpFile(NULL),
  63. m_i64IntendedStartTime(0),
  64. m_dwIntendedStartTimeFlags(0)
  65. {
  66. LockModule(true);
  67. this->ClearErrorInfo();
  68. IActiveScriptParse *pActiveScriptParse = NULL;
  69. if (!m_pParentScript)
  70. {
  71. assert(false);
  72. *phr = E_POINTER;
  73. goto Fail;
  74. }
  75. // Create the scripting engine
  76. CLSID clsid;
  77. *phr = CLSIDFromProgID(pwszLanguage, &clsid);
  78. if (FAILED(*phr))
  79. goto Fail;
  80. *phr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IActiveScript, reinterpret_cast<void **>(&m_pActiveScript));
  81. if (FAILED(*phr))
  82. goto Fail;
  83. // Initialize the scripting engine
  84. {
  85. IObjectSafety* pSafety = NULL;
  86. if (SUCCEEDED(m_pActiveScript->QueryInterface(IID_IObjectSafety, (void**) &pSafety)))
  87. {
  88. DWORD dwSafetySupported, dwSafetyEnabled;
  89. // Get the interface safety otions
  90. if (SUCCEEDED(*phr = pSafety->GetInterfaceSafetyOptions(IID_IActiveScript, &dwSafetySupported, &dwSafetyEnabled)))
  91. {
  92. // Only allow objects which say they are safe for untrusted data, and
  93. // say that we require the use of a security manager. This gives us much
  94. // more control
  95. dwSafetyEnabled |= INTERFACESAFE_FOR_UNTRUSTED_DATA | INTERFACESAFE_FOR_UNTRUSTED_CALLER |
  96. INTERFACE_USES_DISPEX | INTERFACE_USES_SECURITY_MANAGER;
  97. *phr = pSafety->SetInterfaceSafetyOptions(IID_IActiveScript, dwSafetySupported, dwSafetyEnabled);
  98. }
  99. pSafety->Release();
  100. if (FAILED(*phr)) goto Fail;
  101. }
  102. }
  103. *phr = m_pActiveScript->SetScriptSite(this);
  104. if (FAILED(*phr))
  105. goto Fail;
  106. // Add the default objects
  107. *phr = m_pActiveScript->AddNamedItem(g_wszGlobalDispatch, SCRIPTITEM_ISVISIBLE | SCRIPTITEM_NOCODE | SCRIPTITEM_GLOBALMEMBERS);
  108. if (FAILED(*phr))
  109. goto Fail;
  110. // Parse the script
  111. *phr = m_pActiveScript->QueryInterface(IID_IActiveScriptParse, reinterpret_cast<void **>(&pActiveScriptParse));
  112. if (FAILED(*phr))
  113. {
  114. if (*phr == E_NOINTERFACE)
  115. {
  116. Trace(1, "Error: Scripting engine '%S' does not support the IActiveScriptParse interface required for use with DirectMusic.\n", pwszLanguage);
  117. *phr = DMUS_E_SCRIPT_LANGUAGE_INCOMPATIBLE;
  118. }
  119. goto Fail;
  120. }
  121. *phr = pActiveScriptParse->InitNew();
  122. if (FAILED(*phr))
  123. goto Fail;
  124. EXCEPINFO exinfo;
  125. ZeroMemory(&exinfo, sizeof(EXCEPINFO));
  126. *phr = pActiveScriptParse->ParseScriptText(
  127. pwszSource,
  128. NULL,
  129. NULL,
  130. NULL,
  131. NULL,
  132. 0,
  133. 0,
  134. NULL,
  135. &exinfo);
  136. if (*phr == DISP_E_EXCEPTION)
  137. this->ContributeErrorInfo(L"parsing script", L"", exinfo);
  138. if (FAILED(*phr))
  139. goto Fail;
  140. SafeRelease(pActiveScriptParse); // No longer needed
  141. return;
  142. Fail:
  143. if (m_pActiveScript)
  144. m_pActiveScript->Close();
  145. SafeRelease(pActiveScriptParse);
  146. SafeRelease(m_pActiveScript);
  147. *phr = this->ReturnErrorInfo(*phr, pErrorInfo);
  148. }
  149. HRESULT
  150. CActiveScriptManager::Start(DMUS_SCRIPT_ERRORINFO *pErrorInfo)
  151. {
  152. if (!m_pActiveScript)
  153. {
  154. Trace(1, "Error: Script element not initialized.\n");
  155. return DMUS_E_NOT_INIT;
  156. }
  157. // Start the script running
  158. // Set context to this script (VBScript runs global code and could play something when it starts)
  159. CActiveScriptManager *pASM = NULL;
  160. HRESULT hr = CActiveScriptManager::SetCurrentContext(this, &pASM);
  161. if (FAILED(hr))
  162. return hr;
  163. hr = m_pActiveScript->SetScriptState(SCRIPTSTATE_STARTED); // We don't need to sink any events
  164. CActiveScriptManager::SetCurrentContext(pASM, NULL);
  165. if (FAILED(hr))
  166. goto Fail;
  167. assert(hr != S_FALSE);
  168. if (hr != S_OK)
  169. {
  170. assert(false);
  171. hr = DMUS_E_SCRIPT_LANGUAGE_INCOMPATIBLE;
  172. goto Fail;
  173. }
  174. hr = m_pActiveScript->GetScriptDispatch(NULL, &m_pDispatchScript);
  175. if (FAILED(hr))
  176. goto Fail;
  177. return S_OK;
  178. Fail:
  179. if (m_pActiveScript)
  180. m_pActiveScript->Close();
  181. SafeRelease(m_pActiveScript);
  182. SafeRelease(m_pDispatchScript);
  183. hr = this->ReturnErrorInfo(hr, pErrorInfo);
  184. return hr;
  185. }
  186. HRESULT
  187. CActiveScriptManager::CallRoutine(
  188. const WCHAR *pwszRoutineName,
  189. DMUS_SCRIPT_ERRORINFO *pErrorInfo)
  190. {
  191. if (!m_pDispatchScript)
  192. {
  193. Trace(1, "Error calling script routine: Script element not initialized.\n");
  194. return DMUS_E_NOT_INIT;
  195. }
  196. DISPID dispid;
  197. HRESULT hr = this->GetIDOfName(pwszRoutineName, &dispid);
  198. if (hr == DISP_E_UNKNOWNNAME)
  199. {
  200. Trace(1, "Error: Attempt to call routine '%S' that is not defined in the script.\n", pwszRoutineName);
  201. return DMUS_E_SCRIPT_ROUTINE_NOT_FOUND;
  202. }
  203. if (FAILED(hr))
  204. return hr;
  205. this->ClearErrorInfo();
  206. DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
  207. EXCEPINFO exinfo;
  208. ZeroMemory(&exinfo, sizeof(EXCEPINFO));
  209. // Set context to this script
  210. CActiveScriptManager *pASM = NULL;
  211. hr = CActiveScriptManager::SetCurrentContext(this, &pASM);
  212. if (FAILED(hr))
  213. return hr;
  214. hr = m_pDispatchScript->Invoke(
  215. dispid,
  216. m_fUseOleAut ? IID_NULL : g_guidInvokeWithoutOleaut,
  217. lcidUSEnglish,
  218. DISPATCH_METHOD,
  219. &dispparamsNoArgs,
  220. NULL,
  221. &exinfo,
  222. NULL);
  223. // Restore previous context (the routine could have been called from another script,
  224. // whose context needs to be restored).
  225. CActiveScriptManager::SetCurrentContext(pASM, NULL);
  226. if (hr == DISP_E_EXCEPTION)
  227. this->ContributeErrorInfo(L"calling routine ", pwszRoutineName, exinfo);
  228. return this->ReturnErrorInfo(hr, pErrorInfo);
  229. }
  230. HRESULT
  231. CActiveScriptManager::ScriptTrackCallRoutine(
  232. const WCHAR *pwszRoutineName,
  233. IDirectMusicSegmentState *pSegSt,
  234. DWORD dwVirtualTrackID,
  235. bool fErrorPMsgsEnabled,
  236. __int64 i64IntendedStartTime,
  237. DWORD dwIntendedStartTimeFlags)
  238. {
  239. DMUS_SCRIPT_ERRORINFO ErrorInfo;
  240. if (fErrorPMsgsEnabled)
  241. ZeroAndSize(&ErrorInfo);
  242. // record current timing context
  243. __int64 i64IntendedStartTime_PreCall = m_i64IntendedStartTime;
  244. DWORD dwIntendedStartTimeFlags_PreCall = m_dwIntendedStartTimeFlags;
  245. // set designated timing context (used by play/stop methods if called within the routine)
  246. m_i64IntendedStartTime = i64IntendedStartTime;
  247. m_dwIntendedStartTimeFlags = dwIntendedStartTimeFlags;
  248. HRESULT hr = CallRoutine(pwszRoutineName, &ErrorInfo);
  249. // Restore the previous timing context.
  250. // This is important because when R finishes it will resore both fields to the values set in the
  251. // constructor, which are music time 0. This setting means that routines called via IDirectMusicScript
  252. // will play segments at the current time.
  253. // It is also important because such calls can be nested. Assume that track T calls a script routine R
  254. // that plays a segment containing track T', which calls another script routine R'. Statements
  255. // in R should be associated with the time of R in T, but statements in R' get the time of R' in T'.
  256. m_i64IntendedStartTime = i64IntendedStartTime_PreCall;
  257. m_dwIntendedStartTimeFlags = dwIntendedStartTimeFlags_PreCall;
  258. if (fErrorPMsgsEnabled && hr == DMUS_E_SCRIPT_ERROR_IN_SCRIPT)
  259. {
  260. IDirectMusicPerformance *pPerf = m_pParentScript->GetPerformance();
  261. FireScriptTrackErrorPMsg(pPerf, pSegSt, dwVirtualTrackID, &ErrorInfo);
  262. }
  263. return hr;
  264. }
  265. HRESULT
  266. CActiveScriptManager::SetVariable(
  267. const WCHAR *pwszVariableName,
  268. VARIANT varValue,
  269. bool fSetRef,
  270. DMUS_SCRIPT_ERRORINFO *pErrorInfo)
  271. {
  272. if (!m_pDispatchScript)
  273. {
  274. Trace(1, "Error setting script variable: Script element not initialized.\n");
  275. return DMUS_E_NOT_INIT;
  276. }
  277. DISPID dispid;
  278. HRESULT hr = this->GetIDOfName(pwszVariableName, &dispid);
  279. if (hr == DISP_E_UNKNOWNNAME)
  280. {
  281. Trace(1, "Error: Attempt to set variable '%S' that is not defined in the script.\n", pwszVariableName);
  282. return DMUS_E_SCRIPT_VARIABLE_NOT_FOUND;
  283. }
  284. if (FAILED(hr))
  285. return hr;
  286. this->ClearErrorInfo();
  287. DISPID dispidPropPut = DISPID_PROPERTYPUT;
  288. DISPPARAMS dispparams;
  289. dispparams.rgvarg = &varValue;
  290. dispparams.rgdispidNamedArgs = &dispidPropPut;
  291. dispparams.cArgs = 1;
  292. dispparams.cNamedArgs = 1;
  293. EXCEPINFO exinfo;
  294. ZeroMemory(&exinfo, sizeof(EXCEPINFO));
  295. hr = m_pDispatchScript->Invoke(
  296. dispid,
  297. m_fUseOleAut ? IID_NULL : g_guidInvokeWithoutOleaut,
  298. lcidUSEnglish,
  299. fSetRef ? DISPATCH_PROPERTYPUTREF : DISPATCH_PROPERTYPUT,
  300. &dispparams,
  301. NULL,
  302. &exinfo,
  303. NULL);
  304. if (hr == DISP_E_EXCEPTION)
  305. {
  306. this->ContributeErrorInfo(L"setting variable ", pwszVariableName, exinfo);
  307. // Check if it was more likely a malformed call to SetVariable rather than an error in the script, in which
  308. // case return a descriptive HRESULT rather than the textual error.
  309. bool fObject = varValue.vt == VT_DISPATCH || varValue.vt == VT_UNKNOWN;
  310. if (fObject)
  311. {
  312. if (!fSetRef)
  313. {
  314. // Theoretically an object could support the value property, which would allow it to be assigned by value.
  315. // (Not that any of our built-in objects currently do this.)
  316. // But in this case we know that the set failed, so probably this is the fault of the caller, who forgot to use
  317. // fSetRef when setting an object.
  318. this->ClearErrorInfo();
  319. return DMUS_E_SCRIPT_VALUE_NOT_SUPPORTED;
  320. }
  321. }
  322. else
  323. {
  324. if (fSetRef)
  325. {
  326. // Setting by reference without using an object.
  327. this->ClearErrorInfo();
  328. return DMUS_E_SCRIPT_NOT_A_REFERENCE;
  329. }
  330. }
  331. }
  332. return this->ReturnErrorInfo(hr, pErrorInfo);
  333. }
  334. HRESULT
  335. CActiveScriptManager::GetVariable(const WCHAR *pwszVariableName, VARIANT *pvarValue, DMUS_SCRIPT_ERRORINFO *pErrorInfo)
  336. {
  337. if (!m_pDispatchScript)
  338. {
  339. Trace(1, "Error getting script variable: Script element not initialized.\n");
  340. return DMUS_E_NOT_INIT;
  341. }
  342. assert(pvarValue->vt == VT_EMPTY);
  343. DISPID dispid;
  344. HRESULT hr = this->GetIDOfName(pwszVariableName, &dispid);
  345. if (hr == DISP_E_UNKNOWNNAME)
  346. return DMUS_E_SCRIPT_VARIABLE_NOT_FOUND;
  347. if (FAILED(hr))
  348. return hr;
  349. this->ClearErrorInfo();
  350. DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
  351. EXCEPINFO exinfo;
  352. ZeroMemory(&exinfo, sizeof(EXCEPINFO));
  353. hr = m_pDispatchScript->Invoke(
  354. dispid,
  355. m_fUseOleAut ? IID_NULL : g_guidInvokeWithoutOleaut,
  356. lcidUSEnglish,
  357. DISPATCH_PROPERTYGET,
  358. &dispparamsNoArgs,
  359. pvarValue,
  360. &exinfo,
  361. NULL);
  362. if (hr == DISP_E_EXCEPTION)
  363. this->ContributeErrorInfo(L"getting variable ", pwszVariableName, exinfo);
  364. return this->ReturnErrorInfo(hr, pErrorInfo);
  365. }
  366. HRESULT
  367. CActiveScriptManager::EnumItem(bool fRoutine, DWORD dwIndex, WCHAR *pwszName, int *pcItems)
  368. {
  369. HRESULT hr = this->EnsureEnumItemsCached(fRoutine);
  370. if (FAILED(hr))
  371. return hr;
  372. ScriptNames &snames = fRoutine ? m_snamesRoutines : m_snamesVariables;
  373. DWORD cNames = snames.size();
  374. // snames was allocated for the size of the most items there could be as reported by the script's type info.
  375. // However, the global "DirectMusic" variable may have been skipped, leaving a NULL entry at the end of snames.
  376. if (cNames > 0 && !snames[cNames - 1])
  377. --cNames;
  378. if (pcItems)
  379. *pcItems = cNames;
  380. if (dwIndex >= cNames)
  381. return S_FALSE;
  382. const BSTR bstrName = snames[dwIndex];
  383. if (!bstrName)
  384. {
  385. assert(false);
  386. return S_FALSE;
  387. }
  388. return wcsTruncatedCopy(pwszName, bstrName, MAX_PATH);
  389. }
  390. HRESULT CActiveScriptManager::DispGetIDsOfNames(REFIID riid, LPOLESTR __RPC_FAR *rgszNames, UINT cNames, LCID lcid, DISPID __RPC_FAR *rgDispId)
  391. {
  392. if (!m_pDispatchScript)
  393. {
  394. Trace(1, "Error: Script element not initialized.\n");
  395. return DMUS_E_NOT_INIT;
  396. }
  397. // handle the dummy load method
  398. HRESULT hr = AutLoadDispatchGetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
  399. if (SUCCEEDED(hr))
  400. return hr;
  401. // otherwise defer to the scripting engine
  402. return m_pDispatchScript->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
  403. }
  404. HRESULT CActiveScriptManager::DispInvoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr)
  405. {
  406. if (!m_pDispatchScript)
  407. {
  408. Trace(1, "Error: Script element not initialized.\n");
  409. return DMUS_E_NOT_INIT;
  410. }
  411. // handle the dummy load method
  412. HRESULT hr = AutLoadDispatchInvoke(NULL, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
  413. if (SUCCEEDED(hr))
  414. return hr;
  415. // otherwise defer to the scripting engine...
  416. CActiveScriptManager *pASM = NULL;
  417. hr = CActiveScriptManager::SetCurrentContext(this, &pASM);
  418. if (FAILED(hr))
  419. return hr;
  420. // If this is a property set of an object then we need to report it to garbage collecting loader if present.
  421. // Note that we do this before actually setting the property with Invoke. We do this because if the garbage collector
  422. // fails to track the reference then it won't necessarily keep the target object alive and we don't want to create
  423. // a dangling reference in the script.
  424. if (wFlags & DISPATCH_PROPERTYPUTREF && pDispParams && pDispParams->cArgs == 1)
  425. {
  426. IDirectMusicLoader8P *pLoader8P = m_pParentScript->GetLoader8P();
  427. VARIANT &var = pDispParams->rgvarg[0];
  428. if (pLoader8P && (var.vt == VT_UNKNOWN || var.vt == VT_DISPATCH))
  429. {
  430. hr = pLoader8P->ReportDynamicallyReferencedObject(m_pParentScript, var.vt == VT_UNKNOWN ? var.punkVal : var.pdispVal);
  431. if (FAILED(hr))
  432. return hr;
  433. }
  434. }
  435. hr = m_pDispatchScript->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
  436. bool fExceptionUsingOleAut = !!(riid != g_guidInvokeWithoutOleaut);
  437. if (hr == 0x80020101 && pExcepInfo) // supposedly this is SCRIPT_E_REPORTED
  438. {
  439. // See KB article ID: Q247784, INFO: '80020101' Returned From Some ActiveX Scripting Methods.
  440. // Sometimes VBScript just returns this undocumented HRESULT, which means the error has already been
  441. // reported via OnScriptError. Since it then doesn't give us the exception info via pExcepInfo, we have
  442. // to take the info we saves from OnScriptError and put it back in.
  443. assert(fExceptionUsingOleAut && m_fUseOleAut); // We don't expect this to happen with a custom scripting engine.
  444. assert(!pExcepInfo->bstrSource && !pExcepInfo->bstrDescription && !pExcepInfo->bstrHelpFile); // We don't expect this will happen when the exception info has been filled in.
  445. pExcepInfo->scode = m_hrError;
  446. DMS_SysFreeString(fExceptionUsingOleAut, pExcepInfo->bstrSource);
  447. pExcepInfo->bstrSource = DMS_SysAllocString(fExceptionUsingOleAut, m_bstrErrorSourceComponent);
  448. DMS_SysFreeString(fExceptionUsingOleAut, pExcepInfo->bstrDescription);
  449. pExcepInfo->bstrDescription = DMS_SysAllocString(fExceptionUsingOleAut, m_bstrErrorDescription);
  450. DMS_SysFreeString(fExceptionUsingOleAut, pExcepInfo->bstrHelpFile);
  451. pExcepInfo->bstrHelpFile = DMS_SysAllocString(fExceptionUsingOleAut, m_bstrHelpFile);
  452. hr = DISP_E_EXCEPTION;
  453. }
  454. if (hr == DISP_E_EXCEPTION)
  455. {
  456. // Hack: See packexception.h for more info
  457. PackExceptionFileAndLine(fExceptionUsingOleAut, pExcepInfo, m_pParentScript->GetFilename(), m_fError ? &m_ulErrorLineNumber : NULL);
  458. }
  459. CActiveScriptManager::SetCurrentContext(pASM, NULL);
  460. return hr;
  461. }
  462. void
  463. CActiveScriptManager::Close()
  464. {
  465. if (!m_pActiveScript)
  466. {
  467. assert(false); // Close being called if initialization failed. Or Close was called twice. Or else m_pActiveScript is getting cleared prematurely somehow.
  468. return;
  469. }
  470. HRESULT hr = m_pActiveScript->Close();
  471. assert(SUCCEEDED(hr) && hr != S_FALSE);
  472. SafeRelease(m_pDispatchScript);
  473. SafeRelease(m_pActiveScript);
  474. }
  475. //////////////////////////////////////////////////////////////////////
  476. // IUnknown
  477. STDMETHODIMP
  478. CActiveScriptManager::QueryInterface(const IID &iid, void **ppv)
  479. {
  480. V_INAME(CActiveScriptManager::QueryInterface);
  481. V_PTRPTR_WRITE(ppv);
  482. V_REFGUID(iid);
  483. if (iid == IID_IUnknown || iid == IID_IActiveScriptSite)
  484. {
  485. *ppv = static_cast<IActiveScriptSite*>(this);
  486. }
  487. else
  488. {
  489. *ppv = NULL;
  490. return E_NOINTERFACE;
  491. }
  492. reinterpret_cast<IUnknown*>(this)->AddRef();
  493. return S_OK;
  494. }
  495. STDMETHODIMP_(ULONG)
  496. CActiveScriptManager::AddRef()
  497. {
  498. return InterlockedIncrement(&m_cRef);
  499. }
  500. STDMETHODIMP_(ULONG)
  501. CActiveScriptManager::Release()
  502. {
  503. if (!InterlockedDecrement(&m_cRef))
  504. {
  505. SafeRelease(m_pDispatchScript);
  506. SafeRelease(m_pActiveScript);
  507. delete this;
  508. LockModule(false);
  509. return 0;
  510. }
  511. return m_cRef;
  512. }
  513. //////////////////////////////////////////////////////////////////////
  514. // IActiveScriptSite
  515. STDMETHODIMP
  516. CActiveScriptManager::GetLCID(/* [out] */ LCID __RPC_FAR *plcid)
  517. {
  518. V_INAME(CActiveScriptManager::GetLCID);
  519. V_PTR_WRITE(plcid, LCID);
  520. *plcid = lcidUSEnglish;
  521. return S_OK;
  522. }
  523. STDMETHODIMP
  524. CActiveScriptManager::GetItemInfo(
  525. /* [in] */ LPCOLESTR pstrName,
  526. /* [in] */ DWORD dwReturnMask,
  527. /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppiunkItem,
  528. /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppti)
  529. {
  530. V_INAME(CActiveScriptManager::GetLCID);
  531. V_PTR_WRITE_OPT(ppti, ITypeInfo*);
  532. bool fGetUnknown = !!(dwReturnMask | SCRIPTINFO_IUNKNOWN);
  533. if (fGetUnknown || ppiunkItem)
  534. {
  535. V_PTR_WRITE(ppiunkItem, IUnknown*);
  536. }
  537. if (ppiunkItem)
  538. *ppiunkItem = NULL;
  539. if (ppti)
  540. *ppti = NULL;
  541. if (0 != wcscmp(g_wszGlobalDispatch, pstrName))
  542. {
  543. assert(false); // we should only be asked about the global object
  544. return TYPE_E_ELEMENTNOTFOUND;
  545. }
  546. if (fGetUnknown)
  547. {
  548. IDispatch *pDispGlobal = m_pParentScript->GetGlobalDispatch();
  549. pDispGlobal->AddRef();
  550. *ppiunkItem = pDispGlobal;
  551. }
  552. return S_OK;
  553. }
  554. STDMETHODIMP
  555. CActiveScriptManager::GetDocVersionString(/* [out] */ BSTR __RPC_FAR *pbstrVersion)
  556. {
  557. return E_NOTIMPL; // Not an issue for our scripts that don't persist their state and aren't edited at runtime.
  558. }
  559. STDMETHODIMP
  560. CActiveScriptManager::OnScriptTerminate(
  561. /* [in] */ const VARIANT __RPC_FAR *pvarResult,
  562. /* [in] */ const EXCEPINFO __RPC_FAR *pexcepinfo)
  563. {
  564. if (pexcepinfo)
  565. this->ContributeErrorInfo(L"terminating script", L"", *pexcepinfo);
  566. return S_OK;
  567. }
  568. STDMETHODIMP
  569. CActiveScriptManager::OnStateChange(/* [in] */ SCRIPTSTATE ssScriptState)
  570. {
  571. return S_OK;
  572. }
  573. STDMETHODIMP
  574. CActiveScriptManager::OnScriptError(/* [in] */ IActiveScriptError __RPC_FAR *pscripterror)
  575. {
  576. V_INAME(CActiveScriptManager::OnScriptError);
  577. V_INTERFACE(pscripterror);
  578. BSTR bstrSource = NULL;
  579. pscripterror->GetSourceLineText(&bstrSource); // this may fail, in which case the source text will remain blank
  580. ULONG ulLine = 0;
  581. LONG lChar = 0;
  582. HRESULT hr = pscripterror->GetSourcePosition(NULL, &ulLine, &lChar);
  583. assert(SUCCEEDED(hr));
  584. EXCEPINFO exinfo;
  585. ZeroMemory(&exinfo, sizeof(EXCEPINFO));
  586. hr = pscripterror->GetExceptionInfo(&exinfo);
  587. assert(SUCCEEDED(hr));
  588. this->SetErrorInfo(ulLine, lChar, bstrSource, exinfo);
  589. return S_OK;
  590. }
  591. STDMETHODIMP
  592. CActiveScriptManager::OnEnterScript()
  593. {
  594. return S_OK;
  595. }
  596. STDMETHODIMP
  597. CActiveScriptManager::OnLeaveScript()
  598. {
  599. return S_OK;
  600. }
  601. IDirectMusicPerformance8 *
  602. CActiveScriptManager::GetCurrentPerformanceNoAssertWEAK()
  603. {
  604. CActiveScriptManager *pASM = CActiveScriptManager::GetCurrentContext();
  605. if (!pASM)
  606. return NULL;
  607. return pASM->m_pParentScript->GetPerformance();
  608. }
  609. IDirectMusicObject *
  610. CActiveScriptManager::GetCurrentScriptObjectWEAK()
  611. {
  612. CActiveScriptManager *pASM = CActiveScriptManager::GetCurrentContext();
  613. if (!pASM)
  614. {
  615. assert(false);
  616. return NULL;
  617. }
  618. assert(pASM->m_pParentScript);
  619. return pASM->m_pParentScript;
  620. }
  621. IDirectMusicComposer8 *CActiveScriptManager::GetComposerWEAK()
  622. {
  623. CActiveScriptManager *pASM = CActiveScriptManager::GetCurrentContext();
  624. if (!pASM)
  625. {
  626. assert(false);
  627. return NULL;
  628. }
  629. assert(pASM->m_pParentScript);
  630. return pASM->m_pParentScript->GetComposer();
  631. }
  632. void CActiveScriptManager::GetCurrentTimingContext(__int64 *pi64IntendedStartTime, DWORD *pdwIntendedStartTimeFlags)
  633. {
  634. CActiveScriptManager *pASM = CActiveScriptManager::GetCurrentContext();
  635. if (!pASM)
  636. {
  637. assert(false);
  638. *pi64IntendedStartTime = 0;
  639. *pdwIntendedStartTimeFlags = 0;
  640. }
  641. else
  642. {
  643. *pi64IntendedStartTime = pASM->m_i64IntendedStartTime;
  644. *pdwIntendedStartTimeFlags = pASM->m_dwIntendedStartTimeFlags;
  645. }
  646. }
  647. //////////////////////////////////////////////////////////////////////
  648. // Private functions
  649. HRESULT
  650. CActiveScriptManager::GetIDOfName(const WCHAR *pwszName, DISPID *pdispid)
  651. {
  652. V_INAME(CDirectMusicScript::GetIDOfName);
  653. V_BUFPTR_READ(pwszName, 2);
  654. V_PTR_WRITE(pdispid, DISPID);
  655. if (!m_pDispatchScript)
  656. {
  657. assert(false);
  658. return DMUS_E_NOT_INIT;
  659. }
  660. HRESULT hr = m_pDispatchScript->GetIDsOfNames(
  661. IID_NULL,
  662. const_cast<WCHAR **>(&pwszName),
  663. 1,
  664. lcidUSEnglish,
  665. pdispid);
  666. return hr;
  667. }
  668. // Clears the error info and frees all cached BSTRs.
  669. void
  670. CActiveScriptManager::ClearErrorInfo()
  671. {
  672. m_fError = false;
  673. if (m_bstrErrorSourceComponent)
  674. {
  675. DMS_SysFreeString(m_fUseOleAut, m_bstrErrorSourceComponent);
  676. m_bstrErrorSourceComponent = NULL;
  677. }
  678. if (m_bstrErrorDescription)
  679. {
  680. DMS_SysFreeString(m_fUseOleAut, m_bstrErrorDescription);
  681. m_bstrErrorDescription = NULL;
  682. }
  683. if (m_bstrErrorSourceLineText)
  684. {
  685. DMS_SysFreeString(m_fUseOleAut, m_bstrErrorSourceLineText);
  686. m_bstrErrorSourceLineText = NULL;
  687. }
  688. if (m_bstrHelpFile)
  689. {
  690. DMS_SysFreeString(m_fUseOleAut, m_bstrHelpFile);
  691. m_bstrHelpFile = NULL;
  692. }
  693. }
  694. // Saves the passed error values.
  695. // Assumes ownership of the BSTRs so don't use them after this call since they may be freed!
  696. void
  697. CActiveScriptManager::SetErrorInfo(
  698. ULONG ulLineNumber,
  699. LONG ichCharPosition,
  700. BSTR bstrSourceLine,
  701. const EXCEPINFO &excepinfo)
  702. {
  703. this->ClearErrorInfo();
  704. m_fError = true;
  705. m_hrError = excepinfo.scode;
  706. m_ulErrorLineNumber = ulLineNumber;
  707. m_ichErrorCharPosition = ichCharPosition;
  708. m_bstrErrorSourceComponent = excepinfo.bstrSource;
  709. m_bstrErrorDescription = excepinfo.bstrDescription;
  710. m_bstrErrorSourceLineText = bstrSourceLine;
  711. m_bstrHelpFile = excepinfo.bstrHelpFile;
  712. }
  713. // Sometimes a EXCEPINFO is returned when calling Invoke or on script termination. Although
  714. // there is no source code information, we still want to do our best to set info about
  715. // the error. If OnScriptError has already been called, then calling this function has
  716. // no effect, since we prefer that information.
  717. // Assumes ownership of the BSTRs so don't use them after this call since they may be freed!
  718. void
  719. CActiveScriptManager::ContributeErrorInfo(
  720. const WCHAR *pwszActivity,
  721. const WCHAR *pwszSubject,
  722. const EXCEPINFO &excepinfo)
  723. {
  724. if (m_fError)
  725. {
  726. // Error info already set. Just clear the BSTRs and bail.
  727. if (excepinfo.bstrSource)
  728. DMS_SysFreeString(m_fUseOleAut, excepinfo.bstrSource);
  729. if (excepinfo.bstrDescription)
  730. DMS_SysFreeString(m_fUseOleAut, excepinfo.bstrDescription);
  731. if (excepinfo.bstrHelpFile)
  732. DMS_SysFreeString(m_fUseOleAut, excepinfo.bstrHelpFile);
  733. return;
  734. }
  735. this->SetErrorInfo(0, 0, NULL, excepinfo);
  736. }
  737. // If no error occurred, hr is returned unchanged and pErrorInfo is unaffected.
  738. // If an error did occur, DMUS_E_SCRIPT_ERROR_IN_SCRIPT is returned, the error
  739. // information is saved into pErrorInfo (if nonnull), and the error info is
  740. // cleared for next time.
  741. HRESULT
  742. CActiveScriptManager::ReturnErrorInfo(HRESULT hr, DMUS_SCRIPT_ERRORINFO *pErrorInfo)
  743. {
  744. if (!m_fError)
  745. return hr;
  746. assert(FAILED(hr));
  747. if (pErrorInfo)
  748. {
  749. // We'll fill in a structure with the error info and then copy it to pErrorInfo.
  750. // This is done because it will make things simpler if more fields are added
  751. // to DMUS_SCRIPT_ERRORINFO in the future.
  752. DMUS_SCRIPT_ERRORINFO dmei;
  753. ZeroAndSize(&dmei);
  754. dmei.hr = m_hrError;
  755. dmei.ulLineNumber = m_ulErrorLineNumber;
  756. dmei.ichCharPosition = m_ichErrorCharPosition;
  757. if (m_bstrErrorDescription)
  758. {
  759. // Hack: See packexception.h for more info
  760. UnpackExceptionFileAndLine(m_bstrErrorDescription, &dmei);
  761. }
  762. // The IActiveScript interfaces return zero-based line and column numbers, but we want
  763. // to return them from IDirectMusicScript using a one-based line and column that is
  764. // natural for users.
  765. ++dmei.ulLineNumber;
  766. ++dmei.ichCharPosition;
  767. if (dmei.wszSourceFile[0] == L'\0')
  768. {
  769. // if there was no filename packaged in the description, use this script's filename
  770. const WCHAR *pwszFilename = m_pParentScript->GetFilename();
  771. if (pwszFilename)
  772. wcsTruncatedCopy(dmei.wszSourceFile, pwszFilename, DMUS_MAX_FILENAME);
  773. }
  774. if (m_bstrErrorSourceComponent)
  775. wcsTruncatedCopy(dmei.wszSourceComponent, m_bstrErrorSourceComponent, DMUS_MAX_FILENAME);
  776. if (m_bstrErrorSourceLineText)
  777. wcsTruncatedCopy(dmei.wszSourceLineText, m_bstrErrorSourceLineText, DMUS_MAX_FILENAME);
  778. CopySizedStruct(pErrorInfo, &dmei);
  779. }
  780. this->ClearErrorInfo();
  781. #ifdef DBG
  782. if (pErrorInfo)
  783. {
  784. Trace(1, "Error: Script error in %S, line %u, column %i, near %S. %S: %S. Error code 0x%08X.\n",
  785. pErrorInfo->wszSourceFile,
  786. pErrorInfo->ulLineNumber,
  787. pErrorInfo->ichCharPosition,
  788. pErrorInfo->wszSourceLineText,
  789. pErrorInfo->wszSourceComponent,
  790. pErrorInfo->wszDescription,
  791. pErrorInfo->hr);
  792. }
  793. else
  794. {
  795. Trace(1, "Error: Unknown Script error.\n");
  796. }
  797. #endif
  798. return DMUS_E_SCRIPT_ERROR_IN_SCRIPT;
  799. }
  800. CActiveScriptManager *CActiveScriptManager::GetCurrentContext()
  801. {
  802. DWORD dwThreadId = GetCurrentThreadId();
  803. UINT uiSize = ms_svecContext.size();
  804. for (UINT i = 0; i < uiSize; ++i)
  805. {
  806. if (ms_svecContext[i].dwThreadId == dwThreadId)
  807. break;
  808. }
  809. if (i == uiSize)
  810. return NULL;
  811. return ms_svecContext[i].pActiveScriptManager;
  812. }
  813. HRESULT
  814. CActiveScriptManager::SetCurrentContext(CActiveScriptManager *pActiveScriptManager, CActiveScriptManager **ppActiveScriptManagerPrevious)
  815. {
  816. if (ppActiveScriptManagerPrevious)
  817. *ppActiveScriptManagerPrevious = NULL;
  818. DWORD dwThreadId = GetCurrentThreadId();
  819. UINT uiSize = ms_svecContext.size();
  820. for (UINT i = 0; i < uiSize; ++i)
  821. {
  822. if (ms_svecContext[i].dwThreadId == dwThreadId)
  823. break;
  824. }
  825. if (i == uiSize)
  826. {
  827. // add an entry
  828. if (!ms_svecContext.AccessTo(i))
  829. return E_OUTOFMEMORY;
  830. }
  831. ThreadContextPair &tcp = ms_svecContext[i];
  832. if (i == uiSize)
  833. {
  834. // initialize the new entry
  835. tcp.dwThreadId = dwThreadId;
  836. tcp.pActiveScriptManager = NULL;
  837. }
  838. if (ppActiveScriptManagerPrevious)
  839. *ppActiveScriptManagerPrevious = tcp.pActiveScriptManager;
  840. tcp.pActiveScriptManager = pActiveScriptManager;
  841. return S_OK;
  842. }
  843. HRESULT
  844. CActiveScriptManager::EnsureEnumItemsCached(bool fRoutine)
  845. {
  846. if (!m_pDispatchScript)
  847. {
  848. Trace(1, "Error: Script element not initialized.\n");
  849. return DMUS_E_NOT_INIT;
  850. }
  851. ScriptNames &snames = fRoutine ? m_snamesRoutines : m_snamesVariables;
  852. if (snames)
  853. return S_OK;
  854. UINT uiTypeInfoCount = 0;
  855. HRESULT hr = m_pDispatchScript->GetTypeInfoCount(&uiTypeInfoCount);
  856. if (SUCCEEDED(hr) && !uiTypeInfoCount)
  857. hr = E_NOTIMPL;
  858. if (FAILED(hr))
  859. return hr;
  860. SmartRef::ComPtr<ITypeInfo> scomITypeInfo;
  861. hr = m_pDispatchScript->GetTypeInfo(0, lcidUSEnglish, &scomITypeInfo);
  862. if (FAILED(hr))
  863. return hr;
  864. TYPEATTR *pattr = NULL;
  865. hr = scomITypeInfo->GetTypeAttr(&pattr);
  866. if (FAILED(hr))
  867. return hr;
  868. UINT cMaxItems = fRoutine ? pattr->cFuncs : pattr->cVars;
  869. hr = snames.Init(m_fUseOleAut, cMaxItems);
  870. if (FAILED(hr))
  871. return hr;
  872. // Iterate over the items
  873. DWORD dwCurIndex = 0; // Index position of next name to be saved in our cache
  874. for (UINT i = 0; i < cMaxItems; ++i)
  875. {
  876. FUNCDESC *pfunc = NULL;
  877. VARDESC *pvar = NULL;
  878. MEMBERID memid = DISPID_UNKNOWN;
  879. if (fRoutine)
  880. {
  881. hr = scomITypeInfo->GetFuncDesc(i, &pfunc);
  882. if (FAILED(hr))
  883. break;
  884. if (pfunc->funckind == FUNC_DISPATCH && pfunc->invkind == INVOKE_FUNC && pfunc->cParams == 0)
  885. memid = pfunc->memid;
  886. }
  887. else
  888. {
  889. hr = scomITypeInfo->GetVarDesc(i, &pvar);
  890. if (SUCCEEDED(hr) && pvar->varkind == VAR_DISPATCH)
  891. memid = pvar->memid;
  892. }
  893. if (memid != DISPID_UNKNOWN)
  894. {
  895. UINT cNames = 0;
  896. BSTR bstrName = NULL;
  897. hr = scomITypeInfo->GetNames(memid, &bstrName, 1, &cNames);
  898. if (SUCCEEDED(hr) && cNames == 1 && (fRoutine || 0 != wcscmp(bstrName, g_wszGlobalDispatch)))
  899. snames[dwCurIndex++] = bstrName;
  900. else
  901. DMS_SysFreeString(m_fUseOleAut, bstrName);
  902. }
  903. if (fRoutine)
  904. scomITypeInfo->ReleaseFuncDesc(pfunc);
  905. else
  906. scomITypeInfo->ReleaseVarDesc(pvar);
  907. }
  908. scomITypeInfo->ReleaseTypeAttr(pattr);
  909. return hr;
  910. }