]>
git.saurik.com Git - wxWidgets.git/blob - src/msw/ole/automtn.cpp
   1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     OLE automation utilities 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) 1998, Julian Smart 
   9 // Licence:     wxWindows Licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  13 #pragma implementation "automtn.h" 
  16 // For compilers that support precompilation, includes "wx.h". 
  17 #include "wx/wxprec.h" 
  19 #if defined(__BORLANDC__) 
  25 // Watcom C++ gives a linker error if this is compiled in. 
  26 // With Borland C++, all samples crash if this is compiled in. 
  27 #if wxUSE_OLE &&!defined(__WATCOMC__) && !(defined(__BORLANDC__) && (__BORLANDC__ < 0x520)) && !defined(__CYGWIN10__) 
  29 #define _FORCENAMELESSUNION 
  31 #include "wx/msw/private.h" 
  32 #include "wx/msw/ole/oleutils.h" 
  33 #include "wx/msw/ole/automtn.h" 
  38 #include "wx/msw/wince/time.h" 
  55 // Verifies will fail if the needed buffer size is too large 
  56 #define MAX_TIME_BUFFER_SIZE    128         // matches that in timecore.cpp 
  57 #define MIN_DATE                (-657434L)  // about year 100 
  58 #define MAX_DATE                2958465L    // about year 9999 
  60 // Half a second, expressed in days 
  61 #define HALF_SECOND  (1.0/172800.0) 
  63 // One-based array of days in year at month start 
  64 static int rgMonthDays
[13] = 
  65         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; 
  68 #include "wx/datetime.h" 
  70 static BOOL 
OleDateFromTm(WORD wYear
, WORD wMonth
, WORD wDay
, 
  71         WORD wHour
, WORD wMinute
, WORD wSecond
, DATE
& dtDest
); 
  72 static BOOL 
TmFromOleDate(DATE dtSrc
, struct tm
& tmDest
); 
  73 #endif // wxUSE_TIMEDATE 
  75 static void ClearVariant(VARIANTARG 
*pvarg
) ; 
  76 static void ReleaseVariant(VARIANTARG 
*pvarg
) ; 
  77 // static void ShowException(LPOLESTR szMember, HRESULT hr, EXCEPINFO *pexcep, unsigned int uiArgErr); 
  83 wxAutomationObject::wxAutomationObject(WXIDISPATCH
* dispatchPtr
) 
  85         m_dispatchPtr 
= dispatchPtr
; 
  88 wxAutomationObject::~wxAutomationObject() 
  92                 ((IDispatch
*)m_dispatchPtr
)->Release(); 
  97 #define INVOKEARG(i) (args ? args[i] : *(ptrArgs[i])) 
  99 // For Put/Get, no named arguments are allowed. 
 100 bool wxAutomationObject::Invoke(const wxString
& member
, int action
, 
 101         wxVariant
& retValue
, int noArgs
, wxVariant args
[], const wxVariant
* ptrArgs
[]) const 
 106         // nonConstMember is necessary because the wxString class doesn't have enough consts... 
 107         wxString 
nonConstMember(member
); 
 109         int ch 
= nonConstMember
.Find('.'); 
 112                 // Use dot notation to get the next object 
 113                 wxString 
member2(nonConstMember
.Left((size_t) ch
)); 
 114                 wxString 
rest(nonConstMember
.Right(nonConstMember
.Length() - ch 
- 1)); 
 115                 wxAutomationObject obj
; 
 116                 if (!GetObject(obj
, member2
)) 
 118                 return obj
.Invoke(rest
, action
, retValue
, noArgs
, args
, ptrArgs
); 
 122         ClearVariant(& vReturn
); 
 124         VARIANTARG
* vReturnPtr 
= & vReturn
; 
 126         // Find number of names args 
 127         int namedArgCount 
= 0; 
 129         for (i 
= 0; i 
< noArgs
; i
++) 
 130                 if (!INVOKEARG(i
).GetName().IsNull()) 
 135         int namedArgStringCount 
= namedArgCount 
+ 1; 
 136         BSTR
* argNames 
= new BSTR
[namedArgStringCount
]; 
 137         argNames
[0] = wxConvertStringToOle(member
); 
 139         // Note that arguments are specified in reverse order 
 140         // (all totally logical; hey, we're dealing with OLE here.) 
 143         for (i 
= 0; i 
< namedArgCount
; i
++) 
 145                 if (!INVOKEARG(i
).GetName().IsNull()) 
 147                         argNames
[(namedArgCount
-j
)] = wxConvertStringToOle(INVOKEARG(i
).GetName()); 
 152         // + 1 for the member name, + 1 again in case we're a 'put' 
 153         DISPID
