]>
git.saurik.com Git - wxWidgets.git/blob - src/msw/ole/automtn.cpp
   1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/ole/automtn.cpp 
   3 // Purpose:     OLE automation utilities 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) 1998, Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // For compilers that support precompilation, includes "wx.h". 
  13 #include "wx/wxprec.h" 
  15 #if defined(__BORLANDC__) 
  19 // Watcom C++ gives a linker error if this is compiled in. 
  20 // With Borland C++, all samples crash if this is compiled in. 
  21 #if wxUSE_OLE && !(defined(__BORLANDC__) && (__BORLANDC__ < 0x520)) && !defined(__CYGWIN10__) 
  28 #define _FORCENAMELESSUNION 
  29 #include "wx/msw/private.h" 
  30 #include "wx/msw/ole/oleutils.h" 
  31 #include "wx/msw/ole/automtn.h" 
  34 #include "wx/msw/wince/time.h" 
  52 #include "wx/datetime.h" 
  53 #endif // wxUSE_TIMEDATE 
  55 static void ClearVariant(VARIANTARG 
*pvarg
) ; 
  56 static void ReleaseVariant(VARIANTARG 
*pvarg
) ; 
  57 // static void ShowException(LPOLESTR szMember, HRESULT hr, EXCEPINFO *pexcep, unsigned int uiArgErr); 
  63 wxAutomationObject::wxAutomationObject(WXIDISPATCH
* dispatchPtr
) 
  65     m_dispatchPtr 
= dispatchPtr
; 
  68 wxAutomationObject::~wxAutomationObject() 
  72         ((IDispatch
*)m_dispatchPtr
)->Release(); 
  77 #define INVOKEARG(i) (args ? args[i] : *(ptrArgs[i])) 
  79 // For Put/Get, no named arguments are allowed. 
  80 bool wxAutomationObject::Invoke(const wxString
& member
, int action
, 
  81         wxVariant
& retValue
, int noArgs
, wxVariant args
[], const wxVariant
* ptrArgs
[]) const 
  86     // nonConstMember is necessary because the wxString class doesn't have enough consts... 
  87     wxString 
nonConstMember(member
); 
  89     int ch 
= nonConstMember
.Find('.'); 
  92         // Use dot notation to get the next object 
  93         wxString 
member2(nonConstMember
.Left((size_t) ch
)); 
  94         wxString 
rest(nonConstMember
.Right(nonConstMember
.length() - ch 
- 1)); 
  95         wxAutomationObject obj
; 
  96         if (!GetObject(obj
, member2
)) 
  98         return obj
.Invoke(rest
, action
, retValue
, noArgs
, args
, ptrArgs
); 
 102     ClearVariant(& vReturn
); 
 104     VARIANTARG
* vReturnPtr 
= & vReturn
; 
 106     // Find number of names args 
 107     int namedArgCount 
= 0; 
 109     for (i 
= 0; i 
< noArgs
; i
++) 
 110         if (!INVOKEARG(i
).GetName().IsNull()) 
 115     int namedArgStringCount 
= namedArgCount 
+ 1; 
 116     BSTR
* argNames 
= new BSTR
[namedArgStringCount
]; 
 117     argNames
[0] = wxConvertStringToOle(member
); 
 119     // Note that arguments are specified in reverse order 
 120     // (all totally logical; hey, we're dealing with OLE here.) 
 123     for (i 
= 0; i 
< namedArgCount
; i
++) 
 125         if (!INVOKEARG(i
).GetName().IsNull()) 
 127             argNames
[(namedArgCount
-j
)] = wxConvertStringToOle(INVOKEARG(i
).GetName()); 
 132     // + 1 for the member name, + 1 again in case we're a 'put' 
 133     DISPID
* dispIds 
= new DISPID
[namedArgCount 
+ 2]; 
 136     DISPPARAMS dispparams
; 
 137     unsigned int uiArgErr
; 
 140     // Get the IDs for the member and its arguments.  GetIDsOfNames expects the 
 141     // member name as the first name, followed by argument names (if any). 
 142     hr 
