OSDN Git Service

Fix typo in lwdisp.c (#1515)
[winmerge-jp/winmerge-jp.git] / Src / Common / lwdisp.c
1 /* File:        lwdisp.c - light weight dispatch API
2  * Author:      Jochen Tucht 2003/01/09
3  *                      Copyright (C) 2003 herbert dahm datensysteme GmbH
4  *
5  * Purpose:     - Create windows scripting objects (scriptlets)
6  *                      - Invoke methods and access properties
7  *                      - Implement callback interfaces to be invoked by scriptlets
8  *
9  * Remarks:     requires Win32
10  *                      link with oleaut32, shlwapi 4.71
11  *
12  *                      Features not supported by this API include:
13  *                      - type libraries
14  *                      - named arguments
15  *
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.
24  *
25
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
40 */
41
42 //#define _WIN32_IE             0x0300
43 //#define _WIN32_WINNT  0x0400  
44
45 #define NONAMELESSUNION         // avoid warning C4201
46 #define CINTERFACE                      // tell gcc this is "C"
47
48 struct IShellView;                      // avoid MSC warning C4115
49 struct _RPC_ASYNC_STATE;        // avoid MSC warning C4115
50
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.
53 #include <shlobj.h>
54 #pragma warning (pop)
55 #include <shlwapi.h>
56 #include <tchar.h>
57 #include <stdarg.h>
58 #include <strsafe.h>
59 #include "lwdisp.h"
60
61 /**
62 * @brief Display or return error message string (from
63 number)
64 *
65 * @param style: if 0, return sysalloc'd string, else
66 msgbox
67 *
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.
72 */
73 static LPTSTR NTAPI ReportError(HRESULT sc, UINT style)
74 {
75         LPTCH pc = NULL;
76         FormatMessage
77         (
78                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
79                 NULL, sc,
80                 MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
81                 (LPTCH)&pc, 0, NULL
82         );
83         if (pc == NULL)
84         {
85                 FormatMessage
86                 (
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
92                 );
93         }
94         if (style)
95         {
96                 MessageBox(0, pc, 0, style);
97                 LocalFree(pc);
98                 pc = NULL;
99         }
100         return pc;
101 }
102
103 /**
104  * @brief build a formatted message string 
105  */
106 static LPTSTR FormatMessageFromString(LPCTSTR format, ...)
107 {
108         LPTCH pc = NULL;
109         va_list list;
110         va_start(list, format);
111         FormatMessage
112         (
113                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
114                 format, 0,
115                 MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
116                 (LPTCH)&pc, 0, &list
117         );
118         va_end(list);
119         return pc;
120 }
121
122 static void mycpyt2w(LPCTSTR tsz, wchar_t * wdest, size_t limit)
123 {
124 #ifdef _UNICODE
125         StringCchCopyW(wdest, limit, tsz);
126 #else
127         MultiByteToWideChar(CP_ACP, 0, tsz, -1, wdest, (int)limit);
128         // always terminate the string
129         wdest[limit-1] = 0;
130 #endif
131 }
132
133 static void mycpyt2a(LPCTSTR tsz, char * adest, size_t limit)
134 {
135 #ifdef _UNICODE
136         WideCharToMultiByte(CP_ACP, 0, tsz, -1, adest, (int)limit, 0, 0);
137         // always terminate the string
138         adest[limit-1] = 0;
139 #else
140         StringCchCopyA(adest, limit, tsz);
141 #endif
142 }
143
144 #ifdef _WIN64
145 LPDISPATCH CreatDispatchBy32BitProxy(LPCTSTR source, LPCWSTR progid)
146 {
147         CLSID clsid;
148         VARIANT v[2], ret;
149         void *pv = NULL;
150         SCODE sc;
151         wchar_t wpath[512];
152
153         sc = CLSIDFromProgID(L"WinMerge32BitPluginProxy.Loader", &clsid);
154         if (SUCCEEDED(sc))
155                 sc = CoCreateInstance(&clsid, 0, CLSCTX_LOCAL_SERVER|CLSCTX_ACTIVATE_32_BIT_SERVER, &IID_IDispatch, &pv);
156         if (FAILED(sc))
157         {
158                 LPTSTR errorText = ReportError(sc, 0);
159                 LPTSTR tmp;
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);
165                 errorText = tmp;
166                 MessageBox(NULL, errorText, NULL, MB_ICONSTOP|MB_TASKMODAL);
167                 LocalFree(errorText);
168                 return NULL;
169         }
170         VariantInit(&v[0]);
171         VariantInit(&v[1]);
172         VariantInit(&ret);
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);
179         if (SUCCEEDED(sc))
180                 pv = V_DISPATCH(&ret);
181         VariantClear(&v[0]);
182         VariantClear(&v[1]);
183         return pv;
184 }
185 #endif
186
187 LPDISPATCH CreateDispatchBySourceAndCLSID(LPCTSTR source, CLSID *pObjectCLSID)
188 {
189         LPDISPATCH pv = NULL;
190         HMODULE hLibrary = LoadLibrary(source);
191         if (hLibrary)
192         {
193                 HRESULT (NTAPI*DllGetClassObject)(REFCLSID,REFIID,IClassFactory**)
194                         = (HRESULT(NTAPI*)(REFCLSID, REFIID, IClassFactory**))GetProcAddress(hLibrary, "DllGetClassObject");
195                 if (DllGetClassObject)
196                 {
197                         SCODE sc;
198                         IClassFactory *piClassFactory;
199                         if (SUCCEEDED(sc = DllGetClassObject(pObjectCLSID, &IID_IClassFactory, &piClassFactory)))
200                         {
201                                 sc = piClassFactory->lpVtbl->CreateInstance(piClassFactory, 0, &IID_IDispatch, &pv);
202                                 piClassFactory->lpVtbl->Release(piClassFactory);
203                         }
204                 }
205                 if (pv == NULL)
206                         FreeLibrary(hLibrary);
207         }
208         return pv;
209 }
210
211
212 /**
213  * 
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
217  *
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.
220  */
221 LPDISPATCH NTAPI CreateDispatchBySource(LPCTSTR source, LPCWSTR progid)
222 {
223         void *pv = NULL;
224         SCODE sc;
225         WCHAR wc[320];
226         if (source == NULL)
227         {
228                 CLSID clsid;
229                 if (SUCCEEDED(sc=CLSIDFromProgID(progid, &clsid)))
230                 {
231                         sc=CoCreateInstance(&clsid, 0, CLSCTX_ALL, &IID_IDispatch, &pv);
232                 }
233         }
234         else if (PathMatchSpec(source, _T("*.ocx")) || PathMatchSpec(source, _T("*.dll")))
235         {
236                 CLSID objectGUID = {0};
237                 BOOL bGUIDFound = FALSE;
238
239                 // search in the interface of the dll for the CLSID of progid
240                 ITypeLib *piTypeLib;
241                 mycpyt2w(source, wc, DIMOF(wc));
242                 if (SUCCEEDED(sc=LoadTypeLib(wc, &piTypeLib)))
243                 {
244                         UINT count = piTypeLib->lpVtbl->GetTypeInfoCount(piTypeLib);
245                         while (SUCCEEDED(sc) && !bGUIDFound && count--)
246                         {
247                                 ITypeInfo *piTypeInfo;
248                                 if (SUCCEEDED(sc=piTypeLib->lpVtbl->GetTypeInfo(piTypeLib, count, &piTypeInfo)))
249                                 {
250                                         TYPEATTR *pTypeAttr;
251                                         if (SUCCEEDED(sc=piTypeInfo->lpVtbl->GetTypeAttr(piTypeInfo, &pTypeAttr)))
252                                         {
253                                                 BSTR bstrName = 0;
254                                                 if (SUCCEEDED(sc=piTypeInfo->lpVtbl->GetDocumentation(piTypeInfo, MEMBERID_NIL, &bstrName, 0, 0, 0)))
255                                                 {
256                                                         if (pTypeAttr->typekind == TKIND_COCLASS && StrCmpIW(bstrName, progid) == 0)
257                                                         {
258                                                                 memcpy(&objectGUID, &pTypeAttr->guid, sizeof(CLSID));
259                                                                 bGUIDFound = TRUE;
260                                                         }
261                                                         SysFreeString(bstrName);
262                                                 }
263                                                 piTypeInfo->lpVtbl->ReleaseTypeAttr(piTypeInfo, pTypeAttr);
264                                         }
265                                         piTypeInfo->lpVtbl->Release(piTypeInfo);
266                                 }
267                         }
268                         piTypeLib->lpVtbl->Release(piTypeLib);
269                 }
270         
271                 if (bGUIDFound)
272                 {
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
275 #ifdef _WIN64
276                         {
277                         HMODULE hLibrary = LoadLibrary(source);
278                         if (hLibrary == NULL)
279                         {
280                                 // assume 32bit DLL if failed to load DLL
281                                 pv = CreatDispatchBy32BitProxy(source, progid);
282                         }
283                         else
284                         {
285                                 pv = CreateDispatchBySourceAndCLSID(source, &objectGUID);
286                                 FreeLibrary(hLibrary);
287                         }
288                         }
289 #else
290                         pv = CreateDispatchBySourceAndCLSID(source, &objectGUID);
291 #endif
292                 }
293                 // don't display an error message if no interface (normal dll)
294                 if (PathMatchSpec(source, _T("*.dll")) && sc == TYPE_E_CANTLOADLIBRARY)
295                         sc = 0;
296                 // don't display an error message if the format is too old
297                 if (sc == TYPE_E_UNSUPFORMAT)
298                         sc = 0;
299         }
300         else 
301         {
302                 BIND_OPTS bind_opts;
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));
313                 else
314                         mycpyt2w(_T(""), wc, DIMOF(wc));
315                 size_t len = wcslen(wc);
316                 mycpyt2w(source, wc + len, DIMOF(wc) - len);
317
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)
322                 {
323                         // give it a second try after opening within associated application:
324                         SHELLEXECUTEINFO sein;
325                         sein.cbSize = sizeof sein;
326                         sein.hwnd = NULL;
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))
335                         {
336                                 sc=CoGetObject(wc, &bind_opts, &IID_IDispatch, &pv);
337                         }
338                 }
339                 // no error if the interface does not exist
340                 if (sc == MK_E_INTERMEDIATEINTERFACENOTSUPPORTED || sc == E_UNEXPECTED)
341                         sc = 0;
342         }
343         if (FAILED(sc))
344         {
345                 // get the error description
346                 LPTSTR errorText = ReportError(sc, 0);
347                 if (source)
348                 {
349                         // append the source name
350                         LPTSTR tmp;
351                         tmp = FormatMessageFromString(_T("%1\n%2"), errorText, source);
352                         LocalFree(errorText);
353                         errorText = tmp;
354                 }
355                 // report error
356                 MessageBox(0, errorText, 0, MB_ICONSTOP|MB_TASKMODAL);
357                 LocalFree(errorText);
358                 // no valid dispatch
359                 pv = NULL;
360         }
361         return (LPDISPATCH)pv;
362 }
363
364 STDAPI invokeV(LPDISPATCH pi, VARIANT *ret, DISPID id, LPCCH op, VARIANT *argv)
365 {
366         HRESULT sc = E_FAIL;
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))
376         {
377                 dispparams.cNamedArgs = 1;
378                 dispparams.rgdispidNamedArgs = &idNamed;
379         }
380         if (pi != NULL)
381         {
382                 BOOL bParamByRef = FALSE;
383                 BOOL bNeedToConv = FALSE;
384                 VARIANT varParams[12] = { 0 };
385                 VARIANT varData[12] = { 0 };
386                 int i;
387
388                 for (i = 0; i < (int)dispparams.cArgs; i++)
389                 {
390                         if (V_ISBYREF(&argv[i]))
391                         {
392                                 bParamByRef = TRUE;
393                                 break;
394                         }
395                 }
396                 if (bParamByRef)
397                 {
398                         ITypeInfo *pTypeInfo;
399                         HRESULT hr;
400
401                         hr = pi->lpVtbl->GetTypeInfo(pi, 0, 0, &pTypeInfo);
402                         if (SUCCEEDED(hr))
403                         {
404                                 FUNCDESC* pFuncDesc = NULL;
405                                 ITypeInfo2 *pTypeInfo2 = NULL;
406                                 pTypeInfo->lpVtbl->QueryInterface(pTypeInfo, &IID_ITypeInfo2, &pTypeInfo2);
407                                 if (pTypeInfo2 != NULL)
408                                 {
409                                         UINT nIndex;
410                                         hr = pTypeInfo2->lpVtbl->GetFuncIndexOfMemId(pTypeInfo2, id, INVOKE_FUNC, &nIndex);
411                                         if (SUCCEEDED(hr))
412                                         {
413                                                 hr = pTypeInfo->lpVtbl->GetFuncDesc(pTypeInfo, nIndex, &pFuncDesc);
414                                                 if (SUCCEEDED(hr))
415                                                 {
416                                                         if (pFuncDesc->oVft == 0)
417                                                                 bNeedToConv = TRUE;
418                                                         pTypeInfo->lpVtbl->ReleaseFuncDesc(pTypeInfo, pFuncDesc);
419                                                 }
420                                         }
421                                         pTypeInfo2->lpVtbl->Release(pTypeInfo2);
422                                 }
423                                 pTypeInfo->lpVtbl->Release(pTypeInfo);
424                         }
425                 }
426
427                 if (bNeedToConv)
428                 {
429                         for (i = 0; i < (int)dispparams.cArgs; i++)
430                         {
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;
435                         }
436                         dispparams.rgvarg = varParams;
437                 }
438                 else
439                 {
440                         dispparams.rgvarg = argv;
441                 }
442
443                 sc = pi->lpVtbl->Invoke(pi, id, &IID_NULL, 0, wFlags, &dispparams,
444                         ret, &excepInfo, &nArgErr);
445                 if (FAILED(sc))
446                 {
447                         if (excepInfo.pfnDeferredFillIn)
448                         {
449                                 excepInfo.pfnDeferredFillIn(&excepInfo);
450                         }
451                         if (excepInfo.bstrDescription)
452                         {
453                                 MessageBoxW(0, excepInfo.bstrDescription, excepInfo.bstrSource, MB_ICONSTOP|MB_TASKMODAL);
454                         }
455                         else
456                         {
457                                 ReportError(excepInfo.scode == 0 ? sc : excepInfo.scode, MB_ICONSTOP|MB_TASKMODAL);
458                         }
459                         SysFreeString(excepInfo.bstrDescription);
460                         SysFreeString(excepInfo.bstrSource);
461                         SysFreeString(excepInfo.bstrHelpFile);
462                 }
463                 else
464                 {
465                         if (bNeedToConv)
466                         {
467                                 for (i = 0; i < (int)dispparams.cArgs; i++)
468                                 {
469                                         if (V_ISBYREF(&argv[i]))
470                                         {
471                                                 VARIANT varTemp;
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;
481                                                 case VT_BSTR: 
482                                                         SysFreeString(*V_BSTRREF(&argv[i]));
483                                                         *V_BSTRREF(&argv[i]) = V_BSTR(&varTemp);
484                                                         break;
485                                                 }
486                                         }
487                                         VariantClear(&varParams[i]);
488                                 }
489                         }
490                 }
491         }
492         while (dispparams.cArgs--)
493         {
494                 VariantClear(dispparams.rgvarg++);
495         }
496         return sc;
497 }
498
499 HRESULT invokeA(LPDISPATCH pi, VARIANT *ret, DISPID id, LPCCH op, VARIANT *argv)
500 {
501         return invokeV(pi, ret, id, op, argv);
502 }
503 HRESULT invokeW(LPDISPATCH pi, VARIANT *ret, LPCOLESTR silent, LPCCH op, VARIANT *argv)
504 {
505         DISPID id = DISPID_UNKNOWN;
506         LPOLESTR  name = (LPOLESTR )((UINT_PTR)silent & ~1);
507         if (pi != NULL)
508         {
509                 HRESULT sc = pi->lpVtbl->GetIDsOfNames(pi, &IID_NULL, &name, 1, 0, &id);
510                 if (FAILED(sc))
511                 {
512                         if (!((UINT_PTR)silent & 1))
513                         {
514                                 ReportError(sc, MB_ICONSTOP);
515                         }
516                         pi = NULL;
517                 }
518         }
519         return invokeV(pi, ret, id, op, argv);
520 }
521
522 STDAPI ValidateArgs(VARIANT *argv, UINT argc, LPCCH pvt)
523 {
524         VARTYPE vt;
525         while ((vt = (BYTE)*pvt++) != 0)
526         {
527                 if (argc == 0)
528                         return DISP_E_BADPARAMCOUNT;
529                 if (vt == VT_VARIANT)
530                 {
531                         VARIANT *ref = argv;
532                         while (V_VT(ref) == VT_VARIANT + VT_BYREF)
533                         {
534                                 ref = V_VARIANTREF(ref);
535                         }
536                         vt = V_VT(ref);
537                 }
538                 if (vt != VT_VARIANT)
539                 {
540                         if (V_VT(argv) != vt)
541                         {
542                                 HRESULT sc = VariantChangeType(argv, argv, 0, vt);
543                                 if (FAILED(sc))
544                                         return sc;
545                         }
546                         else if (vt == VT_BSTR)
547                         {
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().
556                                 char buffer[520];
557                                 buffer[0] = '\0';
558                                 UINT length =  SysStringByteLen V_BSTR(argv);
559                                 if (length <= sizeof buffer)
560                                 {
561                                         CopyMemory(buffer, V_BSTR(argv), length);
562                                         SysFreeString V_BSTR(argv);
563                                         V_BSTR(argv) = SysAllocStringByteLen(buffer, length);
564                                 }
565                         }
566                 }
567                 --argc;
568                 ++argv;
569         }
570         return argc ? DISP_E_BADPARAMCOUNT : S_OK;
571 }
572
573 /** 
574  * @brief BSTR to PCH (ANSI) conversion
575  * It needs BSTR to be wide (always the case in Windows) 
576  *
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
580  */
581 /*
582 PCH NTAPI B2A(BSTR bcVal)
583 {
584         static const char empty[] = {0};
585         PCH pcVal = (PCH)empty;
586         if (bcVal)
587         {
588                 pcVal = ((PCH)bcVal) + 1;
589                 if (*(PCH)bcVal)
590                 {
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)
594                         *(PCH)bcVal = 0;
595                 }
596         }
597         return pcVal;
598 }
599 */
600
601 // FIX B2A BEFORE UNCOMMENTING THIS ONE
602 /*
603 LPCTSTR NTAPI B2T(BSTR bcVal)
604 {
605 #ifdef _UNICODE
606         return bcVal;
607 #else
608         return B2A(bcVal);
609 #endif
610 }
611 */
612
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))
616 {
617         return DISP_E_MEMBERNOTFOUND;
618 }
619
620 struct LWDispMap
621 {
622         LWDispFxn pfnProc;
623         int count;
624         struct LWDispRef ref[1];
625 };
626
627 static STDMETHODIMP QueryInterface(struct LWDispatch *This,
628                                         /* [in] */ REFIID riid,
629                                         /* [iid_is][out] */ void **ppvObject)
630 {
631         if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch))
632         {
633                 This -> lpVtbl -> AddRef(This);
634                 *ppvObject = This;
635                 return S_OK;
636         }
637         return E_NOINTERFACE;
638 }
639         
640 static ULONG STDMETHODCALLTYPE AddRef(struct LWDispatch *This)
641 {
642         return ++This->refc;
643 }        
644
645 static ULONG STDMETHODCALLTYPE Release(struct LWDispatch *This)
646 {
647         ULONG Release = --This->refc;
648         if (Release == 0)
649         {
650                 free(This);
651         }
652         return Release;
653 }
654
655 static STDMETHODIMP GetTypeInfoCount(struct LWDispatch *UNUSED_ARG(This),
656                                         /* [out] */ UINT *UNUSED_ARG(pctinfo))
657 {
658         return E_NOTIMPL;
659 }
660
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))
665 {
666         return E_NOTIMPL;
667 }
668
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)
675 {
676         DISPID dispIdMember = 0;
677         for ( ; dispIdMember < This->map->count ; ++dispIdMember)
678         {
679                 if (StrCmpIW(This->map->ref[dispIdMember].pwcName, rgszNames[0]) == 0)
680                 {
681                         *rgDispId = dispIdMember;
682                         return S_OK;
683                 }
684         }
685         return DISP_E_MEMBERNOTFOUND;
686 }
687
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))
697 {
698         // provide a dummy result variable if pVarResult == 0
699         VARIANT varEmpty;
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);
705         sc =
706         (
707                 dispIdMember < 0 ? This->map->pfnProc :
708                 dispIdMember < This->map->count ? This->map->ref[dispIdMember].pfnProc :
709                 LWDefProc
710         )
711         (
712                 This->target, sc,
713                 pVarResult ? pVarResult : &varEmpty,
714                 pDispParams->rgvarg, pDispParams->cArgs,
715                 pExcepInfo
716         );
717         VariantClear(&varEmpty);
718         return sc;
719 }
720
721 static const struct LWDispVtbl vtbl =
722 {
723         QueryInterface,
724         AddRef,
725         Release,
726         GetTypeInfoCount,
727         GetTypeInfo,
728         GetIDsOfNames,
729         Invoke
730 };
731
732 const struct LWDispVtbl *NTAPI LWDispSubclass(struct LWDispVtbl *lpVtbl)
733 {
734         *lpVtbl = vtbl;
735         return &vtbl;
736 }
737
738 IDispatch *NTAPI LWDispatch(void *target, const void *disp_map,
739         const struct LWDispVtbl *lpVtbl, struct LWDispatch *This)
740 {
741         if (lpVtbl == NULL)
742                 lpVtbl = &vtbl;
743         if (This == NULL)
744                 This = (struct LWDispatch *)malloc(sizeof(*This));
745         if (This)
746         {
747                 This->lpVtbl = lpVtbl;
748                 This->target = target;
749                 This->map = (const struct LWDispMap *)disp_map;
750                 This->refc = 0;
751         }
752         return (IDispatch *)This;
753 }
754
755 VARIANT NTAPI LWArg(LPDISPATCH pdispVal)
756 {
757         VARIANT v;
758         VariantInit(&v);
759         if (pdispVal) (V_VAR(&v,DISPATCH) = pdispVal) -> lpVtbl -> AddRef(pdispVal);
760         return v;
761 }
762
763 VARIANT NTAPI LWArgW(LPCWSTR wcVal)
764 {
765         VARIANT v;
766         VariantInit(&v);
767         V_VAR(&v,BSTR) = SysAllocString(wcVal);
768         return v;
769 }
770
771 VARIANT NTAPI LWArgA(LPCSTR cVal)
772 {
773         UINT len = lstrlenA(cVal);
774         VARIANT v;
775         VariantInit(&v);
776         V_VAR(&v,BSTR) = SysAllocStringLen(NULL, len);
777         MultiByteToWideChar(CP_ACP, 0, cVal, -1, V_BSTR(&v), len);
778         return v;
779 }
780
781 VARIANT NTAPI LWArgV(UINT vt, ...)
782 {
783         VARIANT v;
784         va_list list;
785         VariantInit(&v);
786         va_start(list, vt);
787         V_VT(&v) = (VARTYPE)(vt & 0xF0FF);
788         CopyMemory(&V_NONE(&v), va_arg(list, void *), (vt & 0x0F00) >> 8);
789         va_end(list);
790         return v;
791 }