1 /* File: lwdisp.c - light weight dispatch API
2 * Author: Jochen Tucht 2003/01/09
3 * Copyright (C) 2003 herbert dahm datensysteme GmbH
5 * Purpose: - Create windows scripting objects (scriptlets)
6 * - Invoke methods and access properties
7 * - Implement callback interfaces to be invoked by scriptlets
9 * Remarks: requires Win32
10 * link with oleaut32, shlwapi 4.71
12 * Features not supported by this API include:
16 * License: THIS FILE CONTAINS FREE SOURCE CODE. IT IS PROVIDED *AS IS*, WITHOUT
17 * WARRANTY OF ANY KIND. YOU MAY USE IT AT YOUR OWN RISK, AS LONG AS
18 * YOU KEEP IT IN A SEPARATE FILE AND PRESERVE THIS COMMENT.
19 * CHANGES MUST BE RECORDED IN THE MODIFICATION HISTORY BELOW SO THERE
20 * IS EVIDENCE THAT THE FILE DIFFERS FROM EARLIER RELEASES. THE LEVEL
21 * OF DETAIL IS UP TO YOU. YOU MAY SET THE BY: ENTRY TO "NOBODY@ALL"
22 * IF YOU DON'T WANT TO EXPOSE YOUR NAME. SUBSEQUENT CHANGES MAY BE
23 * REFLECTED BY A SINGLE RECORD CARRYING THE DATE OF THE LATEST CHANGE.
26 DATE: BY: DESCRIPTION:
27 ========== ================== ================================================
28 2003/01/14 J.Tucht provide a way to subclass the default LWDispVtbl
29 2003/03/16 J.Tucht ensure BSTR arguments are writeable
30 2003/05/31 J.Tucht registration of SCT and OCX no longer required
31 2003/08/05 J.Tucht change some names for use with MFC
32 2003/08/31 J.Tucht avoid wnsprintfW to get away with shlwapi < 5.0
33 2003/10/05 J.Tucht allow calls from other threads through HWND
34 2003/11/04 J.Tucht more explicit error messages, SEH
35 2003/11/06 NOBODY@ALL incredible number of changes for unknown reasons
36 2003/11/18 Laoran CreateDispatchBySource : avoid crash if loading dll fails
37 2003/11/18 Laoran CreateDispatchBySource, cosmetic : move dll load&object creation after the CLSID search (= less indentations)
38 2004/01/08 Perry Updated function comment preceding ReportError
39 2008/01/22 Kimmo Changed map argument name to disp_map to not confuse VC6
42 //#define _WIN32_IE 0x0300
43 //#define _WIN32_WINNT 0x0400
45 #define NONAMELESSUNION // avoid warning C4201
46 #define CINTERFACE // tell gcc this is "C"
48 struct IShellView; // avoid MSC warning C4115
49 struct _RPC_ASYNC_STATE; // avoid MSC warning C4115
51 #pragma warning (push) // prevent "warning C4091: 'typedef ': ignored on left of 'tagGPFIDL_FLAGS' when no variable is declared"
52 #pragma warning (disable:4091) // VC bug when using XP enabled toolsets.
62 * @brief Display or return error message string (from
65 * @param style: if 0, return sysalloc'd string, else
68 * Calls FormatMessage to get description of system string.
69 * If not found, makes a string containing raw number.
70 * If msgbox (style!=0), then function returns 0.
71 * If not msgbox (style==0), caller must LocalFree string.
73 static LPTSTR NTAPI ReportError(HRESULT sc, UINT style)
78 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
80 MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
87 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING |
88 FORMAT_MESSAGE_ARGUMENT_ARRAY,
89 _T("Error 0x%1!lX!"), 0,
90 MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
91 (LPTCH)&pc, 0, (va_list *)&sc
96 MessageBox(0, pc, 0, style);
104 * @brief build a formatted message string
106 static LPTSTR FormatMessageFromString(LPCTSTR format, ...)
110 va_start(list, format);
113 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
115 MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
122 static void mycpyt2w(LPCTSTR tsz, wchar_t * wdest, size_t limit)
125 StringCchCopyW(wdest, limit, tsz);
127 MultiByteToWideChar(CP_ACP, 0, tsz, -1, wdest, (int)limit);
128 // always terminate the string
133 static void mycpyt2a(LPCTSTR tsz, char * adest, size_t limit)
136 WideCharToMultiByte(CP_ACP, 0, tsz, -1, adest, (int)limit, 0, 0);
137 // always terminate the string
140 StringCchCopyA(adest, limit, tsz);
145 LPDISPATCH CreatDispatchBy32BitProxy(LPCTSTR source, LPCWSTR progid)
153 sc = CLSIDFromProgID(L"WinMerge32BitPluginProxy.Loader", &clsid);
155 sc = CoCreateInstance(&clsid, 0, CLSCTX_LOCAL_SERVER|CLSCTX_ACTIVATE_32_BIT_SERVER, &IID_IDispatch, &pv);
158 LPTSTR errorText = ReportError(sc, 0);
160 tmp = FormatMessageFromString(_T("32bitプラグイン(%1)のロードに失敗しました。:%2\n")
161 _T("WinMerge32BitPluginProxy.exeが登録されていないかもしれません。\n")
162 _T("管理者権限のコマンドプロンプトで以下を実行してみてください。\n\n")
163 _T("\"{WinMergeインストールパス}\\WinMerge32BitPluginProxy.exe\" /RegServer"), source, errorText);
164 LocalFree(errorText);
166 MessageBox(NULL, errorText, NULL, MB_ICONSTOP|MB_TASKMODAL);
167 LocalFree(errorText);
173 V_VT(&v[1]) = VT_BSTR;
174 mycpyt2w(source, wpath, DIMOF(wpath));
175 V_BSTR(&v[1]) = SysAllocString(wpath);
176 V_VT(&v[0]) = VT_BSTR;
177 V_BSTR(&v[0]) = SysAllocString(progid);
178 sc = invokeW(pv, &ret, L"Load", opFxn[2], v);
180 pv = V_DISPATCH(&ret);
187 LPDISPATCH CreateDispatchBySourceAndCLSID(LPCTSTR source, CLSID *pObjectCLSID)
189 LPDISPATCH pv = NULL;
190 HMODULE hLibrary = LoadLibrary(source);
193 HRESULT (NTAPI*DllGetClassObject)(REFCLSID,REFIID,IClassFactory**)
194 = (HRESULT(NTAPI*)(REFCLSID, REFIID, IClassFactory**))GetProcAddress(hLibrary, "DllGetClassObject");
195 if (DllGetClassObject)
198 IClassFactory *piClassFactory;
199 if (SUCCEEDED(sc = DllGetClassObject(pObjectCLSID, &IID_IClassFactory, &piClassFactory)))
201 sc = piClassFactory->lpVtbl->CreateInstance(piClassFactory, 0, &IID_IDispatch, &pv);
202 piClassFactory->lpVtbl->Release(piClassFactory);
206 FreeLibrary(hLibrary);
214 * @Note We can use this code with unregistered COM DLL
215 * For VC++ DLL, we need a custom CComTypeInfoHolder as the default one search the registry
216 * For VB DLL, instance can not be shared across thread, one must be created for each thread
218 * Don't catch unknown errors in this function, because we want to catch
219 * both C++ and C errors, and this is a C file.
221 LPDISPATCH NTAPI CreateDispatchBySource(LPCTSTR source, LPCWSTR progid)
229 if (SUCCEEDED(sc=CLSIDFromProgID(progid, &clsid)))
231 sc=CoCreateInstance(&clsid, 0, CLSCTX_ALL, &IID_IDispatch, &pv);
234 else if (PathMatchSpec(source, _T("*.ocx")) || PathMatchSpec(source, _T("*.dll")))
236 CLSID objectGUID = {0};
237 BOOL bGUIDFound = FALSE;
239 // search in the interface of the dll for the CLSID of progid
241 mycpyt2w(source, wc, DIMOF(wc));
242 if (SUCCEEDED(sc=LoadTypeLib(wc, &piTypeLib)))
244 UINT count = piTypeLib->lpVtbl->GetTypeInfoCount(piTypeLib);
245 while (SUCCEEDED(sc) && !bGUIDFound && count--)
247 ITypeInfo *piTypeInfo;
248 if (SUCCEEDED(sc=piTypeLib->lpVtbl->GetTypeInfo(piTypeLib, count, &piTypeInfo)))
251 if (SUCCEEDED(sc=piTypeInfo->lpVtbl->GetTypeAttr(piTypeInfo, &pTypeAttr)))
254 if (SUCCEEDED(sc=piTypeInfo->lpVtbl->GetDocumentation(piTypeInfo, MEMBERID_NIL, &bstrName, 0, 0, 0)))
256 if (pTypeAttr->typekind == TKIND_COCLASS && StrCmpIW(bstrName, progid) == 0)
258 memcpy(&objectGUID, &pTypeAttr->guid, sizeof(CLSID));
261 SysFreeString(bstrName);
263 piTypeInfo->lpVtbl->ReleaseTypeAttr(piTypeInfo, pTypeAttr);
265 piTypeInfo->lpVtbl->Release(piTypeInfo);
268 piTypeLib->lpVtbl->Release(piTypeLib);
273 // we have found the CLSID, so this is really a COM dll for WinMerge
274 // now try to load the dll and to create an instance of the object
277 HMODULE hLibrary = LoadLibrary(source);
278 if (hLibrary == NULL)
280 // assume 32bit DLL if failed to load DLL
281 pv = CreatDispatchBy32BitProxy(source, progid);
285 pv = CreateDispatchBySourceAndCLSID(source, &objectGUID);
286 FreeLibrary(hLibrary);
290 pv = CreateDispatchBySourceAndCLSID(source, &objectGUID);
293 // don't display an error message if no interface (normal dll)
294 if (PathMatchSpec(source, _T("*.dll")) && sc == TYPE_E_CANTLOADLIBRARY)
296 // don't display an error message if the format is too old
297 if (sc == TYPE_E_UNSUPFORMAT)
303 // initialize to official defaults:
304 bind_opts.cbStruct = sizeof bind_opts;
305 bind_opts.grfFlags = 0;
306 bind_opts.grfMode = STGM_READWRITE;
307 bind_opts.dwTickCountDeadline = 0;
308 // prepend appropriate moniker:
309 if (PathIsContentType(source, _T("text/scriptlet"))
310 || PathMatchSpec(source, _T("*.sct"))
311 || PathMatchSpec(source, _T("*.wsc")))
312 mycpyt2w(_T("script:"), wc, DIMOF(wc));
314 mycpyt2w(_T(""), wc, DIMOF(wc));
315 size_t len = wcslen(wc);
316 mycpyt2w(source, wc + len, DIMOF(wc) - len);
318 // I observed that CoGetObject() may internally provoke an access
319 // violation and succeed anyway. No idea how to avoid this.
320 sc=CoGetObject(wc, &bind_opts, &IID_IDispatch, &pv);
321 if (sc == E_NOINTERFACE)
323 // give it a second try after opening within associated application:
324 SHELLEXECUTEINFO sein;
325 sein.cbSize = sizeof sein;
327 // SEE_MASK_FLAG_DDEWAIT: wait until application is ready to listen
328 sein.fMask = SEE_MASK_FLAG_DDEWAIT;
329 sein.lpVerb = _T("open");
330 sein.lpFile = source;
331 sein.lpParameters = NULL;
332 sein.lpDirectory = _T(".");
333 sein.nShow = SW_SHOWNORMAL;
334 if (ShellExecuteEx(&sein))
336 sc=CoGetObject(wc, &bind_opts, &IID_IDispatch, &pv);
339 // no error if the interface does not exist
340 if (sc == MK_E_INTERMEDIATEINTERFACENOTSUPPORTED || sc == E_UNEXPECTED)
345 // get the error description
346 LPTSTR errorText = ReportError(sc, 0);
349 // append the source name
351 tmp = FormatMessageFromString(_T("%1\n%2"), errorText, source);
352 LocalFree(errorText);
356 MessageBox(0, errorText, 0, MB_ICONSTOP|MB_TASKMODAL);
357 LocalFree(errorText);
361 return (LPDISPATCH)pv;
364 STDAPI invokeV(LPDISPATCH pi, VARIANT *ret, DISPID id, LPCCH op, VARIANT *argv)
367 DISPID idNamed = DISPID_PROPERTYPUT;
368 WORD wFlags = HIBYTE((UINT_PTR)op);
369 DISPPARAMS dispparams;
370 UINT nArgErr = (UINT)-1;
371 EXCEPINFO excepInfo = {0};
372 dispparams.cArgs = LOBYTE((UINT_PTR)op);
373 dispparams.cNamedArgs = 0;
374 dispparams.rgvarg = argv;
375 if (wFlags & (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF))
377 dispparams.cNamedArgs = 1;
378 dispparams.rgdispidNamedArgs = &idNamed;
382 BOOL bParamByRef = FALSE;
383 BOOL bNeedToConv = FALSE;
384 VARIANT varParams[12] = { 0 };
385 VARIANT varData[12] = { 0 };
388 for (i = 0; i < (int)dispparams.cArgs; i++)
390 if (V_ISBYREF(&argv[i]))
398 ITypeInfo *pTypeInfo;
401 hr = pi->lpVtbl->GetTypeInfo(pi, 0, 0, &pTypeInfo);
404 FUNCDESC* pFuncDesc = NULL;
405 ITypeInfo2 *pTypeInfo2 = NULL;
406 pTypeInfo->lpVtbl->QueryInterface(pTypeInfo, &IID_ITypeInfo2, &pTypeInfo2);
407 if (pTypeInfo2 != NULL)
410 hr = pTypeInfo2->lpVtbl->GetFuncIndexOfMemId(pTypeInfo2, id, INVOKE_FUNC, &nIndex);
413 hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, nIndex, &pFuncDesc);
416 if (pFuncDesc->oVft == 0)
418 pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc);
421 pTypeInfo2->lpVtbl->Release(pTypeInfo2);
423 pTypeInfo->lpVtbl->Release(pTypeInfo);
429 for (i = 0; i < (int)dispparams.cArgs; i++)
431 VariantInit(&varData[i]);
432 VariantCopyInd(&varData[i], &argv[i]);
433 V_VARIANTREF(&varParams[i]) = &varData[i];
434 V_VT(&varParams[i]) = VT_VARIANT | VT_BYREF;
436 dispparams.rgvarg = varParams;
440 dispparams.rgvarg = argv;
443 sc = pi->lpVtbl->Invoke(pi, id, &IID_NULL, 0, wFlags, &dispparams,
444 ret, &excepInfo, &nArgErr);
447 if (excepInfo.pfnDeferredFillIn)
449 excepInfo.pfnDeferredFillIn(&excepInfo);
451 if (excepInfo.bstrDescription)
453 MessageBoxW(0, excepInfo.bstrDescription, excepInfo.bstrSource, MB_ICONSTOP|MB_TASKMODAL);
457 ReportError(excepInfo.scode == 0 ? sc : excepInfo.scode, MB_ICONSTOP|MB_TASKMODAL);
459 SysFreeString(excepInfo.bstrDescription);
460 SysFreeString(excepInfo.bstrSource);
461 SysFreeString(excepInfo.bstrHelpFile);
467 for (i = 0; i < (int)dispparams.cArgs; i++)
469 if (V_ISBYREF(&argv[i]))
472 VariantInit(&varTemp);
473 VariantChangeType(&varTemp, &varData[i], 0, (unsigned short)(V_VT(&argv[i]) & ~VT_BYREF));
474 switch(V_VT(&varTemp)) {
475 case VT_BOOL: *V_BOOLREF(&argv[i]) = V_BOOL(&varTemp); break;
476 case VT_I1: *V_I2REF(&argv[i]) = V_I1(&varTemp); break;
477 case VT_I2: *V_I2REF(&argv[i]) = V_I2(&varTemp); break;
478 case VT_I4: *V_I4REF(&argv[i]) = V_I4(&varTemp); break;
479 case VT_R4: *V_R4REF(&argv[i]) = V_R4(&varTemp); break;
480 case VT_R8: *V_R8REF(&argv[i]) = V_R8(&varTemp); break;
482 SysFreeString(*V_BSTRREF(&argv[i]));
483 *V_BSTRREF(&argv[i]) = V_BSTR(&varTemp);
487 VariantClear(&varParams[i]);
492 while (dispparams.cArgs--)
494 VariantClear(dispparams.rgvarg++);
499 HRESULT invokeA(LPDISPATCH pi, VARIANT *ret, DISPID id, LPCCH op, VARIANT *argv)
501 return invokeV(pi, ret, id, op, argv);
503 HRESULT invokeW(LPDISPATCH pi, VARIANT *ret, LPCOLESTR silent, LPCCH op, VARIANT *argv)
505 DISPID id = DISPID_UNKNOWN;
506 LPOLESTR name = (LPOLESTR )((UINT_PTR)silent & ~1);
509 HRESULT sc = pi->lpVtbl->GetIDsOfNames(pi, &IID_NULL, &name, 1, 0, &id);
512 if (!((UINT_PTR)silent & 1))
514 ReportError(sc, MB_ICONSTOP);
519 return invokeV(pi, ret, id, op, argv);
522 STDAPI ValidateArgs(VARIANT *argv, UINT argc, LPCCH pvt)
525 while ((vt = (BYTE)*pvt++) != 0)
528 return DISP_E_BADPARAMCOUNT;
529 if (vt == VT_VARIANT)
532 while (V_VT(ref) == VT_VARIANT + VT_BYREF)
534 ref = V_VARIANTREF(ref);
538 if (vt != VT_VARIANT)
540 if (V_VT(argv) != vt)
542 HRESULT sc = VariantChangeType(argv, argv, 0, vt);
546 else if (vt == VT_BSTR)
548 // Const strings passed in from Compiled VB may reside in r/o
549 // memory. Since B2A() expects BSTR arguments to be writeable,
550 // the following code copies short strings to writeable memory.
551 // Long strings are assumed to be writeable anyway since they
552 // are normally a result of some non-const expression.
553 // This is a tradeoff between efficiency and safety.
554 // A const string longer than 260 OLECHARs (520 bytes) will
555 // provoke an access violation in B2A().
558 UINT length = SysStringByteLen V_BSTR(argv);
559 if (length <= sizeof buffer)
561 CopyMemory(buffer, V_BSTR(argv), length);
562 SysFreeString V_BSTR(argv);
563 V_BSTR(argv) = SysAllocStringByteLen(buffer, length);
570 return argc ? DISP_E_BADPARAMCOUNT : S_OK;
574 * @brief BSTR to PCH (ANSI) conversion
575 * It needs BSTR to be wide (always the case in Windows)
577 * @note THIS METHOD IS BUGGY : IT IS NOT CORRECT TO PUT A CHAR STRING IN A WCHAR BUFFER
578 * IN FACT IN PLACE TRANSFORMATION OF WCHAR TO CHAR IS NOT CORRECT
579 * the content of bcVal is changed
582 PCH NTAPI B2A(BSTR bcVal)
584 static const char empty[] = {0};
585 PCH pcVal = (PCH)empty;
588 pcVal = ((PCH)bcVal) + 1;
591 int cch = lstrlenW(bcVal) + 1;
592 WideCharToMultiByte(CP_ACP, 0, bcVal, cch, pcVal, cch, 0, 0);
593 // this is buggy : we need to set *bcVal = 0 (so we use pcVal = ((PCH)bcVal) + 2)
601 // FIX B2A BEFORE UNCOMMENTING THIS ONE
603 LPCTSTR NTAPI B2T(BSTR bcVal)
613 STDAPI LWDefProc(PVOID UNUSED_ARG(target), HRESULT UNUSED_ARG(sc),
614 VARIANT *UNUSED_ARG(ret), VARIANT *UNUSED_ARG(argv), UINT UNUSED_ARG(argc),
615 EXCEPINFO *UNUSED_ARG(e))
617 return DISP_E_MEMBERNOTFOUND;
624 struct LWDispRef ref[1];
627 static STDMETHODIMP QueryInterface(struct LWDispatch *This,
628 /* [in] */ REFIID riid,
629 /* [iid_is][out] */ void **ppvObject)
631 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch))
633 This -> lpVtbl -> AddRef(This);
637 return E_NOINTERFACE;
640 static ULONG STDMETHODCALLTYPE AddRef(struct LWDispatch *This)
645 static ULONG STDMETHODCALLTYPE Release(struct LWDispatch *This)
647 ULONG Release = --This->refc;
655 static STDMETHODIMP GetTypeInfoCount(struct LWDispatch *UNUSED_ARG(This),
656 /* [out] */ UINT *UNUSED_ARG(pctinfo))
661 static STDMETHODIMP GetTypeInfo(struct LWDispatch *UNUSED_ARG(This),
662 /* [in] */ UINT UNUSED_ARG(iTInfo),
663 /* [in] */ LCID UNUSED_ARG(lcid),
664 /* [out] */ ITypeInfo **UNUSED_ARG(ppTInfo))
669 static STDMETHODIMP GetIDsOfNames(struct LWDispatch *This,
670 /* [in] */ REFIID UNUSED_ARG(riid),
671 /* [size_is][in] */ LPOLESTR *rgszNames,
672 /* [in] */ UINT UNUSED_ARG(cNames),
673 /* [in] */ LCID UNUSED_ARG(lcid),
674 /* [size_is][out] */ DISPID *rgDispId)
676 DISPID dispIdMember = 0;
677 for ( ; dispIdMember < This->map->count ; ++dispIdMember)
679 if (StrCmpIW(This->map->ref[dispIdMember].pwcName, rgszNames[0]) == 0)
681 *rgDispId = dispIdMember;
685 return DISP_E_MEMBERNOTFOUND;
688 static STDMETHODIMP Invoke(struct LWDispatch *This,
689 /* [in] */ DISPID dispIdMember,
690 /* [in] */ REFIID UNUSED_ARG(riid),
691 /* [in] */ LCID UNUSED_ARG(lcid),
692 /* [in] */ WORD wFlags,
693 /* [out][in] */ DISPPARAMS *pDispParams,
694 /* [out] */ VARIANT *pVarResult,
695 /* [out] */ EXCEPINFO *pExcepInfo,
696 /* [out] */ UINT *UNUSED_ARG(puArgErr))
698 // provide a dummy result variable if pVarResult == 0
700 HRESULT sc = dispIdMember < 0 ? dispIdMember : (HRESULT)wFlags;
701 if (pDispParams->cNamedArgs >
702 ((wFlags & (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF)) ? 1U : 0U))
703 return DISP_E_NONAMEDARGS;
704 VariantInit(&varEmpty);
707 dispIdMember < 0 ? This->map->pfnProc :
708 dispIdMember < This->map->count ? This->map->ref[dispIdMember].pfnProc :
713 pVarResult ? pVarResult : &varEmpty,
714 pDispParams->rgvarg, pDispParams->cArgs,
717 VariantClear(&varEmpty);
721 static const struct LWDispVtbl vtbl =
732 const struct LWDispVtbl *NTAPI LWDispSubclass(struct LWDispVtbl *lpVtbl)
738 IDispatch *NTAPI LWDispatch(void *target, const void *disp_map,
739 const struct LWDispVtbl *lpVtbl, struct LWDispatch *This)
744 This = (struct LWDispatch *)malloc(sizeof(*This));
747 This->lpVtbl = lpVtbl;
748 This->target = target;
749 This->map = (const struct LWDispMap *)disp_map;
752 return (IDispatch *)This;
755 VARIANT NTAPI LWArg(LPDISPATCH pdispVal)
759 if (pdispVal) (V_VAR(&v,DISPATCH) = pdispVal) -> lpVtbl -> AddRef(pdispVal);
763 VARIANT NTAPI LWArgW(LPCWSTR wcVal)
767 V_VAR(&v,BSTR) = SysAllocString(wcVal);
771 VARIANT NTAPI LWArgA(LPCSTR cVal)
773 UINT len = lstrlenA(cVal);
776 V_VAR(&v,BSTR) = SysAllocStringLen(NULL, len);
777 MultiByteToWideChar(CP_ACP, 0, cVal, -1, V_BSTR(&v), len);
781 VARIANT NTAPI LWArgV(UINT vt, ...)
787 V_VT(&v) = (VARTYPE)(vt & 0xF0FF);
788 CopyMemory(&V_NONE(&v), va_arg(list, void *), (vt & 0x0F00) >> 8);