* dispIds 
= new DISPID
[namedArgCount 
+ 2]; 
 156         DISPPARAMS dispparams
; 
 157         unsigned int uiArgErr
; 
 160         // Get the IDs for the member and its arguments.  GetIDsOfNames expects the 
 161         // member name as the first name, followed by argument names (if any). 
 162         hr 
= ((IDispatch
*)m_dispatchPtr
)->GetIDsOfNames(IID_NULL
, argNames
, 
 163                                                                 1 + namedArgCount
, LOCALE_SYSTEM_DEFAULT
, dispIds
); 
 166 //              ShowException(szMember, hr, NULL, 0); 
 172         // if doing a property put(ref), we need to adjust the first argument to have a 
 173         // named arg of DISPID_PROPERTYPUT. 
 174         if (action 
& (DISPATCH_PROPERTYPUT 
| DISPATCH_PROPERTYPUTREF
)) 
 177                 dispIds
[1] = DISPID_PROPERTYPUT
; 
 178                 vReturnPtr 
= (VARIANTARG
*) NULL
; 
 181         // Convert the wxVariants to VARIANTARGs 
 182         VARIANTARG
* oleArgs 
= new VARIANTARG
[noArgs
]; 
 183         for (i 
= 0; i 
< noArgs
; i
++) 
 185                 // Again, reverse args 
 186                 if (!wxConvertVariantToOle(INVOKEARG((noArgs
-1) - i
), oleArgs
[i
])) 
 195         dispparams
.rgdispidNamedArgs 
= dispIds 
+ 1; 
 196         dispparams
.rgvarg 
= oleArgs
; 
 197         dispparams
.cArgs 
= noArgs
; 
 198         dispparams
.cNamedArgs 
= namedArgCount
; 
 200         excep
.pfnDeferredFillIn 
= NULL
; 
 202         hr 
= ((IDispatch
*)m_dispatchPtr
)->Invoke(dispIds
[0], IID_NULL
, LOCALE_SYSTEM_DEFAULT
, 
 203                                                 action
, &dispparams
, vReturnPtr
, &excep
, &uiArgErr
); 
 205         for (i 
= 0; i 
< namedArgStringCount
; i
++) 
 207                 SysFreeString(argNames
[i
]); 
 212         for (i 
= 0; i 
< noArgs
; i
++) 
 213                 ReleaseVariant(& oleArgs
[i
]) ; 
 218                 // display the exception information if appropriate: 
 219 //              ShowException((const char*) member, hr, &excep, uiArgErr); 
 221                 // free exception structure information 
 222                 SysFreeString(excep
.bstrSource
); 
 223                 SysFreeString(excep
.bstrDescription
); 
 224                 SysFreeString(excep
.bstrHelpFile
); 
 227                         ReleaseVariant(vReturnPtr
); 
 234                         // Convert result to wxVariant form 
 235                         wxConvertOleToVariant(vReturn
, retValue
); 
 236                         // Mustn't release the dispatch pointer 
 237                         if (vReturn
.vt 
== VT_DISPATCH
) 
 239                                 vReturn
.pdispVal 
= (IDispatch
*) NULL
; 
 241                         ReleaseVariant(& vReturn
); 
 247 // Invoke a member function 
 248 wxVariant 
wxAutomationObject::CallMethod(const wxString
& member
, int noArgs
, wxVariant args
[]) 
 250         wxVariant retVariant
; 
 251         if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, noArgs
, args
)) 
 253                 retVariant
.MakeNull(); 
 258 wxVariant 
wxAutomationObject::CallMethodArray(const wxString
& member
, int noArgs
, const wxVariant 
**args
) 
 260         wxVariant retVariant
; 
 261         if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, noArgs
, NULL
, args
)) 
 263                 retVariant
.MakeNull(); 
 268 wxVariant 
wxAutomationObject::CallMethod(const wxString
& member
, 
 269                 const wxVariant
& arg1
, const wxVariant
& arg2
, 
 270                 const wxVariant
& arg3
, const wxVariant
& arg4
, 
 271                 const wxVariant
& arg5
, const wxVariant
& arg6
) 
 273         const wxVariant
** args 
= new const wxVariant
*[6]; 
 305         wxVariant retVariant
; 
 306         if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, i
, NULL
, args
)) 
 308                 retVariant
.MakeNull(); 
 315 wxVariant 
wxAutomationObject::GetPropertyArray(const wxString
& property
, int noArgs
, const wxVariant 
**args
) const 
 317         wxVariant retVariant
; 
 318         if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, NULL
, args
)) 
 320                 retVariant