= ((IDispatch
*)m_dispatchPtr
)->GetIDsOfNames(IID_NULL
, argNames
, 
 143                                 1 + namedArgCount
, LOCALE_SYSTEM_DEFAULT
, dispIds
); 
 146 //        ShowException(szMember, hr, NULL, 0); 
 152     // if doing a property put(ref), we need to adjust the first argument to have a 
 153     // named arg of DISPID_PROPERTYPUT. 
 154     if (action 
& (DISPATCH_PROPERTYPUT 
| DISPATCH_PROPERTYPUTREF
)) 
 157         dispIds
[1] = DISPID_PROPERTYPUT
; 
 158         vReturnPtr 
= (VARIANTARG
*) NULL
; 
 161     // Convert the wxVariants to VARIANTARGs 
 162     VARIANTARG
* oleArgs 
= new VARIANTARG
[noArgs
]; 
 163     for (i 
= 0; i 
< noArgs
; i
++) 
 165         // Again, reverse args 
 166         if (!wxConvertVariantToOle(INVOKEARG((noArgs
-1) - i
), oleArgs
[i
])) 
 175     dispparams
.rgdispidNamedArgs 
= dispIds 
+ 1; 
 176     dispparams
.rgvarg 
= oleArgs
; 
 177     dispparams
.cArgs 
= noArgs
; 
 178     dispparams
.cNamedArgs 
= namedArgCount
; 
 180     excep
.pfnDeferredFillIn 
= NULL
; 
 182     hr 
= ((IDispatch
*)m_dispatchPtr
)->Invoke(dispIds
[0], IID_NULL
, LOCALE_SYSTEM_DEFAULT
, 
 183                         (WORD
)action
, &dispparams
, vReturnPtr
, &excep
, &uiArgErr
); 
 185     for (i 
= 0; i 
< namedArgStringCount
; i
++) 
 187         SysFreeString(argNames
[i
]); 
 192     for (i 
= 0; i 
< noArgs
; i
++) 
 193         ReleaseVariant(& oleArgs
[i
]) ; 
 198         // display the exception information if appropriate: 
 199 //        ShowException((const char*) member, hr, &excep, uiArgErr); 
 201         // free exception structure information 
 202         SysFreeString(excep
.bstrSource
); 
 203         SysFreeString(excep
.bstrDescription
); 
 204         SysFreeString(excep
.bstrHelpFile
); 
 207             ReleaseVariant(vReturnPtr
); 
 214             // Convert result to wxVariant form 
 215             wxConvertOleToVariant(vReturn
, retValue
); 
 216             // Mustn't release the dispatch pointer 
 217             if (vReturn
.vt 
== VT_DISPATCH
) 
 219                 vReturn
.pdispVal 
= (IDispatch
*) NULL
; 
 221             ReleaseVariant(& vReturn
); 
 227 // Invoke a member function 
 228 wxVariant 
wxAutomationObject::CallMethod(const wxString
& member
, int noArgs
, wxVariant args
[]) 
 230     wxVariant retVariant
; 
 231     if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, noArgs
, args
)) 
 233         retVariant
.MakeNull(); 
 238 wxVariant 
wxAutomationObject::CallMethodArray(const wxString
& member
, int noArgs
, const wxVariant 
**args
) 
 240     wxVariant retVariant
; 
 241     if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, noArgs
, NULL
, args
)) 
 243         retVariant
.MakeNull(); 
 248 wxVariant 
wxAutomationObject::CallMethod(const wxString
& member
, 
 249         const wxVariant
& arg1
, const wxVariant
& arg2
, 
 250         const wxVariant
& arg3
, const wxVariant
& arg4
, 
 251         const wxVariant
& arg5
, const wxVariant
& arg6
) 
 253     const wxVariant
** args 
= new const wxVariant
*[6]; 
 285     wxVariant retVariant
; 
 286     if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, i
, NULL
, args
)) 
 288         retVariant
.MakeNull(); 
 295 wxVariant 
wxAutomationObject::GetPropertyArray(const wxString
& property
, int noArgs
, const wxVariant 
**args
) const 
 297     wxVariant retVariant
; 
 298     if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, NULL
, args
)) 
 300         retVariant
