"Fossies" - the Fresh Open Source Software Archive

Member "AutoHotkey_L-1.1.33.09/source/script_com.cpp" (8 May 2021, 49812 Bytes) of package /windows/misc/AutoHotkey_L-1.1.33.09.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "script_com.cpp" see the Fossies "Dox" file reference documentation.

    1 #include "stdafx.h"
    2 #include "globaldata.h"
    3 #include "script.h"
    4 #include "script_object.h"
    5 #include "script_com.h"
    6 #include "script_func_impl.h"
    7 #include <DispEx.h>
    8 
    9 
   10 // IID__IObject -- .NET's System.Object:
   11 const IID IID__Object = {0x65074F7F, 0x63C0, 0x304E, 0xAF, 0x0A, 0xD5, 0x17, 0x41, 0xCB, 0x4A, 0x8D};
   12 
   13 // Identifies an AutoHotkey object which was passed to a COM API and back again:
   14 const IID IID_IObjectComCompatible = { 0x619f7e25, 0x6d89, 0x4eb4, 0xb2, 0xfb, 0x18, 0xe7, 0xc7, 0x3c, 0xe, 0xa6 };
   15 
   16 
   17 
   18 BIF_DECL(BIF_ComObjCreate)
   19 {
   20     HRESULT hr;
   21     CLSID clsid, iid;
   22     for (;;)
   23     {
   24 #ifdef UNICODE
   25         LPTSTR cls = TokenToString(*aParam[0]);
   26 #else
   27         CStringWCharFromTChar cls = TokenToString(*aParam[0]);
   28 #endif
   29         // It has been confirmed on Windows 10 that both CLSIDFromString and CLSIDFromProgID
   30         // were unable to resolve a ProgID starting with '{', like "{Foo", though "Foo}" works.
   31         // There are probably also guidelines and such that prohibit it.
   32         if (cls[0] == '{')
   33             hr = CLSIDFromString(cls, &clsid);
   34         else
   35             // CLSIDFromString is known to be able to resolve ProgIDs via the registry,
   36             // but fails on registration-free classes such as "Microsoft.Windows.ActCtx".
   37             // CLSIDFromProgID works for that, but fails when given a CLSID string
   38             // (consistent with VBScript and JScript in both cases).
   39             hr = CLSIDFromProgID(cls, &clsid);
   40         if (FAILED(hr)) break;
   41 
   42         if (aParamCount > 1)
   43         {
   44             hr = CLSIDFromString(CStringWCharFromTCharIfNeeded(TokenToString(*aParam[1])), &iid);
   45             if (FAILED(hr)) break;
   46             
   47             IUnknown *punk;
   48             hr = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, iid, (void **)&punk);
   49             if (FAILED(hr)) break;
   50 
   51             // Return interface pointer, as requested.
   52             aResultToken.symbol = SYM_INTEGER;
   53             aResultToken.value_int64 = (__int64)punk;
   54         }
   55         else
   56         {
   57             IDispatch *pdisp;
   58             hr = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void **)&pdisp);
   59             if (FAILED(hr)) break;
   60             
   61             // Return dispatchable object.
   62             if ( !(aResultToken.object = new ComObject(pdisp)) )
   63             {
   64                 pdisp->Release();
   65                 hr = E_OUTOFMEMORY;
   66                 break;
   67             }
   68             aResultToken.symbol = SYM_OBJECT;
   69         }
   70         return;
   71     }
   72     aResultToken.symbol = SYM_STRING;
   73     aResultToken.marker = _T("");
   74     ComError(hr);
   75 }
   76 
   77 
   78 BIF_DECL(BIF_ComObjGet)
   79 {
   80     HRESULT hr;
   81     IDispatch *pdisp;
   82     hr = CoGetObject(CStringWCharFromTCharIfNeeded(TokenToString(*aParam[0])), NULL, IID_IDispatch, (void **)&pdisp);
   83     if (SUCCEEDED(hr))
   84     {
   85         if (aResultToken.object = new ComObject(pdisp))
   86         {
   87             aResultToken.symbol = SYM_OBJECT;
   88             return;
   89         }
   90         hr = E_OUTOFMEMORY;
   91         pdisp->Release();
   92     }
   93     aResultToken.symbol = SYM_STRING;
   94     aResultToken.marker = _T("");
   95     ComError(hr);
   96 }
   97 
   98 
   99 BIF_DECL(BIF_ComObjActive)
  100 {
  101     if (!aParamCount) // ComObjMissing()
  102     {
  103         SafeSetTokenObject(aResultToken, new ComObject(DISP_E_PARAMNOTFOUND, VT_ERROR));
  104         return;
  105     }
  106 
  107     aResultToken.symbol = SYM_STRING;
  108     aResultToken.marker = _T("");
  109 
  110     ComObject *obj;
  111 
  112     if (TokenIsPureNumeric(*aParam[0]))
  113     {
  114         VARTYPE vt;
  115         __int64 llVal;
  116         USHORT flags = 0;
  117 
  118         if (aParamCount > 1)
  119         {
  120             // ComObj(vt, value [, flags])
  121             vt = (VARTYPE)TokenToInt64(*aParam[0]);
  122             llVal = TokenToInt64(*aParam[1]);
  123             if (aParamCount > 2)
  124                 flags = (USHORT)TokenToInt64(*aParam[2]);
  125         }
  126         else
  127         {
  128             // ComObj(pdisp)
  129             vt = VT_DISPATCH;
  130             llVal = TokenToInt64(*aParam[0]);
  131         }
  132         
  133         if (vt == VT_DISPATCH || vt == VT_UNKNOWN)
  134         {
  135             IUnknown *punk = (IUnknown *)llVal;
  136             if (punk)
  137             {
  138                 if (aParamCount == 1) // Implies above set vt = VT_DISPATCH.
  139                 {
  140                     IDispatch *pdisp;
  141                     if (SUCCEEDED(punk->QueryInterface(IID_IDispatch, (void **)&pdisp)))
  142                     {
  143                         // Replace caller-specified interface pointer with pdisp.  If caller
  144                         // has requested we take responsibility for freeing it, do that now:
  145                         if (flags & ComObject::F_OWNVALUE)
  146                             punk->Release();
  147                         flags |= ComObject::F_OWNVALUE; // Don't AddRef() below since we own this reference.
  148                         llVal = (__int64)pdisp;
  149                     }
  150                     // Otherwise interpret it as IDispatch anyway, since caller has requested it and
  151                     // there are known cases where it works (such as some CLR COM callable wrappers).
  152                 }
  153                 if ( !(flags & ComObject::F_OWNVALUE) )
  154                     punk->AddRef(); // "Copy" caller's reference.
  155                 // Otherwise caller (or above) indicated the object now owns this reference.
  156             }
  157             // Otherwise, NULL may have some meaning, so allow it.  If the
  158             // script tries to invoke the object, it'll get a warning then.
  159         }
  160 
  161         if (obj = new ComObject(llVal, vt, flags))
  162         {
  163             aResultToken.symbol = SYM_OBJECT;
  164             aResultToken.object = obj;
  165         }
  166         else if (vt == VT_DISPATCH || vt == VT_UNKNOWN)
  167             ((IUnknown *)llVal)->Release();
  168     }
  169     else if (obj = dynamic_cast<ComObject *>(TokenToObject(*aParam[0])))
  170     {
  171         if (aParamCount > 1)
  172         {
  173             // For backward-compatibility:
  174             aResultToken.symbol = SYM_INTEGER;
  175             aResultToken.marker = _T("ComObjType");
  176             BIF_ComObjTypeOrValue(aResult, aResultToken, aParam, aParamCount);
  177         }
  178         else if (VT_DISPATCH == obj->mVarType)
  179         {
  180             aResultToken.symbol = SYM_INTEGER;
  181             aResultToken.value_int64 = (__int64) obj->mDispatch; // mDispatch vs mVal64 ensures we zero the high 32 bits in 32-bit builds.
  182             if (obj->mDispatch)
  183                 obj->mDispatch->AddRef();
  184         }
  185     }
  186     else
  187     {
  188         HRESULT hr;
  189         CLSID clsid;
  190         IUnknown *punk;
  191         hr = CLSIDFromString(CStringWCharFromTCharIfNeeded(TokenToString(*aParam[0])), &clsid);
  192         if (SUCCEEDED(hr))
  193         {
  194             hr = GetActiveObject(clsid, NULL, &punk);
  195             if (SUCCEEDED(hr))
  196             {
  197                 IDispatch *pdisp;
  198                 hr = punk->QueryInterface(IID_IDispatch, (void **)&pdisp);
  199                 punk->Release();
  200                 if (SUCCEEDED(hr))
  201                 {
  202                     if (obj = new ComObject(pdisp))
  203                     {
  204                         aResultToken.symbol = SYM_OBJECT;
  205                         aResultToken.object = obj;
  206                         return;
  207                     }
  208                     hr = E_OUTOFMEMORY;
  209                     pdisp->Release();
  210                 }
  211             }
  212         }
  213         ComError(hr);
  214     }
  215 }
  216 
  217 
  218 ITypeInfo *GetClassTypeInfo(IUnknown *aUnk)
  219 {
  220     BOOL found = false;
  221     ITypeInfo *ptinfo;
  222     TYPEATTR *typeattr;
  223 
  224     // Find class typeinfo via IProvideClassInfo if available.
  225     // Testing shows this works in some cases where the IDispatch method fails,
  226     // such as for an HTMLDivElement object from an IE11 WebBrowser control.
  227     IProvideClassInfo *ppci;
  228     if (SUCCEEDED(aUnk->QueryInterface<IProvideClassInfo>(&ppci)))
  229     {
  230         found = SUCCEEDED(ppci->GetClassInfo(&ptinfo));
  231         ppci->Release();
  232         if (found)
  233             return ptinfo;
  234     }
  235 
  236     //
  237     // Find class typeinfo via IDispatch.
  238     //
  239     IDispatch *pdsp;
  240     ITypeLib *ptlib;
  241     IID iid;
  242     if (SUCCEEDED(aUnk->QueryInterface<IDispatch>(&pdsp)))
  243     {
  244         // Get IID and typelib of this object's IDispatch implementation.
  245         if (SUCCEEDED(pdsp->GetTypeInfo(0, LOCALE_USER_DEFAULT, &ptinfo)))
  246         {
  247             UINT index;
  248             if (SUCCEEDED(ptinfo->GetTypeAttr(&typeattr)))
  249             {
  250                 iid = typeattr->guid;
  251                 ptinfo->ReleaseTypeAttr(typeattr);
  252                 found = SUCCEEDED(ptinfo->GetContainingTypeLib(&ptlib, &index));
  253             }
  254             ptinfo->Release();
  255         }
  256         pdsp->Release();
  257     }
  258 
  259     if (!found)
  260         // No typelib to search through, so give up.
  261         return NULL;
  262     found = false;
  263 
  264     UINT ctinfo = ptlib->GetTypeInfoCount();
  265     for (UINT i = 0; i < ctinfo; i++)
  266     {
  267         TYPEKIND typekind;
  268         // Consider only classes:
  269         if (FAILED(ptlib->GetTypeInfoType(i, &typekind)) || typekind != TKIND_COCLASS
  270             || FAILED(ptlib->GetTypeInfo(i, &ptinfo)))
  271             continue;
  272 
  273         WORD cImplTypes = 0;
  274         if (SUCCEEDED(ptinfo->GetTypeAttr(&typeattr)))
  275         {
  276             cImplTypes = typeattr->cImplTypes;
  277             ptinfo->ReleaseTypeAttr(typeattr);
  278         }
  279 
  280         for (UINT j = 0; j < cImplTypes; j++)
  281         {
  282             INT flags;
  283             if (SUCCEEDED(ptinfo->GetImplTypeFlags(j, &flags)) && flags == IMPLTYPEFLAG_FDEFAULT)
  284             {
  285                 // This is the default interface of the class.
  286                 HREFTYPE reftype;
  287                 ITypeInfo *prinfo;
  288                 if (SUCCEEDED(ptinfo->GetRefTypeOfImplType(j, &reftype))
  289                     && SUCCEEDED(ptinfo->GetRefTypeInfo(reftype, &prinfo)))
  290                 {
  291                     if (SUCCEEDED(prinfo->GetTypeAttr(&typeattr)))
  292                     {
  293                         // If the IID matches, this is probably the right class.
  294                         found = iid == typeattr->guid;
  295                         prinfo->ReleaseTypeAttr(typeattr);
  296                     }
  297                     prinfo->Release();
  298                 }
  299                 break;
  300             }
  301         }
  302         if (found)
  303             break;
  304         ptinfo->Release();
  305     }
  306     ptlib->Release();
  307     return found ? ptinfo : NULL;
  308 }
  309 
  310 
  311 BIF_DECL(BIF_ComObjConnect)
  312 {
  313     aResultToken.symbol = SYM_STRING;
  314     aResultToken.marker = _T("");
  315 
  316     if (ComObject *obj = dynamic_cast<ComObject *>(TokenToObject(*aParam[0])))
  317     {
  318         if ((obj->mVarType != VT_DISPATCH && obj->mVarType != VT_UNKNOWN) || !obj->mUnknown)
  319         {
  320             ComError(-1); // Previously E_NOINTERFACE.
  321             return;
  322         }
  323         
  324         ITypeInfo *ptinfo;
  325         if (  !obj->mEventSink && (ptinfo = GetClassTypeInfo(obj->mUnknown))  )
  326         {
  327             TYPEATTR *typeattr;
  328             WORD cImplTypes = 0;
  329             if (SUCCEEDED(ptinfo->GetTypeAttr(&typeattr)))
  330             {
  331                 cImplTypes = typeattr->cImplTypes;
  332                 ptinfo->ReleaseTypeAttr(typeattr);
  333             }
  334 
  335             for(UINT j = 0; j < cImplTypes; j++)
  336             {
  337                 INT flags;
  338                 HREFTYPE reftype;
  339                 ITypeInfo *prinfo;
  340                 if (SUCCEEDED(ptinfo->GetImplTypeFlags(j, &flags)) && flags == (IMPLTYPEFLAG_FDEFAULT | IMPLTYPEFLAG_FSOURCE)
  341                     && SUCCEEDED(ptinfo->GetRefTypeOfImplType(j, &reftype))
  342                     && SUCCEEDED(ptinfo->GetRefTypeInfo(reftype, &prinfo)))
  343                 {
  344                     if (SUCCEEDED(prinfo->GetTypeAttr(&typeattr)))
  345                     {
  346                         if (typeattr->typekind == TKIND_DISPATCH)
  347                         {
  348                             obj->mEventSink = new ComEvent(obj, prinfo, typeattr->guid);
  349                             prinfo->ReleaseTypeAttr(typeattr);
  350                             break;
  351                         }
  352                         prinfo->ReleaseTypeAttr(typeattr);
  353                     }
  354                     prinfo->Release();
  355                 }
  356             }
  357             ptinfo->Release();
  358         }
  359 
  360         if (obj->mEventSink)
  361         {
  362             HRESULT hr;
  363             if (aParamCount < 2)
  364                 hr = obj->mEventSink->Connect(); // Disconnect.
  365             else
  366                 hr = obj->mEventSink->Connect(TokenToString(*aParam[1]), TokenToObject(*aParam[1]));
  367             if (FAILED(hr))
  368                 ComError(hr);
  369             return;
  370         }
  371 
  372         ComError(E_NOINTERFACE);
  373     }
  374     else
  375         ComError(-1); // "No COM object"
  376 }
  377 
  378 
  379 BIF_DECL(BIF_ComObjError)
  380 {
  381     aResultToken.value_int64 = g_ComErrorNotify;
  382     if (aParamCount && TokenIsPureNumeric(*aParam[0]))
  383         g_ComErrorNotify = (TokenToInt64(*aParam[0]) != 0);
  384 }
  385 
  386 
  387 BIF_DECL(BIF_ComObjTypeOrValue)
  388 {
  389     ComObject *obj = dynamic_cast<ComObject *>(TokenToObject(*aParam[0]));
  390     if (!obj)
  391     {
  392         aResultToken.symbol = SYM_STRING;
  393         aResultToken.marker = _T("");
  394         return;
  395     }
  396     if (ctoupper(aResultToken.marker[6]) == 'V')
  397     {
  398         aResultToken.value_int64 = obj->mVal64;
  399     }
  400     else
  401     {
  402         if (aParamCount < 2)
  403         {
  404             aResultToken.value_int64 = obj->mVarType;
  405         }
  406         else
  407         {
  408             aResultToken.symbol = SYM_STRING; // for all code paths below
  409             aResultToken.marker = _T(""); // in case of error
  410 
  411             LPTSTR requested_info = TokenToString(*aParam[1]);
  412 
  413             ITypeInfo *ptinfo = NULL;
  414             if (tolower(*requested_info) == 'c')
  415             {
  416                 // Get class information.
  417                 if ((VT_DISPATCH == obj->mVarType || VT_UNKNOWN == obj->mVarType) && obj->mUnknown)
  418                 {
  419                     ptinfo = GetClassTypeInfo(obj->mUnknown);
  420                     if (ptinfo)
  421                     {
  422                         if (!_tcsicmp(requested_info, _T("class")))
  423                             requested_info = _T("name");
  424                         else if (!_tcsicmp(requested_info, _T("clsid")))
  425                             requested_info = _T("iid");
  426                     }
  427                 }
  428             }
  429             else
  430             {
  431                 // Get IDispatch information.
  432                 if (VT_DISPATCH == obj->mVarType && obj->mDispatch)
  433                     if (FAILED(obj->mDispatch->GetTypeInfo(0, LOCALE_USER_DEFAULT, &ptinfo)))
  434                         ptinfo = NULL;
  435             }
  436             if (ptinfo)
  437             {
  438                 if (!_tcsicmp(requested_info, _T("name")))
  439                 {
  440                     BSTR name;
  441                     if (SUCCEEDED(ptinfo->GetDocumentation(MEMBERID_NIL, &name, NULL, NULL, NULL)))
  442                     {
  443                         TokenSetResult(aResultToken, CStringTCharFromWCharIfNeeded(name), SysStringLen(name));
  444                         SysFreeString(name);
  445                     }
  446                 }
  447                 else if (!_tcsicmp(requested_info, _T("iid")))
  448                 {
  449                     TYPEATTR *typeattr;
  450                     if (SUCCEEDED(ptinfo->GetTypeAttr(&typeattr)))
  451                     {
  452                         aResultToken.marker = aResultToken.buf;
  453 #ifdef UNICODE
  454                         StringFromGUID2(typeattr->guid, aResultToken.marker, MAX_NUMBER_SIZE);
  455 #else
  456                         WCHAR cnvbuf[MAX_NUMBER_SIZE];
  457                         StringFromGUID2(typeattr->guid, cnvbuf, MAX_NUMBER_SIZE);
  458                         CStringCharFromWChar cnvstring(cnvbuf);
  459                         strncpy(aResultToken.marker, cnvstring.GetBuffer(), MAX_NUMBER_SIZE);
  460 #endif
  461                         ptinfo->ReleaseTypeAttr(typeattr);
  462                     }
  463                 }
  464                 ptinfo->Release();
  465             }
  466         }
  467     }
  468 }
  469 
  470 
  471 BIF_DECL(BIF_ComObjFlags)
  472 {
  473     ComObject *obj = dynamic_cast<ComObject *>(TokenToObject(*aParam[0]));
  474     if (!obj)
  475     {
  476         aResultToken.symbol = SYM_STRING;
  477         aResultToken.marker = _T("");
  478         return;
  479     }
  480     if (aParamCount > 1)
  481     {
  482         USHORT flags, mask;
  483         if (aParamCount > 2)
  484         {
  485             flags = (USHORT)TokenToInt64(*aParam[1]);
  486             mask = (USHORT)TokenToInt64(*aParam[2]);
  487         }
  488         else
  489         {
  490             __int64 bigflags = TokenToInt64(*aParam[1]);
  491             if (bigflags < 0)
  492             {
  493                 // Remove specified -flags.
  494                 flags = 0;
  495                 mask = (USHORT)-bigflags;
  496             }
  497             else
  498             {
  499                 // Add only specified flags.
  500                 flags = (USHORT)bigflags;
  501                 mask = flags;
  502             }
  503         }
  504         obj->mFlags = (obj->mFlags & ~mask) | (flags & mask);
  505     }
  506     aResultToken.value_int64 = obj->mFlags;
  507 }
  508 
  509 
  510 BIF_DECL(BIF_ComObjArray)
  511 {
  512     VARTYPE vt = (VARTYPE)TokenToInt64(*aParam[0]);
  513     SAFEARRAYBOUND bound[8]; // Same limit as ComObject::SafeArrayInvoke().
  514     int dims = aParamCount - 1;
  515     if (dims > _countof(bound)) // Possible only for dynamic function calls.
  516         dims = _countof(bound);
  517     for (int i = 0; i < dims; ++i)
  518     {
  519         bound[i].cElements = (ULONG)TokenToInt64(*aParam[i + 1]);
  520         bound[i].lLbound = 0;
  521     }
  522     SAFEARRAY *psa = SafeArrayCreate(vt, dims, bound);
  523     if (!SafeSetTokenObject(aResultToken, psa ? new ComObject((__int64)psa, VT_ARRAY | vt, ComObject::F_OWNVALUE) : NULL) && psa)
  524         SafeArrayDestroy(psa);
  525 }
  526 
  527 
  528 BIF_DECL(BIF_ComObjQuery)
  529 {
  530     IUnknown *punk = NULL;
  531     ComObject *obj;
  532     HRESULT hr;
  533     
  534     aResultToken.value_int64 = 0; // Set default; on 32-bit builds, only the low 32 bits may be set below.
  535 
  536     if (obj = dynamic_cast<ComObject *>(TokenToObject(*aParam[0])))
  537     {
  538         // We were passed a ComObject, but does it contain an interface pointer?
  539         if (obj->mVarType == VT_UNKNOWN || obj->mVarType == VT_DISPATCH)
  540             punk = obj->mUnknown;
  541     }
  542     if (!punk)
  543     {
  544         // Since it wasn't a valid ComObject, it should be a raw interface pointer.
  545         punk = (IUnknown *)TokenToInt64(*aParam[0]);
  546         if (punk < (IUnknown *)65536) // Error-detection: the first 64KB of address space is always invalid.
  547         {
  548             g->LastError = E_INVALIDARG; // For consistency.
  549             ComError(-1);
  550             return;
  551         }
  552     }
  553 
  554     if (aParamCount > 2) // QueryService(obj, SID, IID)
  555     {
  556         GUID sid, iid;
  557         if (   SUCCEEDED(hr = CLSIDFromString(CStringWCharFromTCharIfNeeded(TokenToString(*aParam[1])), &sid))
  558             && SUCCEEDED(hr = CLSIDFromString(CStringWCharFromTCharIfNeeded(TokenToString(*aParam[2])), &iid))   )
  559         {
  560             IServiceProvider *pprov;
  561             if (SUCCEEDED(hr = punk->QueryInterface<IServiceProvider>(&pprov)))
  562             {
  563                 hr = pprov->QueryService(sid, iid, (void **)&aResultToken.value_int64);
  564             }
  565         }
  566     }
  567     else // QueryInterface(obj, IID)
  568     {
  569         GUID iid;
  570         if (SUCCEEDED(hr = CLSIDFromString(CStringWCharFromTCharIfNeeded(TokenToString(*aParam[1])), &iid)))
  571         {
  572             hr = punk->QueryInterface(iid, (void **)&aResultToken.value_int64);
  573         }
  574     }
  575 
  576     g->LastError = hr;
  577 }
  578 
  579 
  580 bool SafeSetTokenObject(ExprTokenType &aToken, IObject *aObject)
  581 {
  582     if (aObject)
  583     {
  584         aToken.symbol = SYM_OBJECT;
  585         aToken.object = aObject;
  586         return true;
  587     }
  588     else
  589     {
  590         aToken.symbol = SYM_STRING;
  591         aToken.marker = _T("");
  592         aToken.mem_to_free = NULL; // Only needed for some callers.
  593         return false;
  594     }
  595 }
  596 
  597 
  598 void VariantToToken(VARIANT &aVar, ExprTokenType &aToken, bool aRetainVar = true)
  599 {
  600     switch (aVar.vt)
  601     {
  602     case VT_BSTR:
  603         aToken.symbol = SYM_STRING;
  604         aToken.marker = _T("");     // Set defaults.
  605         aToken.mem_to_free = NULL;  //
  606         size_t len;
  607         if (len = SysStringLen(aVar.bstrVal))
  608         {
  609 #ifdef UNICODE
  610             if (aRetainVar)
  611             {
  612                 // It's safe to pass back the actual BSTR from aVar in this case.
  613                 aToken.marker = aVar.bstrVal;
  614             }
  615             // Allocate some memory to pass back to caller:
  616             else if (aToken.mem_to_free = tmalloc(len + 1))
  617             {
  618                 aToken.marker = aToken.mem_to_free;
  619                 aToken.marker_length = len;
  620                 tmemcpy(aToken.marker, aVar.bstrVal, len + 1); // +1 for null-terminator
  621             }
  622 #else
  623             CStringCharFromWChar buf(aVar.bstrVal, len);
  624             len = buf.GetLength(); // Get ANSI length.
  625             if (aToken.mem_to_free = buf.DetachBuffer())
  626             {
  627                 aToken.marker = aToken.mem_to_free;
  628                 aToken.marker_length = len;
  629             }
  630 #endif
  631         }
  632         if (!aRetainVar)
  633             VariantClear(&aVar);
  634         break;
  635     case VT_I4:
  636     case VT_ERROR:
  637         aToken.symbol = SYM_INTEGER;
  638         aToken.value_int64 = aVar.lVal;
  639         break;
  640     case VT_I2:
  641     case VT_BOOL:
  642         aToken.symbol = SYM_INTEGER;
  643         aToken.value_int64 = aVar.iVal;
  644         break;
  645     case VT_R8:
  646         aToken.symbol = SYM_FLOAT;
  647         aToken.value_double = aVar.dblVal;
  648         break;
  649     case VT_R4:
  650         aToken.symbol = SYM_FLOAT;
  651         aToken.value_double = (double)aVar.fltVal;
  652         break;
  653     case VT_UNKNOWN:
  654         if (aVar.punkVal)
  655         {
  656             IEnumVARIANT *penum;
  657             if (SUCCEEDED(aVar.punkVal->QueryInterface(IID_IEnumVARIANT, (void**) &penum)))
  658             {
  659                 if (!aRetainVar)
  660                     aVar.punkVal->Release();
  661                 if (!SafeSetTokenObject(aToken, new ComEnum(penum)))
  662                     penum->Release();
  663                 break;
  664             }
  665             IDispatch *pdisp;
  666             // .NET objects implement the COM IDispatch interface, but unfortunately
  667             // they don't always respond to QueryInterface() for said interface. So
  668             // instead we have to query for the _Object interface, which itself
  669             // inherits from IDispatch.
  670             if (SUCCEEDED(aVar.punkVal->QueryInterface(IID__Object, (void**) &pdisp)))
  671             {
  672                 if (!aRetainVar)
  673                     aVar.punkVal->Release();
  674                 if (!SafeSetTokenObject(aToken, new ComObject(pdisp)))
  675                     pdisp->Release();
  676                 break;
  677             }
  678         }
  679         // FALL THROUGH to the next case:
  680     case VT_DISPATCH:
  681         if (aVar.punkVal)
  682         {
  683             IObjectComCompatible *pobj;
  684             if (SUCCEEDED(aVar.punkVal->QueryInterface(IID_IObjectComCompatible, (void**)&pobj)))
  685             {
  686                 aToken.object = pobj;
  687                 aToken.symbol = SYM_OBJECT;
  688                 if (!aRetainVar)
  689                     // QI called AddRef, so Release this reference.
  690                     aVar.punkVal->Release();
  691                 break;
  692             }
  693             if (aToken.object = new ComObject((__int64)aVar.punkVal, aVar.vt))
  694             {
  695                 aToken.symbol = SYM_OBJECT;
  696                 if (aRetainVar)
  697                     // Caller is keeping their ref, so we AddRef.
  698                     aVar.punkVal->AddRef();
  699                 break;
  700             }
  701             if (!aRetainVar)
  702                 // Above failed, but caller doesn't want their ref, so release it.
  703                 aVar.punkVal->Release();
  704         }
  705         // FALL THROUGH to the next case:
  706     case VT_EMPTY:
  707     case VT_NULL:
  708         aToken.symbol = SYM_STRING;
  709         aToken.marker = _T("");
  710         aToken.mem_to_free = NULL;
  711         break;
  712     default:
  713         {
  714             VARIANT var = {0};
  715             if (aVar.vt < VT_ARRAY // i.e. not byref or an array.
  716                 && SUCCEEDED(VariantChangeType(&var, &aVar, 0, VT_BSTR))) // Convert it to a BSTR.
  717             {
  718                 // Recursive call to handle conversions and memory management correctly:
  719                 VariantToToken(var, aToken, false);
  720             }
  721             else
  722             {
  723                 if (!SafeSetTokenObject(aToken,
  724                         new ComObject((__int64)aVar.parray, aVar.vt, aRetainVar ? 0 : ComObject::F_OWNVALUE)))
  725                 {
  726                     // Out of memory; value cannot be returned, so must be freed here.
  727                     if (!aRetainVar)
  728                         VariantClear(&aVar);
  729                 }
  730             }
  731         }
  732     }
  733 }
  734 
  735 void AssignVariant(Var &aArg, VARIANT &aVar, bool aRetainVar = true)
  736 {
  737     if (aVar.vt == VT_BSTR)
  738     {
  739         // Avoid an unnecessary mem alloc and copy in some cases.
  740         aArg.AssignStringW(aVar.bstrVal, SysStringLen(aVar.bstrVal));
  741         if (!aRetainVar)
  742             VariantClear(&aVar);
  743         return;
  744     }
  745     ExprTokenType token;
  746     VariantToToken(aVar, token, aRetainVar);
  747     switch (token.symbol)
  748     {
  749     case SYM_STRING:  // VT_BSTR was handled above, but VariantToToken coerces unhandled types to strings.
  750         if (token.mem_to_free)
  751             aArg.AcceptNewMem(token.mem_to_free, token.marker_length);
  752         else
  753             aArg.Assign();
  754         break;
  755     case SYM_OBJECT:
  756         aArg.AssignSkipAddRef(token.object); // Let aArg take responsibility for it.
  757         break;
  758     default:
  759         aArg.Assign(token);
  760         break;
  761     }
  762 }
  763 
  764 
  765 void TokenToVariant(ExprTokenType &aToken, VARIANT &aVar, BOOL aVarIsArg)
  766 {
  767     if (aToken.symbol == SYM_VAR)
  768         aToken.var->ToToken(aToken);
  769 
  770     switch(aToken.symbol)
  771     {
  772     case SYM_OPERAND:
  773         if (aToken.buf)
  774         {
  775             __int64 val = *(__int64 *)aToken.buf;
  776             if (val == (int)val)
  777             {
  778                 aVar.vt = VT_I4;
  779                 aVar.lVal = (int)val;
  780             }
  781             else
  782             {
  783                 aVar.vt = VT_R8;
  784                 aVar.dblVal = (double)val;
  785             }
  786             break;
  787         }
  788     case SYM_STRING:
  789         aVar.vt = VT_BSTR;
  790         aVar.bstrVal = SysAllocString(CStringWCharFromTCharIfNeeded(aToken.marker));
  791         break;
  792     case SYM_INTEGER:
  793         {
  794             __int64 val = aToken.value_int64;
  795             if (val == (int)val)
  796             {
  797                 aVar.vt = VT_I4;
  798                 aVar.lVal = (int)val;
  799             }
  800             else
  801             {
  802                 aVar.vt = VT_R8;
  803                 aVar.dblVal = (double)val;
  804             }
  805         }
  806         break;
  807     case SYM_FLOAT:
  808         aVar.vt = VT_R8;
  809         aVar.dblVal = aToken.value_double;
  810         break;
  811     case SYM_OBJECT:
  812         if (ComObject *obj = dynamic_cast<ComObject *>(aToken.object))
  813         {
  814             obj->ToVariant(aVar);
  815             if (!aVarIsArg)
  816             {
  817                 if (aVar.vt == VT_DISPATCH || aVar.vt == VT_UNKNOWN)
  818                 {
  819                     if (aVar.punkVal)
  820                         aVar.punkVal->AddRef();
  821                 }
  822                 else if ((aVar.vt & ~VT_TYPEMASK) == VT_ARRAY && (obj->mFlags & ComObject::F_OWNVALUE))
  823                 {
  824                     // Copy array since both sides will call Destroy().
  825                     if (FAILED(SafeArrayCopy(aVar.parray, &aVar.parray)))
  826                         aVar.vt = VT_EMPTY;
  827                 }
  828             }
  829         }
  830         else
  831         {
  832             aVar.vt = VT_DISPATCH;
  833             aVar.pdispVal = aToken.object;
  834             if (!aVarIsArg)
  835                 aToken.object->AddRef();
  836         }
  837         break;
  838     case SYM_MISSING:
  839         aVar.vt = VT_ERROR;
  840         aVar.scode = DISP_E_PARAMNOTFOUND;
  841         break;
  842     }
  843 }
  844 
  845 
  846 HRESULT TokenToVarType(ExprTokenType &aToken, VARTYPE aVarType, void *apValue)
  847 // Copy the value of a given token into a variable of the given VARTYPE.
  848 {
  849     if (aVarType == VT_VARIANT)
  850     {
  851         VariantClear((VARIANT *)apValue);
  852         TokenToVariant(aToken, *(VARIANT *)apValue, FALSE);
  853         return S_OK;
  854     }
  855 
  856 #define U 0 // Unsupported in SAFEARRAY/VARIANT.
  857 #define P sizeof(void *)
  858     // Unfortunately there appears to be no function to get the size of a given VARTYPE,
  859     // and VariantCopyInd() copies in the wrong direction.  An alternative approach to
  860     // the following would be to switch(aVarType) and copy using the appropriate pointer
  861     // type, but disassembly shows that approach produces larger code and internally uses
  862     // an array of sizes like this anyway:
  863     static char vt_size[] = {U,U,2,4,4,8,8,8,P,P,4,2,0,P,0,U,1,1,2,4,8,8,4,4,U,U,U,U,U,U,U,U,U,U,U,U,U,P,P};
  864     size_t vsize = (aVarType < _countof(vt_size)) ? vt_size[aVarType] : 0;
  865     if (!vsize)
  866         return DISP_E_BADVARTYPE;
  867 #undef P
  868 #undef U
  869 
  870     VARIANT src;
  871     TokenToVariant(aToken, src, FALSE);
  872     // Above may have set var.vt to VT_BSTR (a newly allocated string or one passed via ComObject),
  873     // VT_DISPATCH or VT_UNKNOWN (in which case it called AddRef()).  The value is either freed by
  874     // VariantChangeType() or moved into *apValue, so we don't free it except on failure.
  875     if (src.vt != aVarType)
  876     {
  877         // Attempt to coerce var to the correct type:
  878         HRESULT hr = VariantChangeType(&src, &src, 0, aVarType);
  879         if (FAILED(hr))
  880         {
  881             VariantClear(&src);
  882             return hr;
  883         }
  884     }
  885     // Free existing value.
  886     if (aVarType == VT_UNKNOWN || aVarType == VT_DISPATCH)
  887     {
  888         IUnknown *punk = *(IUnknown **)apValue;
  889         if (punk)
  890             punk->Release();
  891     }
  892     else if (aVarType == VT_BSTR)
  893     {
  894         SysFreeString(*(BSTR *)apValue);
  895     }
  896     // Write new value (shallow copy).
  897     memcpy(apValue, &src.lVal, vsize);
  898     return S_OK;
  899 }
  900 
  901 
  902 void VarTypeToToken(VARTYPE aVarType, void *apValue, ExprTokenType &aToken)
  903 // Copy a value of the given VARTYPE into a token.
  904 {
  905     VARIANT src, dst;
  906     src.vt = VT_BYREF | aVarType;
  907     src.pbVal = (BYTE *)apValue;
  908     dst.vt = VT_EMPTY;
  909     if (FAILED(VariantCopyInd(&dst, &src)))
  910         dst.vt = VT_EMPTY;
  911     VariantToToken(dst, aToken, false);
  912 }
  913 
  914 
  915 void RValueToResultToken(ExprTokenType &aRValue, ExprTokenType &aResultToken)
  916 // Support function for chaining assignments.  Provides consistent results when aRValue has been
  917 // converted to a VARIANT.  Passes wrapper objects as is, whereas VariantToToken would return
  918 // either a simple value or a new wrapper object.
  919 {
  920     switch (aRValue.symbol)
  921     {
  922     case SYM_OPERAND:
  923         if (aRValue.buf)
  924         {
  925             aResultToken.symbol = SYM_INTEGER;
  926             aResultToken.value_int64 = *(__int64 *)aRValue.buf;
  927             break;
  928         }
  929         // FALL THROUGH to next case:
  930     case SYM_STRING:
  931         aResultToken.symbol = SYM_STRING;
  932         aResultToken.marker = aRValue.marker;
  933         break;
  934     case SYM_OBJECT:
  935         aResultToken.symbol = SYM_OBJECT;
  936         aResultToken.object = aRValue.object;
  937         aResultToken.object->AddRef();
  938         break;
  939     case SYM_INTEGER:
  940     case SYM_FLOAT:
  941         aResultToken.symbol = aRValue.symbol;
  942         aResultToken.value_int64 = aRValue.value_int64;
  943         break;
  944     }
  945 }
  946 
  947 
  948 bool g_ComErrorNotify = true;
  949 
  950 void ComError(HRESULT hr, LPTSTR name, EXCEPINFO* pei)
  951 {
  952     if (hr != DISP_E_EXCEPTION)
  953         pei = NULL;
  954 
  955     if (g_ComErrorNotify)
  956     {
  957         if (pei)
  958         {
  959             if (pei->pfnDeferredFillIn)
  960                 (*pei->pfnDeferredFillIn)(pei);
  961             hr = pei->wCode ? 0x80040200 + pei->wCode : pei->scode;
  962         }
  963 
  964         TCHAR buf[4096], *error_text;
  965         if (hr == -1)
  966             error_text = _T("No valid COM object!");
  967         else
  968         {
  969             int size = _stprintf(buf, _T("0x%08X - "), hr);
  970             size += FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, hr, 0, buf + size, _countof(buf) - size, NULL);
  971             if (buf[size-1] == '\n')
  972                 buf[--size] = '\0';
  973             if (buf[size-1] == '\r')
  974                 buf[--size] = '\0';
  975             if (pei)
  976                 _vsntprintf(buf + size, _countof(buf) - size, _T("\nSource:\t\t%ws\nDescription:\t%ws\nHelpFile:\t\t%ws\nHelpContext:\t%d"), (va_list) &pei->bstrSource);
  977             error_text = buf;
  978         }
  979 
  980         g_script.mCurrLine->LineError(error_text, EARLY_EXIT, name);
  981     }
  982 
  983     if (pei)
  984     {
  985         SysFreeString(pei->bstrSource);
  986         SysFreeString(pei->bstrDescription);
  987         SysFreeString(pei->bstrHelpFile);
  988     }
  989 }
  990 
  991 
  992 
  993 STDMETHODIMP ComEvent::QueryInterface(REFIID riid, void **ppv)
  994 {
  995     if (riid == mIID || riid == IID_IDispatch || riid == IID_IUnknown)
  996     {
  997         AddRef();
  998         *ppv = this;
  999         return S_OK;
 1000     }
 1001     else
 1002     {
 1003         *ppv = NULL;
 1004         return E_NOINTERFACE;
 1005     }
 1006 }
 1007 
 1008 STDMETHODIMP ComEvent::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
 1009 {
 1010     return mTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId);
 1011 }
 1012 
 1013 STDMETHODIMP ComEvent::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
 1014 {
 1015     if (!mObject) // mObject == NULL should be next to impossible since it is only set NULL after calling Unadvise(), in which case there shouldn't be anyone left to call this->Invoke().  Check it anyway since it might be difficult to debug, depending on what we're connected to.
 1016         return DISP_E_MEMBERNOTFOUND;
 1017 
 1018     // Resolve method name.
 1019     BSTR memberName;
 1020     UINT nNames;
 1021     if (FAILED(mTypeInfo->GetNames(dispIdMember, &memberName, 1, &nNames)))
 1022         return DISP_E_MEMBERNOTFOUND;
 1023 
 1024     UINT cArgs = pDispParams->cArgs;
 1025     const UINT ADDITIONAL_PARAMS = 2; // mObject (passed by us) and mAhkObject (passed by Object::Invoke).
 1026     const UINT MAX_COM_PARAMS = MAX_FUNCTION_PARAMS - ADDITIONAL_PARAMS;
 1027     if (cArgs > MAX_COM_PARAMS) // Probably won't happen in any real-world script.
 1028         cArgs = MAX_COM_PARAMS; // Just omit the rest of the params.
 1029 
 1030     // Make a temporary copy of pDispParams to allow us to insert one:
 1031     DISPPARAMS dispParams;
 1032     VARIANTARG *vargs = (VARIANTARG *)_alloca((cArgs + 1) * sizeof(VARIANTARG));
 1033     memcpy(&dispParams, pDispParams, sizeof(dispParams));
 1034     memcpy(vargs + 1, pDispParams->rgvarg, cArgs * sizeof(VARIANTARG));
 1035     dispParams.rgvarg = vargs;
 1036     
 1037     // Pass our object last (right-to-left) for either of the following cases:
 1038     //  a) Our caller doesn't include its IDispatch interface pointer in the parameter list.
 1039     //  b) The script needs a reference to the original wrapper object; i.e. mObject.
 1040     vargs[0].vt = VT_DISPATCH;
 1041     vargs[0].pdispVal = mObject;
 1042     dispParams.cArgs = ++cArgs;
 1043 
 1044     HRESULT hr;
 1045     IDispatch *func;
 1046     DISPID dispid;
 1047 
 1048     if (mAhkObject)
 1049     {
 1050         func = mAhkObject;
 1051         hr = func->GetIDsOfNames(IID_NULL, &memberName, 1, lcid, &dispid);
 1052     }
 1053     else
 1054     {
 1055         // Copy method name into our buffer, applying prefix and converting if necessary.
 1056         TCHAR funcName[256];
 1057         sntprintf(funcName, _countof(funcName), _T("%s%ws"), mPrefix, memberName);
 1058         // Find the script function:
 1059         func = g_script.FindFunc(funcName);
 1060         dispid = DISPID_VALUE;
 1061         hr = func ? S_OK : DISP_E_MEMBERNOTFOUND;
 1062     }
 1063     SysFreeString(memberName);
 1064 
 1065     if (SUCCEEDED(hr))
 1066         hr = func->Invoke(dispid, riid, lcid, wFlags, &dispParams, pVarResult, pExcepInfo, puArgErr);
 1067 
 1068     // It's hard to say what our caller will do if DISP_E_MEMBERNOTFOUND is returned,
 1069     // so just return S_OK as in previous versions:
 1070     return S_OK;
 1071 }
 1072 
 1073 HRESULT ComEvent::Connect(LPTSTR pfx, IObject *ahkObject)
 1074 {
 1075     HRESULT hr;
 1076 
 1077     if ((pfx != NULL) != (mCookie != 0)) // want_connection != have_connection
 1078     {
 1079         IConnectionPointContainer *pcpc;
 1080         hr = mObject->mDispatch->QueryInterface(IID_IConnectionPointContainer, (void **)&pcpc);
 1081         if (SUCCEEDED(hr))
 1082         {
 1083             IConnectionPoint *pconn;
 1084             hr = pcpc->FindConnectionPoint(mIID, &pconn);
 1085             if (SUCCEEDED(hr))
 1086             {
 1087                 if (pfx)
 1088                 {
 1089                     hr = pconn->Advise(this, &mCookie);
 1090                 }
 1091                 else
 1092                 {
 1093                     hr = pconn->Unadvise(mCookie);
 1094                     if (SUCCEEDED(hr))
 1095                         mCookie = 0;
 1096                     if (mAhkObject) // Even if above failed:
 1097                     {
 1098                         mAhkObject->Release();
 1099                         mAhkObject = NULL;
 1100                     }
 1101                 }
 1102                 pconn->Release();
 1103             }
 1104             pcpc->Release();
 1105         }
 1106     }
 1107     else
 1108         hr = S_OK; // No change required.
 1109 
 1110     if (SUCCEEDED(hr))
 1111     {
 1112         if (mAhkObject)
 1113             // Release this object before storing the new one below.
 1114             mAhkObject->Release();
 1115         // Update prefix/object.
 1116         if (mAhkObject = ahkObject)
 1117             mAhkObject->AddRef();
 1118         if (pfx)
 1119             _tcscpy(mPrefix, pfx);
 1120         else
 1121             *mPrefix = '\0'; // For maintainability.
 1122     }
 1123     return hr;
 1124 }
 1125 
 1126 ResultType STDMETHODCALLTYPE ComObject::Invoke(ExprTokenType &aResultToken, ExprTokenType &aThisToken, int aFlags, ExprTokenType *aParam[], int aParamCount)
 1127 {
 1128     if (aParamCount < (IS_INVOKE_SET ? 2 : 1))
 1129     {
 1130         HRESULT hr = DISP_E_BADPARAMCOUNT; // Set default.
 1131         // Something like x[] or x[]:=y.
 1132         if (mVarType & VT_BYREF)
 1133         {
 1134             VARTYPE item_type = mVarType & VT_TYPEMASK;
 1135             if (aParamCount) // Implies SET.
 1136             {
 1137                 hr = TokenToVarType(*aParam[0], item_type, mValPtr);
 1138                 if (SUCCEEDED(hr))
 1139                 {
 1140                     RValueToResultToken(*aParam[0], aResultToken);
 1141                     return OK;
 1142                 }
 1143             }
 1144             else // !aParamCount: Implies GET.
 1145             {
 1146                 VarTypeToToken(item_type, mValPtr, aResultToken);
 1147                 return OK;
 1148             }
 1149         }
 1150         if ((mVarType & VT_ARRAY) // Not meaningful for SafeArrays.
 1151             || IS_INVOKE_SET) // Wouldn't be handled correctly below and probably has no real-world use.
 1152         {
 1153             ComError(g->LastError = hr);
 1154             return OK;
 1155         }
 1156     }
 1157 
 1158     if (mVarType != VT_DISPATCH || !mDispatch)
 1159     {
 1160         if (mVarType & VT_ARRAY)
 1161             return SafeArrayInvoke(aResultToken, aFlags, aParam, aParamCount);
 1162         // Otherwise: this object can't be invoked.
 1163         g->LastError = DISP_E_BADVARTYPE; // Seems more informative than -1.
 1164         ComError(-1);
 1165         return OK;
 1166     }
 1167 
 1168     DISPID dispid;
 1169     LPTSTR aName;
 1170     HRESULT hr;
 1171     if (aFlags & IF_NEWENUM)
 1172     {
 1173         hr = S_OK;
 1174         dispid = DISPID_NEWENUM;
 1175         aName = _T("_NewEnum"); // Init for ComError().
 1176     }
 1177     else if (!aParamCount || aParam[0]->symbol == SYM_MISSING)
 1178     {
 1179         // v1.1.20: a[,b] is now permitted.  Rather than treating it the same as 
 1180         // an empty string, invoke the object's default/"value" property/method.
 1181         hr = S_OK;
 1182         dispid = DISPID_VALUE;
 1183         aName = _T("");
 1184     }
 1185     else
 1186     {
 1187         aName = TokenToString(*aParam[0], aResultToken.buf);
 1188 #ifdef UNICODE
 1189         LPOLESTR wname = aName;
 1190 #else
 1191         CStringWCharFromChar cnvbuf(aName);
 1192         LPOLESTR wname = (LPOLESTR)(LPCWSTR)cnvbuf;
 1193 #endif
 1194         hr = mDispatch->GetIDsOfNames(IID_NULL, &wname, 1, LOCALE_USER_DEFAULT, &dispid);
 1195         if (hr == DISP_E_UNKNOWNNAME) // v1.1.18: Retry with IDispatchEx if supported, to allow creating new properties.
 1196         {
 1197             if (IS_INVOKE_SET)
 1198             {
 1199                 IDispatchEx *dispEx;
 1200                 if (SUCCEEDED(mDispatch->QueryInterface<IDispatchEx>(&dispEx)))
 1201                 {
 1202                     BSTR bname = SysAllocString(wname);
 1203                     // fdexNameEnsure gives us a new ID if needed, though GetIDsOfNames() will
 1204                     // still fail for some objects until after the assignment is performed below.
 1205                     hr = dispEx->GetDispID(bname, fdexNameEnsure, &dispid);
 1206                     SysFreeString(bname);
 1207                     dispEx->Release();
 1208                 }
 1209             }
 1210             else if (IS_INVOKE_CALL && TokenIsEmptyString(*aParam[0]))
 1211             {
 1212                 // Fn.() and %Fn%() both produce this condition.  aParam[0] is checked instead of aName
 1213                 // because aName is also empty if an object was passed, as for Fn[Obj]() or {X: Fn}.X().
 1214                 // Although allowing JScript functions to act as methods of AutoHotkey objects could be
 1215                 // useful, a different approach would be needed to pass 'this', such as:
 1216                 //  - IDispatchEx::InvokeEx with the named arg DISPID_THIS.
 1217                 //  - Fn.call(this) -- this would also work with AutoHotkey functions, but would break
 1218                 //    existing meta-function scripts.
 1219                 dispid = DISPID_VALUE;
 1220                 hr = S_OK;
 1221             }
 1222         }
 1223         if (FAILED(hr))
 1224             aParamCount = 0; // Skip parameter conversion and cleanup.
 1225     }
 1226     
 1227     static DISPID dispidParam = DISPID_PROPERTYPUT;
 1228     DISPPARAMS dispparams = {NULL, NULL, 0, 0};
 1229     VARIANTARG *rgvarg;
 1230     EXCEPINFO excepinfo = {0};
 1231     VARIANT varResult = {0};
 1232     
 1233     if (aParamCount)
 1234         --aParamCount; // Exclude the member name, if any.
 1235 
 1236     if (aParamCount)
 1237     {
 1238         rgvarg = (VARIANTARG *)_alloca(sizeof(VARIANTARG) * aParamCount);
 1239 
 1240         for (int i = 0; i < aParamCount; i++)
 1241         {
 1242             TokenToVariant(*aParam[aParamCount-i], rgvarg[i], TRUE);
 1243         }
 1244 
 1245         dispparams.rgvarg = rgvarg;
 1246         dispparams.cArgs = aParamCount;
 1247         if (IS_INVOKE_SET)
 1248         {
 1249             dispparams.rgdispidNamedArgs = &dispidParam;
 1250             dispparams.cNamedArgs = 1;
 1251         }
 1252     }
 1253 
 1254     if (SUCCEEDED(hr)
 1255         // For obj.x:=y where y is a ComObject, invoke PROPERTYPUTREF first:
 1256         && !(IS_INVOKE_SET && rgvarg[0].vt == VT_DISPATCH && SUCCEEDED(mDispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF, &dispparams, NULL, NULL, NULL))
 1257         // For obj.x(), invoke METHOD first since PROPERTYGET|METHOD is ambiguous and gets undesirable results in some known cases; but re-invoke with PROPERTYGET only if DISP_E_MEMBERNOTFOUND is returned:
 1258           || IS_INVOKE_CALL && !aParamCount && DISP_E_MEMBERNOTFOUND != (hr = mDispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &varResult, &excepinfo, NULL))))
 1259         // Invoke PROPERTYPUT or PROPERTYGET|METHOD as appropriate:
 1260         hr = mDispatch->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, IS_INVOKE_SET ? DISPATCH_PROPERTYPUT : DISPATCH_PROPERTYGET | DISPATCH_METHOD, &dispparams, &varResult, &excepinfo, NULL);
 1261 
 1262     for (int i = 0; i < aParamCount; i++)
 1263     {
 1264         // TokenToVariant() in "arg" mode never calls AddRef() or SafeArrayCopy(), so the arg needs to be freed
 1265         // only if it is a BSTR and not one which came from a ComObject wrapper (such as ComObject(9, pbstr)).
 1266         if (rgvarg[i].vt == VT_BSTR && aParam[aParamCount-i]->symbol != SYM_OBJECT)
 1267             SysFreeString(rgvarg[i].bstrVal);
 1268     }
 1269 
 1270     if  (FAILED(hr))
 1271     {
 1272         ComError(hr, aName, &excepinfo);
 1273     }
 1274     else if (IS_INVOKE_SET)
 1275     {   // Allow chaining, e.g. obj2.prop := obj1.prop := val.
 1276         ExprTokenType &rvalue = *aParam[aParamCount];
 1277         aResultToken.symbol = (rvalue.symbol == SYM_OPERAND) ? SYM_STRING : rvalue.symbol;
 1278         aResultToken.value_int64 = rvalue.value_int64;
 1279         if (rvalue.symbol == SYM_OBJECT)
 1280             rvalue.object->AddRef();
 1281     }
 1282     else
 1283     {
 1284         VariantToToken(varResult, aResultToken, false);
 1285     }
 1286 
 1287     g->LastError = hr;
 1288     return  OK;
 1289 }
 1290 
 1291 ResultType ComObject::SafeArrayInvoke(ExprTokenType &aResultToken, int aFlags, ExprTokenType *aParam[], int aParamCount)
 1292 {
 1293     HRESULT hr = S_OK;
 1294     SAFEARRAY *psa = (SAFEARRAY*)mVal64;
 1295     VARTYPE item_type = (mVarType & VT_TYPEMASK);
 1296 
 1297     if (IS_INVOKE_CALL)
 1298     {
 1299         LPTSTR name = TokenToString(*aParam[0]);
 1300         if (*name == '_')
 1301             ++name;
 1302         LONG retval;
 1303         if (!_tcsicmp(name, _T("NewEnum")))
 1304         {
 1305             ComArrayEnum *enm;
 1306             if (SUCCEEDED(hr = ComArrayEnum::Begin(this, enm)))
 1307             {
 1308                 aResultToken.symbol = SYM_OBJECT;
 1309                 aResultToken.object = enm;
 1310             }
 1311         }
 1312         else if (!_tcsicmp(name, _T("Clone")))
 1313         {
 1314             SAFEARRAY *clone;
 1315             if (SUCCEEDED(hr = SafeArrayCopy(psa, &clone)))
 1316                 if (!SafeSetTokenObject(aResultToken, new ComObject((__int64)clone, mVarType, F_OWNVALUE)))
 1317                     SafeArrayDestroy(clone);
 1318         }
 1319         else
 1320         {
 1321             if (!_tcsicmp(name, _T("MaxIndex")))
 1322                 hr = SafeArrayGetUBound(psa, aParamCount > 1 ? (UINT)TokenToInt64(*aParam[1]) : 1, &retval);
 1323             else if (!_tcsicmp(name, _T("MinIndex")))
 1324                 hr = SafeArrayGetLBound(psa, aParamCount > 1 ? (UINT)TokenToInt64(*aParam[1]) : 1, &retval);
 1325             else
 1326                 hr = DISP_E_UNKNOWNNAME; // Seems slightly better than ignoring the call.
 1327             if (SUCCEEDED(hr))
 1328             {
 1329                 aResultToken.symbol = SYM_INTEGER;
 1330                 aResultToken.value_int64 = retval;
 1331             }
 1332         }
 1333         g->LastError = hr;
 1334         if (FAILED(hr))
 1335             ComError(hr);
 1336         return OK;
 1337     }
 1338 
 1339     UINT dims = SafeArrayGetDim(psa);
 1340     LONG index[8];
 1341     // Verify correct number of parameters/dimensions (maximum 8).
 1342     if (dims > _countof(index) || dims != (IS_INVOKE_SET ? aParamCount - 1 : aParamCount))
 1343     {
 1344         g->LastError = DISP_E_BADPARAMCOUNT;
 1345         return OK;
 1346     }
 1347     // Build array of indices from parameters.
 1348     for (UINT i = 0; i < dims; ++i)
 1349     {
 1350         if (!TokenIsPureNumeric(*aParam[i]))
 1351         {
 1352             g->LastError = E_INVALIDARG;
 1353             return OK;
 1354         }
 1355         index[i] = (LONG)TokenToInt64(*aParam[i]);
 1356     }
 1357 
 1358     void *item;
 1359 
 1360     SafeArrayLock(psa);
 1361 
 1362     hr = SafeArrayPtrOfIndex(psa, index, &item);
 1363     if (SUCCEEDED(hr))
 1364     {
 1365         if (IS_INVOKE_GET)
 1366         {
 1367             VarTypeToToken(item_type, item, aResultToken);
 1368         }
 1369         else // SET
 1370         {
 1371             ExprTokenType &rvalue = *aParam[dims];
 1372             hr = TokenToVarType(rvalue, item_type, item);
 1373             if (SUCCEEDED(hr))
 1374                 RValueToResultToken(rvalue, aResultToken);
 1375             // Otherwise, leave aResultToken blank.
 1376         }
 1377     }
 1378 
 1379     SafeArrayUnlock(psa);
 1380 
 1381     g->LastError = hr;
 1382     if (FAILED(hr))
 1383         ComError(hr);
 1384     return OK;
 1385 }
 1386 
 1387 
 1388 LPTSTR ComObject::Type()
 1389 {
 1390     if (mVarType & VT_ARRAY)
 1391         return _T("ComObjArray"); // Has SafeArray methods.
 1392     if (mVarType & VT_BYREF)
 1393         return _T("ComObjRef"); // Has this[].
 1394     if ((mVarType == VT_DISPATCH || mVarType == VT_UNKNOWN) && mUnknown)
 1395     {
 1396         BSTR name;
 1397         ITypeInfo *ptinfo;
 1398         // Use COM class name if available.
 1399         if (  (ptinfo = GetClassTypeInfo(mUnknown))
 1400             && SUCCEEDED(ptinfo->GetDocumentation(MEMBERID_NIL, &name, NULL, NULL, NULL))  )
 1401         {
 1402             static TCHAR sBuf[64]; // Seems generous enough.
 1403             tcslcpy(sBuf, CStringTCharFromWCharIfNeeded(name), _countof(sBuf));
 1404             SysFreeString(name);
 1405             return sBuf;
 1406         }
 1407         if (mVarType == VT_DISPATCH)
 1408             return _T("ComObject"); // Can be invoked.
 1409     }
 1410     return _T("ComObj"); // Can't be invoked; may represent a value or a non-dispatch object.
 1411 }
 1412 
 1413 
 1414 int ComEnum::Next(Var *aOutput, Var *aOutputType)
 1415 {
 1416     VARIANT varResult = {0};
 1417     if (penum->Next(1, &varResult, NULL) == S_OK)
 1418     {
 1419         if (aOutputType)
 1420             aOutputType->Assign((__int64)varResult.vt);
 1421         if (aOutput)
 1422             AssignVariant(*aOutput, varResult, false);
 1423         return true;
 1424     }
 1425     return  false;
 1426 }
 1427 
 1428 
 1429 HRESULT ComArrayEnum::Begin(ComObject *aArrayObject, ComArrayEnum *&aEnum)
 1430 {
 1431     HRESULT hr;
 1432     SAFEARRAY *psa = aArrayObject->mArray;
 1433     char *arrayData, *arrayEnd;
 1434     long lbound, ubound;
 1435 
 1436     if (SafeArrayGetDim(psa) != 1)
 1437         return E_NOTIMPL;
 1438     
 1439     if (   SUCCEEDED(hr = SafeArrayGetLBound(psa, 1, &lbound))
 1440         && SUCCEEDED(hr = SafeArrayGetUBound(psa, 1, &ubound))
 1441         && SUCCEEDED(hr = SafeArrayAccessData(psa, (void **)&arrayData))   )
 1442     {
 1443         VARTYPE arrayType = aArrayObject->mVarType & VT_TYPEMASK;
 1444         UINT elemSize = SafeArrayGetElemsize(psa);
 1445         arrayEnd = arrayData + (ubound - lbound) * (long)elemSize; // Must cast to signed long for correct result when array is empty (ubound - lbound == -1).
 1446         if (aEnum = new ComArrayEnum(aArrayObject, arrayData, arrayEnd, elemSize, arrayType))
 1447         {
 1448             aArrayObject->AddRef(); // Keep obj alive until enumeration completes.
 1449         }
 1450         else
 1451         {
 1452             SafeArrayUnaccessData(psa);
 1453             hr = E_OUTOFMEMORY;
 1454         }
 1455     }
 1456     return hr;
 1457 }
 1458 
 1459 ComArrayEnum::~ComArrayEnum()
 1460 {
 1461     SafeArrayUnaccessData(mArrayObject->mArray); // Counter the lock done by ComArrayEnum::Begin.
 1462     mArrayObject->Release();
 1463 }
 1464 
 1465 int ComArrayEnum::Next(Var *aOutput, Var *aOutputType)
 1466 {
 1467     if ((mPointer += mElemSize) <= mEnd)
 1468     {
 1469         VARIANT var = {0};
 1470         if (mType == VT_VARIANT)
 1471         {
 1472             // Make shallow copy of the VARIANT item.
 1473             memcpy(&var, mPointer, sizeof(VARIANT));
 1474         }
 1475         else
 1476         {
 1477             // Build VARIANT with shallow copy of the item.
 1478             var.vt = mType;
 1479             memcpy(&var.lVal, mPointer, mElemSize);
 1480         }
 1481         // Copy value into var.
 1482         AssignVariant(*aOutput, var);
 1483         if (aOutputType)
 1484             aOutputType->Assign(var.vt);
 1485         return true;
 1486     }
 1487     return false;
 1488 }
 1489 
 1490 
 1491 IObject *GuiType::ControlGetActiveX(HWND aWnd)
 1492 {
 1493     typedef HRESULT (WINAPI *MyAtlAxGetControl)(HWND h, IUnknown **p);
 1494     static MyAtlAxGetControl fnAtlAxGetControl = NULL;
 1495     if (!fnAtlAxGetControl)
 1496         if (HMODULE hmodAtl = GetModuleHandle(_T("atl"))) // GuiType::AddControl should have already permanently loaded it.
 1497             fnAtlAxGetControl = (MyAtlAxGetControl)GetProcAddress(hmodAtl, "AtlAxGetControl");
 1498     if (fnAtlAxGetControl) // Should always be non-NULL if aControl is actually an ActiveX control.
 1499     {
 1500         IUnknown *punk;
 1501         if (SUCCEEDED(fnAtlAxGetControl(aWnd, &punk)))
 1502         {
 1503             IObject *pobj;
 1504             IDispatch *pdisp;
 1505             if (SUCCEEDED(punk->QueryInterface(IID_IDispatch, (void **)&pdisp)))
 1506             {
 1507                 punk->Release();
 1508                 if (  !(pobj = new ComObject(pdisp))  )
 1509                     pdisp->Release();
 1510             }
 1511             else
 1512             {
 1513                 if (  !(pobj = new ComObject((__int64)punk, VT_UNKNOWN))  )
 1514                     punk->Release();
 1515             }
 1516             return pobj;
 1517         }
 1518     }
 1519     return NULL;
 1520 }
 1521 
 1522 
 1523 STDMETHODIMP IObjectComCompatible::QueryInterface(REFIID riid, void **ppv)
 1524 {
 1525     if (riid == IID_IDispatch || riid == IID_IUnknown || riid == IID_IObjectComCompatible)
 1526     {
 1527         AddRef();
 1528         //*ppv = static_cast<IDispatch *>(this);
 1529         *ppv = this;
 1530         return S_OK;
 1531     }
 1532     else
 1533     {
 1534         *ppv = NULL;
 1535         return E_NOINTERFACE;
 1536     }
 1537 }
 1538 
 1539 STDMETHODIMP IObjectComCompatible::GetTypeInfoCount(UINT *pctinfo)
 1540 {
 1541     *pctinfo = 0;
 1542     return S_OK;
 1543 }
 1544 
 1545 STDMETHODIMP IObjectComCompatible::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
 1546 {
 1547     *pptinfo = NULL;
 1548     return E_NOTIMPL;
 1549 }
 1550 
 1551 static Object *g_IdToName;
 1552 static Object *g_NameToId;
 1553 
 1554 STDMETHODIMP IObjectComCompatible::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
 1555 {
 1556 #ifdef UNICODE
 1557     LPTSTR name = *rgszNames;
 1558 #else
 1559     CStringCharFromWChar name_buf(*rgszNames);
 1560     LPTSTR name = const_cast<LPTSTR>(name_buf.GetString());
 1561 #endif
 1562     if ( !(g_IdToName || (g_IdToName = Object::Create())) ||
 1563          !(g_NameToId || (g_NameToId = Object::Create())) )
 1564         return E_OUTOFMEMORY;
 1565     ExprTokenType id;
 1566     if (!g_NameToId->GetItem(id, name))
 1567     {
 1568         if (!g_IdToName->Append(name))
 1569             return E_OUTOFMEMORY;
 1570         id.symbol = SYM_INTEGER;
 1571         id.value_int64 = g_IdToName->GetNumericItemCount();
 1572         if (!g_NameToId->SetItem(name, id))
 1573             return E_OUTOFMEMORY;
 1574     }
 1575     *rgDispId = (DISPID)id.value_int64;
 1576     if (cNames == 1)
 1577         return S_OK;
 1578     for (UINT i = 1; i < cNames; ++i)
 1579         rgDispId[i] = DISPID_UNKNOWN;
 1580     return DISP_E_UNKNOWNNAME;
 1581 }
 1582 
 1583 STDMETHODIMP IObjectComCompatible::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
 1584 {
 1585     ExprTokenType param_token[MAX_FUNCTION_PARAMS];
 1586     ExprTokenType *param[MAX_FUNCTION_PARAMS];
 1587     ExprTokenType result_token;
 1588     TCHAR result_token_buf[MAX_NUMBER_SIZE];
 1589     
 1590     UINT cArgs = pDispParams->cArgs;
 1591     if (cArgs >= MAX_FUNCTION_PARAMS) // Probably won't happen in any real-world script.
 1592         cArgs = MAX_FUNCTION_PARAMS - 1; // Just omit the rest of the params.
 1593 
 1594     // PUT is probably never combined with GET/METHOD, so is handled first.  Some common problem
 1595     // cases which combine GET and METHOD include:
 1596     //  - Foo.Bar in VBScript.
 1597     //  - foo.bar() and foo.bar[] in C#.  There's no way to differentiate, so we just use METHOD
 1598     //    when there are parameters to cover the most common cases.  Both will work with user-
 1599     //    defined properties (v1.1.16+) since they can be "called", but won't work with __Get.
 1600     //  - foo[bar] and foo(bar) in C# both use METHOD|GET with DISPID_VALUE.
 1601     //  - foo(bar) (but not foo[bar]) in JScript uses METHOD|GET with DISPID_VALUE.
 1602     int flags = (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) ? IT_SET
 1603         : (wFlags & DISPATCH_METHOD) ? IT_CALL : IT_GET;
 1604     
 1605     ExprTokenType **first_param = param;
 1606     int param_count = cArgs;
 1607     
 1608     if (dispIdMember > 0)
 1609     {
 1610         if (!g_IdToName->GetItemOffset(param_token[0], dispIdMember - 1))
 1611             return DISP_E_MEMBERNOTFOUND;
 1612         if (IsPureNumeric(param_token[0].marker, FALSE, FALSE)) // o[1] in JScript produces a numeric name.
 1613         {
 1614             param_token[0].symbol = SYM_INTEGER;
 1615             param_token[0].value_int64 = ATOI(param_token[0].marker);
 1616         }
 1617         param[0] = &param_token[0];
 1618         ++param_count;
 1619         if (flags == IT_CALL && (wFlags & DISPATCH_PROPERTYGET))
 1620             flags |= IF_CALL_FUNC_ONLY;
 1621     }
 1622     else
 1623     {
 1624         if (dispIdMember != DISPID_VALUE)
 1625             return DISP_E_MEMBERNOTFOUND;
 1626         if (flags == IT_CALL && !(wFlags & DISPATCH_PROPERTYGET))
 1627         {
 1628             // This approach works well for Func, but not for an Object implementing __Call,
 1629             // which always expects a method name or the object whose method is being called:
 1630             //flags = (IT_CALL|IF_FUNCOBJ);
 1631             //++first_param;
 1632             // This is consistent with %func%():
 1633             param_token[0].symbol = SYM_STRING;
 1634             param_token[0].marker = _T("");
 1635             param[0] = &param_token[0];
 1636             ++param_count;
 1637         }
 1638         else
 1639         {
 1640             if (flags == IT_CALL) // Obj(X) in VBScript and C#, or Obj[X] in C#
 1641                 flags = IT_GET|IF_FUNCOBJ;
 1642             ++first_param;
 1643         }
 1644     }
 1645     
 1646     for (UINT i = 1; i <= cArgs; ++i)
 1647     {
 1648         VARIANTARG *pvar = &pDispParams->rgvarg[cArgs-i];
 1649         while (pvar->vt == (VT_BYREF | VT_VARIANT))
 1650             pvar = pvar->pvarVal;
 1651         VariantToToken(*pvar, param_token[i]);
 1652         param[i] = &param_token[i];
 1653     }
 1654     
 1655     result_token.buf = result_token_buf; // May be used below for short return values and misc purposes.
 1656     result_token.marker = _T("");
 1657     result_token.symbol = SYM_STRING;   // These must be initialized for the cleanup code below.
 1658     result_token.mem_to_free = NULL;    //
 1659     
 1660     ExprTokenType this_token;
 1661     this_token.symbol = SYM_OBJECT;
 1662     this_token.object = this;
 1663 
 1664     HRESULT result_to_return;
 1665     int outer_excptmode = g->ExcptMode;
 1666     g->ExcptMode |= EXCPTMODE_CATCH; // Indicate exceptions will be handled (by our caller, the COM client).
 1667 
 1668     for (;;)
 1669     {
 1670         switch (static_cast<IObject *>(this)->Invoke(result_token, this_token, flags, first_param, param_count))
 1671         {
 1672         case FAIL:
 1673             result_to_return = E_FAIL;
 1674             if (g->ThrownToken)
 1675             {
 1676                 Object *obj;
 1677                 if (pExcepInfo && (obj = dynamic_cast<Object*>(TokenToObject(*g->ThrownToken)))) // MSDN: pExcepInfo "Can be NULL"
 1678                 {
 1679                     ZeroMemory(pExcepInfo, sizeof(EXCEPINFO));
 1680                     pExcepInfo->scode = result_to_return = DISP_E_EXCEPTION;
 1681                     
 1682                     #define SysStringFromToken(...) \
 1683                         SysAllocString(CStringWCharFromTCharIfNeeded(TokenToString(__VA_ARGS__)))
 1684 
 1685                     ExprTokenType token;
 1686                     if (obj->GetItem(token, _T("Message")))
 1687                         pExcepInfo->bstrDescription = SysStringFromToken(token, result_token_buf);
 1688                     if (obj->GetItem(token, _T("What")))
 1689                         pExcepInfo->bstrSource = SysStringFromToken(token, result_token_buf);
 1690                     if (obj->GetItem(token, _T("File")))
 1691                         pExcepInfo->bstrHelpFile = SysStringFromToken(token, result_token_buf);
 1692                     if (obj->GetItem(token, _T("Line")))
 1693                         pExcepInfo->dwHelpContext = (DWORD)TokenToInt64(token);
 1694                 }
 1695                 g_script.FreeExceptionToken(g->ThrownToken);
 1696             }
 1697             break;
 1698         case INVOKE_NOT_HANDLED:
 1699             if ((flags & IT_BITMASK) != IT_GET)
 1700             {
 1701                 if (wFlags & DISPATCH_PROPERTYGET)
 1702                 {
 1703                     flags = IT_GET;
 1704                     continue;
 1705                 }
 1706                 result_to_return = DISP_E_MEMBERNOTFOUND;
 1707                 break;
 1708             }
 1709         default:
 1710             result_to_return = S_OK;
 1711             if (pVarResult)
 1712                 TokenToVariant(result_token, *pVarResult, FALSE);
 1713         }
 1714         break;
 1715     }
 1716 
 1717     g->ExcptMode = outer_excptmode;
 1718 
 1719     if (result_token.symbol == SYM_OBJECT)
 1720         result_token.object->Release();
 1721     if (result_token.mem_to_free)
 1722         free(result_token.mem_to_free);
 1723 
 1724     for (UINT i = 1; i <= cArgs; ++i)
 1725     {
 1726         // Release objects (some or all of which may have been created by VariantToToken()):
 1727         if (param_token[i].symbol == SYM_OBJECT)
 1728             param_token[i].object->Release();
 1729         // Free any temporary memory used to hold strings; see VariantToToken().
 1730         else if (param_token[i].symbol == SYM_STRING && param_token[i].mem_to_free)
 1731             free(param_token[i].mem_to_free);
 1732     }
 1733 
 1734     return result_to_return;
 1735 }
 1736 
 1737 
 1738 #ifdef CONFIG_DEBUGGER
 1739 
 1740 void WriteComObjType(IDebugProperties *aDebugger, ComObject *aObject, LPCSTR aName, LPTSTR aWhichType)
 1741 {
 1742     TCHAR buf[MAX_NUMBER_SIZE];
 1743     ExprTokenType resultToken, paramToken[2], *param[2] = { &paramToken[0], &paramToken[1] };
 1744     paramToken[0].symbol = SYM_OBJECT;
 1745     paramToken[0].object = aObject;
 1746     paramToken[1].symbol = SYM_STRING;
 1747     paramToken[1].marker = aWhichType;
 1748     resultToken.symbol = SYM_INTEGER;
 1749     resultToken.marker = _T("ComObjType");
 1750     resultToken.mem_to_free = NULL;
 1751     resultToken.buf = buf;
 1752     ResultType result = OK;
 1753     BIF_ComObjTypeOrValue(result, resultToken, param, 2);
 1754     aDebugger->WriteProperty(aName, resultToken);
 1755     if (resultToken.mem_to_free)
 1756         free(resultToken.mem_to_free);
 1757 }
 1758 
 1759 void ComObject::DebugWriteProperty(IDebugProperties *aDebugger, int aPage, int aPageSize, int aDepth)
 1760 {
 1761     DebugCookie rootCookie;
 1762     aDebugger->BeginProperty(NULL, "object", 2 + (mVarType == VT_DISPATCH)*2 + (mEventSink != NULL), rootCookie);
 1763     if (aPage == 0)
 1764     {
 1765         // For simplicity, assume they all fit within aPageSize.
 1766         
 1767         aDebugger->WriteProperty("Value", ExprTokenType(mVal64));
 1768         aDebugger->WriteProperty("VarType", ExprTokenType((__int64)mVarType));
 1769 
 1770         if (mVarType == VT_DISPATCH)
 1771         {
 1772             WriteComObjType(aDebugger, this, "DispatchType", _T("Name"));
 1773             WriteComObjType(aDebugger, this, "DispatchIID", _T("IID"));
 1774         }
 1775         
 1776         if (mEventSink)
 1777         {
 1778             DebugCookie sinkCookie;
 1779             aDebugger->BeginProperty("EventSink", "object", 2, sinkCookie);
 1780             
 1781             if (mEventSink->mAhkObject)
 1782                 aDebugger->WriteProperty("Object", ExprTokenType(mEventSink->mAhkObject));
 1783             else
 1784                 aDebugger->WriteProperty("Prefix", ExprTokenType(mEventSink->mPrefix));
 1785             
 1786             OLECHAR buf[40];
 1787             if (!StringFromGUID2(mEventSink->mIID, buf, _countof(buf)))
 1788                 *buf = 0;
 1789             aDebugger->WriteProperty("IID", ExprTokenType((LPTSTR)(LPCTSTR)CStringTCharFromWCharIfNeeded(buf)));
 1790             
 1791             aDebugger->EndProperty(sinkCookie);
 1792         }
 1793     }
 1794     aDebugger->EndProperty(rootCookie);
 1795 }
 1796 
 1797 #endif