.MakeNull(); 
 324 wxVariant 
wxAutomationObject::GetProperty(const wxString
& property
, int noArgs
, wxVariant args
[]) const 
 326         wxVariant retVariant
; 
 327         if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, args
)) 
 329                 retVariant
.MakeNull(); 
 334 wxVariant 
wxAutomationObject::GetProperty(const wxString
& property
, 
 335                 const wxVariant
& arg1
, const wxVariant
& arg2
, 
 336                 const wxVariant
& arg3
, const wxVariant
& arg4
, 
 337                 const wxVariant
& arg5
, const wxVariant
& arg6
) 
 339         const wxVariant
** args 
= new const wxVariant
*[6]; 
 371         wxVariant retVariant
; 
 372         if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, i
, NULL
, args
)) 
 374                 retVariant
.MakeNull(); 
 380 bool wxAutomationObject::PutProperty(const wxString
& property
, int noArgs
, wxVariant args
[]) 
 382         wxVariant retVariant
; 
 383         if (!Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, noArgs
, args
)) 
 390 bool wxAutomationObject::PutPropertyArray(const wxString
& property
, int noArgs
, const wxVariant 
**args
) 
 392         wxVariant retVariant
; 
 393         if (!Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, noArgs
, NULL
, args
)) 
 400 bool wxAutomationObject::PutProperty(const wxString
& property
, 
 401                 const wxVariant
& arg1
, const wxVariant
& arg2
, 
 402                 const wxVariant
& arg3
, const wxVariant
& arg4
, 
 403                 const wxVariant
& arg5
, const wxVariant
& arg6
) 
 405         const wxVariant
** args 
= new const wxVariant
*[6]; 
 437         wxVariant retVariant
; 
 438         bool ret 
= Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, i
, NULL
, args
); 
 444 // Uses DISPATCH_PROPERTYGET 
 445 // and returns a dispatch pointer. The calling code should call Release 
 446 // on the pointer, though this could be implicit by constructing an wxAutomationObject 
 447 // with it and letting the destructor call Release. 
 448 WXIDISPATCH
* wxAutomationObject::GetDispatchProperty(const wxString
& property
, int noArgs
, wxVariant args
[]) const 
 450         wxVariant retVariant
; 
 451         if (Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, args
)) 
 453                 if (retVariant
.GetType() == wxT("void*")) 
 455                         return (WXIDISPATCH
*) retVariant
.GetVoidPtr(); 
 459         return (WXIDISPATCH
*) NULL
; 
 462 // Uses DISPATCH_PROPERTYGET 
 463 // and returns a dispatch pointer. The calling code should call Release 
 464 // on the pointer, though this could be implicit by constructing an wxAutomationObject 
 465 // with it and letting the destructor call Release. 
 466 WXIDISPATCH
* wxAutomationObject::GetDispatchProperty(const wxString
& property
, int noArgs
, const wxVariant 
**args
) const 
 468         wxVariant retVariant
; 
 469         if (Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, NULL
, args
)) 
 471                 if (retVariant
.GetType() == wxT("void*")) 
 473                         return (WXIDISPATCH
*) retVariant
.GetVoidPtr(); 
 477         return (WXIDISPATCH
*) NULL
; 
 481 // A way of initialising another wxAutomationObject with a dispatch object 
 482 bool wxAutomationObject::GetObject(wxAutomationObject
& obj
, const wxString
& property
, int noArgs
, wxVariant args
[]) const 
 484         WXIDISPATCH
* dispatch 
= GetDispatchProperty(property
, noArgs
, args
); 
 487                 obj
.SetDispatchPtr(dispatch
); 
 494 // A way of initialising another wxAutomationObject with a dispatch object 
 495 bool wxAutomationObject::GetObject(wxAutomationObject
& obj
, const wxString
& property
, int noArgs
, const wxVariant 
**args
) const 
 497         WXIDISPATCH
* dispatch 
= GetDispatchProperty(property
, noArgs
, args
); 
 500                 obj
.SetDispatchPtr(dispatch
); 
 507 // Get a dispatch pointer from the current object associated 
 509 bool wxAutomationObject::GetInstance(const wxString
& classId
) const 
 515         IUnknown 
* pUnk 
= NULL
; 
 517         wxBasicString 