.MakeNull(); 
 304 wxVariant 
wxAutomationObject::GetProperty(const wxString
& property
, int noArgs
, wxVariant args
[]) const 
 306     wxVariant retVariant
; 
 307     if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, args
)) 
 309         retVariant
.MakeNull(); 
 314 wxVariant 
wxAutomationObject::GetProperty(const wxString
& property
, 
 315         const wxVariant
& arg1
, const wxVariant
& arg2
, 
 316         const wxVariant
& arg3
, const wxVariant
& arg4
, 
 317         const wxVariant
& arg5
, const wxVariant
& arg6
) 
 319     const wxVariant
** args 
= new const wxVariant
*[6]; 
 351     wxVariant retVariant
; 
 352     if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, i
, NULL
, args
)) 
 354         retVariant
.MakeNull(); 
 360 bool wxAutomationObject::PutProperty(const wxString
& property
, int noArgs
, wxVariant args
[]) 
 362     wxVariant retVariant
; 
 363     if (!Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, noArgs
, args
)) 
 370 bool wxAutomationObject::PutPropertyArray(const wxString
& property
, int noArgs
, const wxVariant 
**args
) 
 372     wxVariant retVariant
; 
 373     if (!Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, noArgs
, NULL
, args
)) 
 380 bool wxAutomationObject::PutProperty(const wxString
& property
, 
 381         const wxVariant
& arg1
, const wxVariant
& arg2
, 
 382         const wxVariant
& arg3
, const wxVariant
& arg4
, 
 383         const wxVariant
& arg5
, const wxVariant
& arg6
) 
 385     const wxVariant
** args 
= new const wxVariant
*[6]; 
 417     wxVariant retVariant
; 
 418     bool ret 
= Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, i
, NULL
, args
); 
 424 // Uses DISPATCH_PROPERTYGET 
 425 // and returns a dispatch pointer. The calling code should call Release 
 426 // on the pointer, though this could be implicit by constructing an wxAutomationObject 
 427 // with it and letting the destructor call Release. 
 428 WXIDISPATCH
* wxAutomationObject::GetDispatchProperty(const wxString
& property
, int noArgs
, wxVariant args
[]) const 
 430     wxVariant retVariant
; 
 431     if (Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, args
)) 
 433         if (retVariant
.GetType() == wxT("void*")) 
 435             return (WXIDISPATCH
*) retVariant
.GetVoidPtr(); 
 439     return (WXIDISPATCH
*) NULL
; 
 442 // Uses DISPATCH_PROPERTYGET 
 443 // and returns a dispatch pointer. The calling code should call Release 
 444 // on the pointer, though this could be implicit by constructing an wxAutomationObject 
 445 // with it and letting the destructor call Release. 
 446 WXIDISPATCH
* wxAutomationObject::GetDispatchProperty(const wxString
& property
, int noArgs
, const wxVariant 
**args
) const 
 448     wxVariant retVariant
; 
 449     if (Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, NULL
, args
)) 
 451         if (retVariant
.GetType() == wxT("void*")) 
 453             return (WXIDISPATCH
*) retVariant
.GetVoidPtr(); 
 457     return (WXIDISPATCH
*) NULL
; 
 461 // A way of initialising another wxAutomationObject with a dispatch object 
 462 bool wxAutomationObject::GetObject(wxAutomationObject
& obj
, const wxString
& property
, int noArgs
, wxVariant args
[]) const 
 464     WXIDISPATCH
* dispatch 
= GetDispatchProperty(property
, noArgs
, args
); 
 467         obj
.SetDispatchPtr(dispatch
); 
 474 // A way of initialising another wxAutomationObject with a dispatch object 
 475 bool wxAutomationObject::GetObject(wxAutomationObject
& obj
, const wxString
& property
, int noArgs
, const wxVariant 
**args
) const 
 477     WXIDISPATCH
* dispatch 
= GetDispatchProperty(property
, noArgs
, args
); 
 480         obj
.SetDispatchPtr(dispatch
); 
 487 // Get a dispatch pointer from the current object associated 
 489 bool wxAutomationObject::GetInstance(const wxString
& classId
) const 
 495     IUnknown 
