From c3869236b814241948b23d2ba8f48469870ece3b Mon Sep 17 00:00:00 2001 From: hugo-fc-visions <220964862+hugo-fc-visions@users.noreply.github.com> Date: Mon, 20 Jul 2026 12:36:10 -0300 Subject: [PATCH] Fix x64 cross-apartment marshaling of IScriptError (Error cast -> E_NOINTERFACE) On x64, casting ScriptControl.Error to the strongly-typed IScriptError/Error interface fails with E_NOINTERFACE when the object is consumed from a COM MTA thread (e.g. a .NET host whose main thread is MTA): Unable to cast COM object ... to interface type 'MSScriptControl.Error' ... QueryInterface ... IID '{70841C78-067D-11D0-95D8-00A02463AB28}' ... E_NOINTERFACE (0x80004002) Root cause: the control is registered ThreadingModel=Apartment (STA). When it is created from an MTA thread, COM returns a proxy, and QueryInterface on that proxy for the IScriptError vtable interface needs marshaling metadata. The type library only declared IScriptControl, never IScriptError, so there was no proxy/stub nor type-library marshaler registered for {70841C78-...} on x64 and the cross-apartment QI failed. (Late-bound/IDispatch access still works because IDispatch is always marshalable; only the strongly-typed vtable cast breaks.) Fix: declare IScriptError in the .idl (dual + oleautomation, with the exact vtable order and dispids CTScriptError already implements) so it becomes part of the type library. The existing DllRegisterServer -> RegisterTypeLib call then registers an oleautomation marshaler for the interface, and the cross-apartment QueryInterface (and the managed cast) succeed. No C++ change: CTScriptError already implements the interface (it uses the #import-ed IScriptError, same IID); tsc64.cpp does not include the MIDL header, so there is no type clash. --- tsc64/tsc64.idl | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tsc64/tsc64.idl b/tsc64/tsc64.idl index 55b5668..1bf2581 100644 --- a/tsc64/tsc64.idl +++ b/tsc64/tsc64.idl @@ -70,6 +70,40 @@ interface IScriptControl : IDispatch [out, retval] VARIANT* pvarResult); }; +// Error object. Declared here (with its real msscript.ocx IID) so the type +// library describes it. RegisterTypeLib then registers an oleautomation +// marshaler for {70841C78-...}, which lets the strongly-typed IScriptError / +// Error interface be marshaled across COM apartments (e.g. an STA-created +// object consumed from a host running on an MTA thread). Members are listed in +// the exact vtable order implemented by CTScriptError. +[ + object, + uuid(70841C78-067D-11D0-95D8-00A02463AB28), + dual, + oleautomation +] +interface IScriptError : IDispatch +{ + [id(0x000000c9), propget] + HRESULT Number([out, retval] long* plNumber); + [id(0x000000ca), propget] + HRESULT Source([out, retval] BSTR* pbstrSource); + [id(0x000000cb), propget] + HRESULT Description([out, retval] BSTR* pbstrDescription); + [id(0x000000cc), propget] + HRESULT HelpFile([out, retval] BSTR* pbstrHelpFile); + [id(0x000000cd), propget] + HRESULT HelpContext([out, retval] long* plHelpContext); + [id(0xfffffdfb), propget] + HRESULT Text([out, retval] BSTR* pbstrText); + [id(0x000000ce), propget] + HRESULT Line([out, retval] long* plLine); + [id(0xfffffdef), propget] + HRESULT Column([out, retval] long* plColumn); + [id(0x000000d0)] + HRESULT Clear(); +}; + // Type library [ version(1.0), @@ -79,4 +113,5 @@ library IScriptControl { importlib("stdole32.tlb"); interface IScriptControl; + interface IScriptError; }; \ No newline at end of file