unicodeName(classId
.mb_str()); 
 519         if (FAILED(CLSIDFromProgID((BSTR
) unicodeName
, &clsId
)))  
 521                 wxLogWarning(wxT("Cannot obtain CLSID from ProgID")); 
 525         if (FAILED(GetActiveObject(clsId
, NULL
, &pUnk
))) 
 527                 wxLogWarning(wxT("Cannot find an active object")); 
 531         if (pUnk
->QueryInterface(IID_IDispatch
, (LPVOID
*) &m_dispatchPtr
) != S_OK
) 
 533                 wxLogWarning(wxT("Cannot find IDispatch interface")); 
 540 // Get a dispatch pointer from a new object associated 
 541 // with the given class id 
 542 bool wxAutomationObject::CreateInstance(const wxString
& classId
) const 
 549         wxBasicString 
unicodeName(classId
.mb_str()); 
 551         if (FAILED(CLSIDFromProgID((BSTR
) unicodeName
, &clsId
)))  
 553                 wxLogWarning(wxT("Cannot obtain CLSID from ProgID")); 
 557         // start a new copy of Excel, grab the IDispatch interface 
 558         if (FAILED(CoCreateInstance(clsId
, NULL
, CLSCTX_LOCAL_SERVER
, IID_IDispatch
, (void**)&m_dispatchPtr
)))  
 560                 wxLogWarning(wxT("Cannot start an instance of this class.")); 
 568 bool wxConvertVariantToOle(const wxVariant
& variant
, VARIANTARG
& oleVariant
) 
 570         ClearVariant(&oleVariant
); 
 571         if (variant
.IsNull()) 
 573                 oleVariant
.vt 
= VT_NULL
; 
 577     wxString 
type(variant
.GetType()); 
 580     if (type 
== wxT("long")) 
 582         oleVariant
.vt 
= VT_I4
; 
 583         oleVariant
.lVal 
= variant
.GetLong() ; 
 585     // cVal not always present 
 587     else if (type 
== wxT("char")) 
 589         oleVariant
.vt
=VT_I1
;                    // Signed Char 
 590         oleVariant
.cVal
=variant
.GetChar(); 
 593     else if (type 
== wxT("double")) 
 595         oleVariant
.vt 
= VT_R8
; 
 596         oleVariant
.dblVal 
= variant
.GetDouble(); 
 598     else if (type 
== wxT("bool")) 
 600         oleVariant
.vt 
= VT_BOOL
; 
 601         // 'bool' required for VC++ 4 apparently 
 602 #if defined(__WATCOMC__) || (defined(__VISUALC__) && (__VISUALC__ <= 1000)) 
 603         oleVariant
.bool = variant
.GetBool(); 
 605         oleVariant
.boolVal 
= variant
.GetBool(); 
 608     else if (type 
== wxT("string")) 
 610         wxString 
str( variant
.GetString() ); 
 611         oleVariant
.vt 
= VT_BSTR
; 
 612         oleVariant
.bstrVal 
= wxConvertStringToOle(str
); 
 614 // For some reason, Watcom C++ can't link variant.cpp with time/date classes compiled 
 616 #if 0 // wxUSE_TIMEDATE && !defined(__WATCOMC__) 
 617     else if (type 
== wxT("date")) 
 619         wxDate 
date( variant
.GetDate() ); 
 620         oleVariant
.vt 
= VT_DATE
; 
 622                 if (!OleDateFromTm(date
.GetYear(), date
.GetMonth(), date
.GetDay(), 
 623                                 0, 0, 0, oleVariant
.date
)) 
 626     else if (type 
== wxT("time")) 
 628         wxTime 
time( variant
.GetTime() ); 
 629         oleVariant
.vt 
= VT_DATE
; 
 631                 if (!OleDateFromTm(time
.GetYear(), time
.GetMonth(), time
.GetDay(), 
 632                         time
.GetHour(), time
.GetMinute(), time
.GetSecond(), oleVariant
.date
)) 
 637     else if (type 
== wxT("datetime")) 
 639         wxDateTime 
date( variant
.GetDateTime() ); 
 640         oleVariant
.vt 
= VT_DATE
; 
 642                 if (!OleDateFromTm(date
.GetYear(), date
.GetMonth(), date
.GetDay(), 
 643                                 date
.GetHour(), date
.GetMinute(), date
.GetSecond(), oleVariant
.date
)) 
 647     else if (type 
== wxT("void*")) 
 649         oleVariant
.vt 
= VT_DISPATCH
; 
 650         oleVariant
.pdispVal 
= (IDispatch
*) variant
.GetVoidPtr(); 
 652     else if (type 
== wxT("list") || type 
== wxT("stringlist")) 
 654         oleVariant
.vt 
= VT_VARIANT 
| VT_ARRAY
; 
 657             SAFEARRAYBOUND saBound
; 
 658             VARIANTARG 
*pvargBase
; 
 662         int iCount 
= variant
.GetCount(); 
 665             saBound