* pUnk 
= NULL
; 
 497     wxBasicString 
unicodeName(classId
.mb_str()); 
 499     if (FAILED(CLSIDFromProgID((BSTR
) unicodeName
, &clsId
))) 
 501         wxLogWarning(wxT("Cannot obtain CLSID from ProgID")); 
 505     if (FAILED(GetActiveObject(clsId
, NULL
, &pUnk
))) 
 507         wxLogWarning(wxT("Cannot find an active object")); 
 511     if (pUnk
->QueryInterface(IID_IDispatch
, (LPVOID
*) &m_dispatchPtr
) != S_OK
) 
 513         wxLogWarning(wxT("Cannot find IDispatch interface")); 
 520 // Get a dispatch pointer from a new object associated 
 521 // with the given class id 
 522 bool wxAutomationObject::CreateInstance(const wxString
& classId
) const 
 529     wxBasicString 
unicodeName(classId
.mb_str()); 
 531     if (FAILED(CLSIDFromProgID((BSTR
) unicodeName
, &clsId
))) 
 533         wxLogWarning(wxT("Cannot obtain CLSID from ProgID")); 
 537     // start a new copy of Excel, grab the IDispatch interface 
 538     if (FAILED(CoCreateInstance(clsId
, NULL
, CLSCTX_LOCAL_SERVER
, IID_IDispatch
, (void**)&m_dispatchPtr
))) 
 540         wxLogWarning(wxT("Cannot start an instance of this class.")); 
 548 WXDLLEXPORT 
bool wxConvertVariantToOle(const wxVariant
& variant
, VARIANTARG
& oleVariant
) 
 550     ClearVariant(&oleVariant
); 
 551     if (variant
.IsNull()) 
 553         oleVariant
.vt 
= VT_NULL
; 
 557     wxString 
type(variant
.GetType()); 
 560     if (type 
== wxT("long")) 
 562         oleVariant
.vt 
= VT_I4
; 
 563         oleVariant
.lVal 
= variant
.GetLong() ; 
 565     // cVal not always present 
 567     else if (type 
== wxT("char")) 
 569         oleVariant
.vt
=VT_I1
;            // Signed Char 
 570         oleVariant
.cVal
=variant
.GetChar(); 
 573     else if (type 
== wxT("double")) 
 575         oleVariant
.vt 
= VT_R8
; 
 576         oleVariant
.dblVal 
= variant
.GetDouble(); 
 578     else if (type 
== wxT("bool")) 
 580         oleVariant
.vt 
= VT_BOOL
; 
 581         // 'bool' required for VC++ 4 apparently 
 582 #if (defined(__VISUALC__) && (__VISUALC__ <= 1000)) 
 583         oleVariant
.bool = variant
.GetBool(); 
 585         oleVariant
.boolVal 
= variant
.GetBool(); 
 588     else if (type 
== wxT("string")) 
 590         wxString 
str( variant
.GetString() ); 
 591         oleVariant
.vt 
= VT_BSTR
; 
 592         oleVariant
.bstrVal 
= wxConvertStringToOle(str
); 
 595     else if (type 
== wxT("datetime")) 
 597         wxDateTime 
date( variant
.GetDateTime() ); 
 598         oleVariant
.vt 
= VT_DATE
; 
 600         long dosDateTime 
= date
.GetAsDOS(); 
 601         short dosDate 
= short((dosDateTime 
& 0xFFFF0000) >> 16); 
 602         short dosTime 
= short(dosDateTime 
& 0xFFFF); 
 604         DosDateTimeToVariantTime(dosDate
, dosTime
, & oleVariant
.date
); 
 607     else if (type 
== wxT("void*")) 
 609         oleVariant
.vt 
= VT_DISPATCH
; 
 610         oleVariant
.pdispVal 
= (IDispatch
*) variant
.GetVoidPtr(); 
 612     else if (type 
== wxT("list") || type 
== wxT("stringlist")) 
 614         oleVariant
.vt 
= VT_VARIANT 
| VT_ARRAY
; 
 617         SAFEARRAYBOUND saBound
; 
 618         VARIANTARG 