.cElements 
= iCount
; 
 667             psa 
= SafeArrayCreate(VT_VARIANT
, 1, &saBound
); 
 671             SafeArrayAccessData(psa
, (void**)&pvargBase
); 
 674             for (i 
= 0; i 
< iCount
; i
++) 
 676                     // copy each string in the list of strings 
 677             wxVariant 
eachVariant(variant
[i
]); 
 678             if (!wxConvertVariantToOle(eachVariant
, * pvarg
)) 
 680                             // memory failure:  back out and free strings alloc'ed up to 
 681                             // now, and then the array itself. 
 683                             for (j 
= 0; j 
< i
; j
++) 
 685                                     SysFreeString(pvarg
->bstrVal
); 
 688                             SafeArrayDestroy(psa
); 
 694             SafeArrayUnaccessData(psa
); 
 696         oleVariant
.parray 
= psa
; 
 700         oleVariant
.vt 
= VT_NULL
; 
 707 #define VT_TYPEMASK 0xfff 
 710 bool wxConvertOleToVariant(const VARIANTARG
& oleVariant
, wxVariant
& variant
) 
 712         switch (oleVariant
.vt 
& VT_TYPEMASK
) 
 716                         wxString 
str(wxConvertStringFromOle(oleVariant
.bstrVal
)); 
 724                         if (!TmFromOleDate(oleVariant
.date
, tmTemp
)) 
 727                         wxDateTime 
date(tmTemp
.tm_yday
, (wxDateTime::Month
) tmTemp
.tm_mon
, tmTemp
.tm_year
, tmTemp
.tm_hour
, tmTemp
.tm_min
, tmTemp
.tm_sec
); 
 736                         variant 
= (long) oleVariant
.lVal
; 
 741                         variant 
= (long) oleVariant
.iVal
; 
 747 #if defined(__WATCOMC__) || (defined(_MSC_VER) && (_MSC_VER <= 1000) && !defined(__MWERKS__) ) //GC 
 748 #ifndef HAVE_BOOL // Can't use bool operator if no native bool type 
 749                         variant 
= (long) (oleVariant
.bool != 0); 
 751                         variant 
= (bool) (oleVariant
.bool != 0); 
 754 #ifndef HAVE_BOOL // Can't use bool operator if no native bool type 
 755                         variant 
= (long) (oleVariant
.boolVal 
!= 0); 
 757                         variant 
= (bool) (oleVariant
.boolVal 
!= 0); 
 764                         variant 
= oleVariant
.dblVal
; 
 771                         int cDims
, cElements
, i
; 
 774                         // Iterate the dimensions: number of elements is x*y*z 
 775                         for (cDims 
= 0, cElements 
= 1; 
 776                                 cDims 
< oleVariant
.parray
->cDims
; cDims 
++) 
 777                                         cElements 
*= oleVariant
.parray
->rgsabound
[cDims
].cElements
; 
 779                         // Get a pointer to the data 
 780                         HRESULT hr 
= SafeArrayAccessData(oleVariant
.parray
, (void HUGEP
* FAR
*) & pvdata
); 
 784                         for (i 
= 0; i 
< cElements
; i
++) 
 786                                 VARIANTARG
& oleElement 
= pvdata
[i
]; 
 788                                 if (!wxConvertOleToVariant(oleElement
, vElement
)) 
 791                                 variant
.Append(vElement
); 
 793                         SafeArrayUnaccessData(oleVariant
.parray
); 
 798                         variant 
= (void*) oleVariant
.pdispVal
; 
 808                         break;  // Ignore Empty Variant, used only during destruction of objects 
 812                         wxLogError(wxT("wxAutomationObject::ConvertOleToVariant: Unknown variant value type")); 
 819 BSTR 
wxConvertStringToOle(const wxString
& str
) 
 822         unsigned int len = strlen((const char*) str); 
 823         unsigned short* s = new unsigned short[len*2+2]; 
 825         memset(s, 0, len*2+2); 
 826         for (i=0; i < len; i++) 
 829         wxBasicString 
bstr(str
.mb_str()); 
 833 wxString 
wxConvertStringFromOle(BSTR bStr
) 
 838         int len 
= SysStringLen(bStr
) + 1; 
 839         char    *buf 
= new char[len
]; 
 840         (void)wcstombs( buf
, bStr
, len
); 
 847 // ---------------------------------------------------------------------------- 
 849 // ---------------------------------------------------------------------------- 
 851 // ctor takes an ANSI string and transforms it to Unicode 
 852 wxBasicString::wxBasicString(const char *sz
) 
 857 // ctor takes an ANSI or Unicode string and transforms it to Unicode 
 858 wxBasicString::wxBasicString(const wxString
& str
) 
 861     m_wzBuf 
= new OLECHAR
[str
.Length() + 1]; 
 862     memcpy(m_wzBuf
, str
.c_str(), str
.Length()*2); 
 863     m_wzBuf
[str
.Length()] = L
'\0'; 
 869 // Takes an ANSI string and transforms it to Unicode 
 870 void wxBasicString::Init(const char *sz
) 
 872     // get the size of required buffer 
 873     UINT lenAnsi 
= strlen(sz
); 
 875     UINT lenWide 
= lenAnsi 
* 2 ; 
 877     UINT lenWide 
= mbstowcs(NULL
, sz
, lenAnsi
); 
 881         m_wzBuf 
= new OLECHAR
[lenWide 
+ 1]; 
 882         mbstowcs(m_wzBuf
, sz
, lenAnsi
); 
 883         m_wzBuf
[lenWide
] = L
'\0'; 
 891 wxBasicString::~wxBasicString() 
 896 ///////////////////////////////////////////////////////////////////////////// 
 897 // COleDateTime class HELPERS - implementation 
 899 BOOL 
OleDateFromTm(WORD wYear
, WORD wMonth
, WORD wDay
, 
 900         WORD wHour
, WORD wMinute
, WORD wSecond
, DATE
& dtDest
) 
 902         // Validate year and month (ignore day of week and milliseconds) 
 903         if (wYear 
> 9999 || wMonth 
< 1 || wMonth 
> 12) 
 906         //  Check for leap year and set the number of days in the month 
 907         BOOL bLeapYear 
= ((wYear 
& 3) == 0) && 
 908                 ((wYear 
% 100) != 0 || (wYear 
% 400) == 0); 
 911                 rgMonthDays