*pvargBase
; 
 622         int iCount 
= variant
.GetCount(); 
 625         saBound
.cElements 
= iCount
; 
 627         psa 
= SafeArrayCreate(VT_VARIANT
, 1, &saBound
); 
 631         SafeArrayAccessData(psa
, (void**)&pvargBase
); 
 634         for (i 
= 0; i 
< iCount
; i
++) 
 636             // copy each string in the list of strings 
 637             wxVariant 
eachVariant(variant
[i
]); 
 638             if (!wxConvertVariantToOle(eachVariant
, * pvarg
)) 
 640                 // memory failure:  back out and free strings alloc'ed up to 
 641                 // now, and then the array itself. 
 643                 for (j 
= 0; j 
< i
; j
++) 
 645                     SysFreeString(pvarg
->bstrVal
); 
 648                 SafeArrayDestroy(psa
); 
 654         SafeArrayUnaccessData(psa
); 
 656         oleVariant
.parray 
= psa
; 
 660         oleVariant
.vt 
= VT_NULL
; 
 667 #define VT_TYPEMASK 0xfff 
 670 WXDLLEXPORT 
bool wxConvertOleToVariant(const VARIANTARG
& oleVariant
, wxVariant
& variant
) 
 672     switch (oleVariant
.vt 
& VT_TYPEMASK
) 
 676             wxString 
str(wxConvertStringFromOle(oleVariant
.bstrVal
)); 
 683             unsigned short dosDate 
= 0; 
 684             unsigned short dosTime 
= 0; 
 685             VariantTimeToDosDateTime(oleVariant
.date
, & dosDate
, & dosTime
); 
 687             long dosDateTime 
= (dosDate 
<< 16) || dosTime
; 
 689             date
.SetFromDOS(dosDateTime
); 
 696             variant 
= (long) oleVariant
.lVal
; 
 701             variant 
= (long) oleVariant
.iVal
; 
 707 #if (defined(_MSC_VER) && (_MSC_VER <= 1000) && !defined(__MWERKS__) ) //GC 
 708 #ifndef HAVE_BOOL // Can't use bool operator if no native bool type 
 709             variant 
= (long) (oleVariant
.bool != 0); 
 711             variant 
= (bool) (oleVariant
.bool != 0); 
 714 #ifndef HAVE_BOOL // Can't use bool operator if no native bool type 
 715             variant 
= (long) (oleVariant
.boolVal 
!= 0); 
 717             variant 
= (bool) (oleVariant
.boolVal 
!= 0); 
 724             variant 
= oleVariant
.dblVal
; 
 731             int cDims
, cElements
, i
; 
 734             // Iterate the dimensions: number of elements is x*y*z 
 735             for (cDims 
= 0, cElements 
= 1; 
 736                 cDims 
< oleVariant
.parray
->cDims
; cDims 
++) 
 737                     cElements 
*= oleVariant
.parray
->rgsabound
[cDims
].cElements
; 
 739             // Get a pointer to the data 
 740             HRESULT hr 
= SafeArrayAccessData(oleVariant
.parray
, (void HUGEP
* FAR
*) & pvdata
); 
 744             for (i 
= 0; i 
< cElements
; i
++) 
 746                 VARIANTARG
& oleElement 
= pvdata
[i
]; 
 748                 if (!wxConvertOleToVariant(oleElement
, vElement
)) 
 751                 variant
.Append(vElement
); 
 753             SafeArrayUnaccessData(oleVariant
.parray
); 
 758             variant 
= (void*) oleVariant
.pdispVal
; 
 768             break;    // Ignore Empty Variant, used only during destruction of objects 
 772             wxLogError(wxT("wxAutomationObject::ConvertOleToVariant: Unknown variant value type")); 
 782  *  Zeros a variant structure without regard to current contents 
 784 static void ClearVariant(VARIANTARG 
*pvarg
) 
 786     pvarg
->vt 
= VT_EMPTY
; 
 787     pvarg
->wReserved1 
= 0; 
 788     pvarg
->wReserved2 
= 0; 
 789     pvarg