[wMonth
] - rgMonthDays
[wMonth
-1] + 
 912                 ((bLeapYear 
&& wDay 
== 29 && wMonth 
== 2) ? 1 : 0); 
 914         // Finish validating the date 
 915         if (wDay 
< 1 || wDay 
> nDaysInMonth 
|| 
 916                 wHour 
> 23 || wMinute 
> 59 || 
 922         // Cache the date in days and time in fractional days 
 926         //It is a valid date; make Jan 1, 1AD be 1 
 927         nDate 
= wYear
*365L + wYear
/4 - wYear
/100 + wYear
/400 + 
 928                 rgMonthDays
[wMonth
-1] + wDay
; 
 930         //  If leap year and it's before March, subtract 1: 
 931         if (wMonth 
<= 2 && bLeapYear
) 
 934         //  Offset so that 12/30/1899 is 0 
 937         dblTime 
= (((long)wHour 
* 3600L) +  // hrs in seconds 
 938                 ((long)wMinute 
* 60L) +  // mins in seconds 
 939                 ((long)wSecond
)) / 86400.; 
 941         dtDest 
= (double) nDate 
+ ((nDate 
>= 0) ? dblTime 
: -dblTime
); 
 946 BOOL 
TmFromOleDate(DATE dtSrc
, struct tm
& tmDest
) 
 948         // The legal range does not actually span year 0 to 9999. 
 949         if (dtSrc 
> MAX_DATE 
|| dtSrc 
< MIN_DATE
) // about year 100 to about 9999 
 952         long nDays
;             // Number of days since Dec. 30, 1899 
 953         long nDaysAbsolute
;     // Number of days since 1/1/0 
 954         long nSecsInDay
;        // Time in seconds since midnight 
 955         long nMinutesInDay
;     // Minutes in day 
 957         long n400Years
;         // Number of 400 year increments since 1/1/0 
 958         long n400Century
;       // Century within 400 year block (0,1,2 or 3) 
 959         long n4Years
;           // Number of 4 year increments since 1/1/0 
 960         long n4Day
;             // Day within 4 year block 
 961                                                         //  (0 is 1/1/yr1, 1460 is 12/31/yr4) 
 962         long n4Yr
;              // Year within 4 year block (0,1,2 or 3) 
 963         BOOL bLeap4 
= TRUE
;     // TRUE if 4 year block includes leap year 
 965         double dblDate 
= dtSrc
; // tempory serial date 
 967         // If a valid date, then this conversion should not overflow 
 968         nDays 
= (long)dblDate
; 
 970         // Round to the second 
 971         dblDate 
+= ((dtSrc 
> 0.0) ? HALF_SECOND 
: -HALF_SECOND
); 
 973         nDaysAbsolute 
= (long)dblDate 
+ 693959L; // Add days from 1/1/0 to 12/30/1899 
 975         dblDate 
= fabs(dblDate
); 
 976         nSecsInDay 
= (long)((dblDate 
- floor(dblDate
)) * 86400.); 
 978         // Calculate the day of week (sun=1, mon=2...) 
 979         //   -1 because 1/1/0 is Sat.  +1 because we want 1-based 
 980         tmDest
.tm_wday 
= (int)((nDaysAbsolute 
- 1) % 7L) + 1; 
 982         // Leap years every 4 yrs except centuries not multiples of 400. 
 983         n400Years 
= (long)(nDaysAbsolute 
/ 146097L); 
 985         // Set nDaysAbsolute to day within 400-year block 
 986         nDaysAbsolute 
%= 146097L; 
 988         // -1 because first century has extra day 
 989         n400Century 
= (long)((nDaysAbsolute 
- 1) / 36524L); 
 992         if (n400Century 
!= 0) 
 994                 // Set nDaysAbsolute to day within century 
 995                 nDaysAbsolute 
= (nDaysAbsolute 
- 1) % 36524L; 
 997                 // +1 because 1st 4 year increment has 1460 days 
 998                 n4Years 
= (long)((nDaysAbsolute 
+ 1) / 1461L); 
1001                         n4Day 
= (long)((nDaysAbsolute 
+ 1) % 1461L); 
1005                         n4Day 
= (long)nDaysAbsolute
; 
1010                 // Leap century - not special case! 
1011                 n4Years 
= (long)(nDaysAbsolute 
/ 1461L); 
1012                 n4Day 
= (long)(nDaysAbsolute 
% 1461L); 
1017                 // -1 because first year has 366 days 
1018                 n4Yr 
= (n4Day 
- 1) / 365; 
1021                         n4Day 
= (n4Day 
- 1) % 365; 
1029         // n4Day is now 0-based day of year. Save 1-based day of year, year number 
1030         tmDest
.tm_yday 
= (int)n4Day 
+ 1; 
1031         tmDest
.tm_year 
= n400Years 
* 400 + n400Century 
* 100 + n4Years 
* 4 + n4Yr
; 
1033         // Handle leap year: before, on, and after Feb. 29. 
1034         if (n4Yr 
== 0 && bLeap4
) 
1041                         tmDest
.tm_mday 
= 29; 
1045                 // Pretend it's not a leap year for month/day comp. 
1050         // Make n4DaY a 1-based day of non-leap year and compute 
1051         //  month/day for everything but Feb. 29. 
1054         // Month number always >= n/32, so save some loop time */ 
1055         for (tmDest
.tm_mon 
= (n4Day 
>> 5) + 1; 
1056                 n4Day 
> rgMonthDays
[tmDest
.tm_mon
]; tmDest
.tm_mon
++); 
1058         tmDest
.tm_mday 
= (int)(n4Day 
- rgMonthDays
[tmDest
.tm_mon
-1]); 
1061         if (nSecsInDay 
== 0) 
1062                 tmDest
.tm_hour 
= tmDest
.tm_min 
= tmDest
.tm_sec 
= 0; 
1065                 tmDest
.tm_sec 
= (int)nSecsInDay 
% 60L; 
1066                 nMinutesInDay 
= nSecsInDay 
/ 60L; 
1067                 tmDest
.tm_min 
= (int)nMinutesInDay 
% 60; 
1068                 tmDest
.tm_hour 
= (int)nMinutesInDay 
/ 60; 
1074 // this function is not used 
1076 void TmConvertToStandardFormat(struct tm
& tmSrc
) 
1078         // Convert afx internal tm to format expected by runtimes (_tcsftime, etc) 
1079         tmSrc
.tm_year 
-= 1900;  // year is based on 1900 
1080         tmSrc
.tm_mon 
-= 1;      // month of year is 0-based 
1081         tmSrc
.tm_wday 
-= 1;     // day of week is 0-based 
1082         tmSrc
.tm_yday 
-= 1;     // day of year is 0-based 
1085 double DoubleFromDate(DATE dt
) 
1087         // No problem if positive 
1091         // If negative, must convert since negative dates not continuous 
1092         // (examples: -1.25 to -.75, -1.50 to -.50, -1.75 to -.25) 
1093         double temp 
= ceil(dt
); 
1094         return temp 
- (dt 
- temp
); 
1097 DATE 
DateFromDouble(double dbl
) 
1099         // No problem if positive 
1103         // If negative, must convert since negative dates not continuous 
1104         // (examples: -.75 to -1.25, -.50 to -1.50, -.25 to -1.75) 
1105         double temp 
= floor(dbl
); // dbl is now whole part 
1106         return temp 
+ (temp 
- dbl
); 
1113  *  Zeros a variant structure without regard to current contents 
1115 static void ClearVariant(VARIANTARG 
*pvarg
) 
1117         pvarg
->vt 
= VT_EMPTY
; 
1118         pvarg
->wReserved1 
= 0; 
1119         pvarg
->wReserved2 
= 0; 
1120         pvarg
->wReserved3 
= 0; 
1127  *  Clears a particular variant structure and releases any external objects 
1128  *  or memory contained in the variant.  Supports the data types listed above. 
1130 static void ReleaseVariant(VARIANTARG 
*pvarg
) 
1133         VARIANTARG _huge 
*pvargArray
; 
1134         long lLBound
, lUBound
, l
; 
1136         vt 
= pvarg
->vt 
& 0xfff;         // mask off flags 
1138         // check if an array.  If so, free its contents, then the array itself. 
1139         if (V_ISARRAY(pvarg
))  
1141                 // variant arrays are all this routine currently knows about.  Since a 
1142                 // variant can contain anything (even other arrays), call ourselves 
1144                 if (vt 
== VT_VARIANT
)  
1146                         SafeArrayGetLBound(pvarg
->parray
, 1, &lLBound
); 
1147                         SafeArrayGetUBound(pvarg
->parray
, 1, &lUBound
); 
1149                         if (lUBound 
> lLBound
)  
1153                                 SafeArrayAccessData(pvarg
->parray
, (void**)&pvargArray
); 
1155                                 for (l 
= 0; l 
< lUBound
; l
++)  
1157                                         ReleaseVariant(pvargArray
); 
1161                                 SafeArrayUnaccessData(pvarg
->parray
); 
1166                         wxLogWarning(wxT("ReleaseVariant: Array contains non-variant type")); 
1169                 // Free the array itself. 
1170                 SafeArrayDestroy(pvarg
->parray
); 
1177                                 if (pvarg
->pdispVal
) 
1178                                         pvarg
->pdispVal
->Release(); 
1182                                 SysFreeString(pvarg
->bstrVal
); 
1188                         case VT_ERROR
:          // to avoid erroring on an error return from Excel 
1189                                 // no work for these types 
1193                                 wxLogWarning(wxT("ReleaseVariant: Unknown type")); 
1198         ClearVariant(pvarg
); 
1203 void ShowException(LPOLESTR szMember
, HRESULT hr
, EXCEPINFO 
*pexcep
, unsigned int uiArgErr
) 
1207         switch (GetScode(hr
))  
1209                 case DISP_E_UNKNOWNNAME
: 
1210                         wsprintf(szBuf
, L
"%s: Unknown name or named argument.", szMember
); 
1213                 case DISP_E_BADPARAMCOUNT
: 
1214                         wsprintf(szBuf
, L
"%s: Incorrect number of arguments.", szMember
); 
1217                 case DISP_E_EXCEPTION
: 
1218                         wsprintf(szBuf
, L
"%s: Error %d: ", szMember
, pexcep
->wCode
); 
1219                         if (pexcep
->bstrDescription 
!= NULL
) 
1220                                 lstrcat(szBuf
, pexcep
->bstrDescription
); 
1222                                 lstrcat(szBuf
, L
"<<No Description>>"); 
1225                 case DISP_E_MEMBERNOTFOUND
: 
1226                         wsprintf(szBuf
, L
"%s: method or property not found.", szMember
); 
1229                 case DISP_E_OVERFLOW
: 
1230                         wsprintf(szBuf
, L
"%s: Overflow while coercing argument values.", szMember
); 
1233                 case DISP_E_NONAMEDARGS
: 
1234                         wsprintf(szBuf
, L
"%s: Object implementation does not support named arguments.", 
1238                 case DISP_E_UNKNOWNLCID
: 
1239                         wsprintf(szBuf
, L
"%s: The locale ID is unknown.", szMember
); 
1242                 case DISP_E_PARAMNOTOPTIONAL
: 
1243                         wsprintf(szBuf
, L
"%s: Missing a required parameter.", szMember
); 
1246                 case DISP_E_PARAMNOTFOUND
: 
1247                         wsprintf(szBuf
, L
"%s: Argument not found, argument %d.", szMember
, uiArgErr
); 
1250                 case DISP_E_TYPEMISMATCH
: 
1251                         wsprintf(szBuf
, L
"%s: Type mismatch, argument %d.", szMember
, uiArgErr
); 
1255                         wsprintf(szBuf
, L
"%s: Unknown error occured.", szMember
); 
1259         wxLogWarning(szBuf
); 
1264 #endif // __WATCOMC__