->wReserved3 
= 0; 
 796  *  Clears a particular variant structure and releases any external objects 
 797  *  or memory contained in the variant.  Supports the data types listed above. 
 799 static void ReleaseVariant(VARIANTARG 
*pvarg
) 
 802     VARIANTARG _huge 
*pvargArray
; 
 803     long lLBound
, lUBound
, l
; 
 805     vt 
= (VARTYPE
)(pvarg
->vt 
& 0xfff);        // mask off flags 
 807     // check if an array.  If so, free its contents, then the array itself. 
 808     if (V_ISARRAY(pvarg
)) 
 810         // variant arrays are all this routine currently knows about.  Since a 
 811         // variant can contain anything (even other arrays), call ourselves 
 813         if (vt 
== VT_VARIANT
) 
 815             SafeArrayGetLBound(pvarg
->parray
, 1, &lLBound
); 
 816             SafeArrayGetUBound(pvarg
->parray
, 1, &lUBound
); 
 818             if (lUBound 
> lLBound
) 
 822                 SafeArrayAccessData(pvarg
->parray
, (void**)&pvargArray
); 
 824                 for (l 
= 0; l 
< lUBound
; l
++) 
 826                     ReleaseVariant(pvargArray
); 
 830                 SafeArrayUnaccessData(pvarg
->parray
); 
 835             wxLogWarning(wxT("ReleaseVariant: Array contains non-variant type")); 
 838         // Free the array itself. 
 839         SafeArrayDestroy(pvarg
->parray
); 
 847                     pvarg
->pdispVal
->Release(); 
 851                 SysFreeString(pvarg
->bstrVal
); 
 858             case VT_ERROR
:        // to avoid erroring on an error return from Excel 
 860                 // no work for these types 
 864                 wxLogWarning(wxT("ReleaseVariant: Unknown type")); 
 874 void ShowException(LPOLESTR szMember
, HRESULT hr
, EXCEPINFO 
*pexcep
, unsigned int uiArgErr
) 
 878     switch (GetScode(hr
)) 
 880         case DISP_E_UNKNOWNNAME
: 
 881             wsprintf(szBuf
, L
"%s: Unknown name or named argument.", szMember
); 
 884         case DISP_E_BADPARAMCOUNT
: 
 885             wsprintf(szBuf
, L
"%s: Incorrect number of arguments.", szMember
); 
 888         case DISP_E_EXCEPTION
: 
 889             wsprintf(szBuf
, L
"%s: Error %d: ", szMember
, pexcep
->wCode
); 
 890             if (pexcep
->bstrDescription 
!= NULL
) 
 891                 lstrcat(szBuf
, pexcep
->bstrDescription
); 
 893                 lstrcat(szBuf
, L
"<<No Description>>"); 
 896         case DISP_E_MEMBERNOTFOUND
: 
 897             wsprintf(szBuf
, L
"%s: method or property not found.", szMember
); 
 900         case DISP_E_OVERFLOW
: 
 901             wsprintf(szBuf
, L
"%s: Overflow while coercing argument values.", szMember
); 
 904         case DISP_E_NONAMEDARGS
: 
 905             wsprintf(szBuf
, L
"%s: Object implementation does not support named arguments.", 
 909         case DISP_E_UNKNOWNLCID
: 
 910             wsprintf(szBuf
, L
"%s: The locale ID is unknown.", szMember
); 
 913         case DISP_E_PARAMNOTOPTIONAL
: 
 914             wsprintf(szBuf
, L
"%s: Missing a required parameter.", szMember
); 
 917         case DISP_E_PARAMNOTFOUND
: 
 918             wsprintf(szBuf
, L
"%s: Argument not found, argument %d.", szMember
, uiArgErr
); 
 921         case DISP_E_TYPEMISMATCH
: 
 922             wsprintf(szBuf
, L
"%s: Type mismatch, argument %d.", szMember
, uiArgErr
); 
 926             wsprintf(szBuf
, L
"%s: Unknown error occurred.", szMember
); 
 935 #endif // wxUSE_OLE && !(defined(__BORLANDC__) && (__BORLANDC__ < 0x520)) && !defined(__CYGWIN10__)