Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

805 lines
20 KiB

  1. /*++
  2. Copyright (c) 1996-1999 Microsoft Corporation
  3. Module Name :
  4. asclogc.cpp
  5. Abstract:
  6. MS Logging Format implementation
  7. Author:
  8. Terence Kwan ( terryk ) 18-Sep-1996
  9. Project:
  10. IIS Logging 3.0
  11. --*/
  12. #include "precomp.hxx"
  13. #include <stdio.h>
  14. #include <winnls.h>
  15. #include <script.h>
  16. #include "LogScript.hxx"
  17. #include <ilogobj.hxx>
  18. #include "filectl.hxx"
  19. #include "asclogc.hxx"
  20. CHAR szAsciiNoPeriodPattern[] = "inetsv*.log";
  21. // -----------------------------------------------------------------------------
  22. // -----------------------------------------------------------------------------
  23. CASCLOG::CASCLOG()
  24. {
  25. }
  26. // -----------------------------------------------------------------------------
  27. // -----------------------------------------------------------------------------
  28. CASCLOG::~CASCLOG()
  29. {
  30. }
  31. // -----------------------------------------------------------------------------
  32. // -----------------------------------------------------------------------------
  33. LPCSTR
  34. CASCLOG::QueryNoPeriodPattern(
  35. VOID
  36. )
  37. {
  38. return szAsciiNoPeriodPattern;
  39. } // CASCLOG::QueryNoPeriodPattern
  40. // -----------------------------------------------------------------------------
  41. // -----------------------------------------------------------------------------
  42. VOID
  43. CASCLOG::FormNewLogFileName(
  44. IN LPSYSTEMTIME pstNow
  45. )
  46. /*++
  47. This function that forms the new log file name based on
  48. type of periodic logging done.
  49. Arguments:
  50. pstNow pointer to SystemTime which contains the current time.
  51. fBackup flag indicating if we want to make current file a backup.
  52. Returns:
  53. TRUE on success in forming the name or FALSE if there is any error.
  54. --*/
  55. {
  56. I_FormNewLogFileName(pstNow,DEFAULT_LOG_FILE_NAME);
  57. return;
  58. } // INET_FILE_LOG::FormNewLogFileName()
  59. // -----------------------------------------------------------------------------
  60. // -----------------------------------------------------------------------------
  61. VOID
  62. FormatLogDwords(
  63. IN IInetLogInformation * pLogObj,
  64. IN LPSTR *pBuffer,
  65. IN DWORD *pcbSize,
  66. IN PDWORD pRequired
  67. )
  68. {
  69. CHAR tmpBuf[32];
  70. DWORD cbTmp;
  71. //
  72. // Processing Time
  73. //
  74. cbTmp = FastDwToA( tmpBuf, pLogObj->GetTimeForProcessing() );
  75. *pRequired += cbTmp;
  76. if ( *pRequired <= *pcbSize ) {
  77. tmpBuf[cbTmp] = ',';
  78. tmpBuf[cbTmp+1] = ' ';
  79. CopyMemory(*pBuffer, tmpBuf, cbTmp+2);
  80. *pBuffer += cbTmp+2;
  81. }
  82. //
  83. // Bytes Received
  84. //
  85. cbTmp = FastDwToA( tmpBuf, pLogObj->GetBytesRecvd() );
  86. *pRequired += cbTmp;
  87. if ( *pRequired <= *pcbSize ) {
  88. tmpBuf[cbTmp] = ',';
  89. tmpBuf[cbTmp+1] = ' ';
  90. CopyMemory(*pBuffer, tmpBuf, cbTmp+2);
  91. *pBuffer += cbTmp+2;
  92. }
  93. //
  94. // Bytes Sent
  95. //
  96. cbTmp = FastDwToA( tmpBuf, pLogObj->GetBytesSent() );
  97. *pRequired += cbTmp;
  98. if ( *pRequired <= *pcbSize ) {
  99. tmpBuf[cbTmp] = ',';
  100. tmpBuf[cbTmp+1] = ' ';
  101. CopyMemory(*pBuffer, tmpBuf, cbTmp+2);
  102. *pBuffer += cbTmp+2;
  103. }
  104. //
  105. // HTTP Status
  106. //
  107. cbTmp = FastDwToA( tmpBuf, pLogObj->GetProtocolStatus() );
  108. *pRequired += cbTmp;
  109. if ( *pRequired <= *pcbSize ) {
  110. tmpBuf[cbTmp] = ',';
  111. tmpBuf[cbTmp+1] = ' ';
  112. CopyMemory(*pBuffer, tmpBuf, cbTmp+2);
  113. *pBuffer += cbTmp+2;
  114. }
  115. //
  116. // Win32 status
  117. //
  118. cbTmp = FastDwToA( tmpBuf, pLogObj->GetWin32Status() );
  119. *pRequired += cbTmp;
  120. if ( *pRequired <= *pcbSize ) {
  121. tmpBuf[cbTmp] = ',';
  122. tmpBuf[cbTmp+1] = ' ';
  123. CopyMemory(*pBuffer, tmpBuf, cbTmp+2);
  124. *pBuffer += cbTmp+2;
  125. }
  126. return;
  127. } // FormatLogDwords
  128. // -----------------------------------------------------------------------------
  129. // -----------------------------------------------------------------------------
  130. BOOL
  131. CASCLOG::FormatLogBuffer(
  132. IN IInetLogInformation *pLogObj,
  133. IN LPSTR pBuf,
  134. IN DWORD *pcbSize,
  135. OUT SYSTEMTIME *pSystemTime
  136. )
  137. {
  138. CHAR rgchDateTime[ 32];
  139. PCHAR pBuffer = pBuf;
  140. PCHAR pTmp;
  141. DWORD cbTmp;
  142. DWORD nRequired;
  143. if ( pBuf == NULL ) {
  144. return ERROR_INVALID_PARAMETER;
  145. }
  146. //
  147. // Format is:
  148. // Host UserName Date Time Service ComputerName ServerIP
  149. // msProcessingTime bytesR bytesS protocolStat Win32Stat
  150. // Operation Target Parameters
  151. //
  152. //
  153. // Host ID
  154. //
  155. pTmp = pLogObj->GetClientHostName( NULL, &cbTmp );
  156. if ( cbTmp == 0 ) {
  157. cbTmp = 1;
  158. pTmp = "-";
  159. }
  160. nRequired = cbTmp + 2; // 2 for delimiter
  161. if ( nRequired <= *pcbSize ) {
  162. CopyMemory(pBuffer, pTmp, cbTmp);
  163. pBuffer += cbTmp;
  164. *(pBuffer++) = ',';
  165. *(pBuffer++) = ' ';
  166. }
  167. //
  168. // UserName
  169. //
  170. pTmp = pLogObj->GetClientUserName( NULL, &cbTmp );
  171. if ( cbTmp == 0 ) {
  172. cbTmp = 1;
  173. pTmp = "-";
  174. }
  175. nRequired += cbTmp + 2; //2 for delimiter
  176. if ( nRequired <= *pcbSize ) {
  177. CopyMemory(pBuffer, pTmp, cbTmp);
  178. pBuffer += cbTmp;
  179. *(pBuffer++) = ',';
  180. *(pBuffer++) = ' ';
  181. }
  182. //
  183. // Date/Time (already delimited)
  184. //
  185. m_DateTimeCache.SetLocalTime(pSystemTime);
  186. cbTmp = m_DateTimeCache.GetFormattedDateTime(pSystemTime,rgchDateTime);
  187. nRequired += cbTmp;
  188. if ( nRequired <= *pcbSize ) {
  189. CopyMemory(pBuffer, rgchDateTime, cbTmp);
  190. pBuffer += cbTmp;
  191. }
  192. //
  193. // Site Name
  194. //
  195. pTmp = pLogObj->GetSiteName( NULL, &cbTmp );
  196. if ( cbTmp == 0 ) {
  197. cbTmp = 1;
  198. pTmp = "-";
  199. }
  200. nRequired += cbTmp + 2; // 2 for delimiter
  201. if ( nRequired <= *pcbSize ) {
  202. CopyMemory(pBuffer, pTmp, cbTmp);
  203. pBuffer += cbTmp;
  204. *(pBuffer++) = ',';
  205. *(pBuffer++) = ' ';
  206. }
  207. //
  208. // ComputerName
  209. //
  210. pTmp = pLogObj->GetComputerName( NULL, &cbTmp );
  211. if ( cbTmp == 0 ) {
  212. cbTmp = 1;
  213. pTmp = "-";
  214. }
  215. nRequired += cbTmp + 2; // 2 for delimiter
  216. if ( nRequired <= *pcbSize ) {
  217. CopyMemory(pBuffer, pTmp, cbTmp);
  218. pBuffer += cbTmp;
  219. *(pBuffer++) = ',';
  220. *(pBuffer++) = ' ';
  221. }
  222. //
  223. // Server IP
  224. //
  225. pTmp = pLogObj->GetServerAddress( NULL, &cbTmp );
  226. if ( cbTmp == 0 ) {
  227. cbTmp = 1;
  228. pTmp = "-";
  229. }
  230. nRequired += cbTmp + 2; // 2 for delimiter
  231. if ( nRequired <= *pcbSize ) {
  232. CopyMemory(pBuffer, pTmp, cbTmp);
  233. pBuffer += cbTmp;
  234. *(pBuffer++) = ',';
  235. *(pBuffer++) = ' ';
  236. }
  237. //
  238. // Use fast path ?
  239. // All numbers are < 4G (10 charaters)
  240. // add 2 per number for delimiters
  241. // so we need 10 * 5 numbers == 30 bytes
  242. //
  243. nRequired += 10; // 10 for delimiters of 5 numbers
  244. if ( (nRequired + 50) <= *pcbSize ) {
  245. //
  246. // Processing Time
  247. //
  248. cbTmp = FastDwToA( pBuffer, pLogObj->GetTimeForProcessing() );
  249. nRequired += cbTmp;
  250. pBuffer += cbTmp;
  251. *(pBuffer++) = ',';
  252. *(pBuffer++) = ' ';
  253. //
  254. // Bytes Received
  255. //
  256. cbTmp = FastDwToA( pBuffer, pLogObj->GetBytesRecvd() );
  257. nRequired += cbTmp;
  258. pBuffer += cbTmp;
  259. *(pBuffer++) = ',';
  260. *(pBuffer++) = ' ';
  261. //
  262. // Bytes Sent
  263. //
  264. cbTmp = FastDwToA( pBuffer, pLogObj->GetBytesSent() );
  265. nRequired += cbTmp;
  266. pBuffer += cbTmp;
  267. *(pBuffer++) = ',';
  268. *(pBuffer++) = ' ';
  269. //
  270. // HTTP Status
  271. //
  272. cbTmp = FastDwToA( pBuffer, pLogObj->GetProtocolStatus() );
  273. nRequired += cbTmp;
  274. pBuffer += cbTmp;
  275. *(pBuffer++) = ',';
  276. *(pBuffer++) = ' ';
  277. //
  278. // Win32 status
  279. //
  280. cbTmp = FastDwToA( pBuffer, pLogObj->GetWin32Status() );
  281. nRequired += cbTmp;
  282. pBuffer += cbTmp;
  283. *(pBuffer++) = ',';
  284. *(pBuffer++) = ' ';
  285. } else {
  286. FormatLogDwords( pLogObj, &pBuffer, pcbSize, &nRequired );
  287. }
  288. //
  289. // Operation
  290. //
  291. pTmp = pLogObj->GetOperation( NULL, &cbTmp );
  292. if ( cbTmp == 0 ) {
  293. cbTmp = 1;
  294. pTmp = "-";
  295. }
  296. nRequired += cbTmp + 2; // 2 for delimiter
  297. if ( nRequired <= *pcbSize ) {
  298. CopyMemory(pBuffer, pTmp, cbTmp);
  299. pBuffer += cbTmp;
  300. *(pBuffer++) = ',';
  301. *(pBuffer++) = ' ';
  302. }
  303. //
  304. // Target
  305. //
  306. pTmp = pLogObj->GetTarget( NULL, &cbTmp );
  307. if ( cbTmp == 0 ) {
  308. cbTmp = 1;
  309. pTmp = "-";
  310. }
  311. nRequired += cbTmp + 2; // 2 for delimiter
  312. if ( nRequired <= *pcbSize ) {
  313. CopyMemory(pBuffer, pTmp, cbTmp);
  314. pBuffer += cbTmp;
  315. *(pBuffer++) = ',';
  316. *(pBuffer++) = ' ';
  317. }
  318. //
  319. // Parameters
  320. //
  321. pTmp = pLogObj->GetParameters( NULL, &cbTmp );
  322. if ( cbTmp == 0 ) {
  323. cbTmp = 1;
  324. pTmp = "-";
  325. }
  326. nRequired += cbTmp + 1 + 2; // 1 for delimiter, 2 for EOL
  327. if ( nRequired <= *pcbSize ) {
  328. CopyMemory(pBuffer, pTmp, cbTmp);
  329. pBuffer += cbTmp;
  330. // NOTE
  331. // Documentation is ambiguous about the presence of a comma
  332. // at the end of the log record, but the comma is required
  333. // for backward compatability with site server
  334. *(pBuffer++) = ',';
  335. *(pBuffer++) = '\r';
  336. *(pBuffer++) = '\n';
  337. }
  338. if ( nRequired > *pcbSize ) {
  339. *pcbSize = nRequired;
  340. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  341. return(FALSE);
  342. } else {
  343. *pcbSize = nRequired;
  344. return(TRUE);
  345. }
  346. } // FormatLogBuffer
  347. // -----------------------------------------------------------------------------
  348. // -----------------------------------------------------------------------------
  349. HRESULT
  350. CASCLOG::ReadFileLogRecord(
  351. IN FILE *fpLogFile,
  352. IN LPINET_LOGLINE pInetLogLine,
  353. IN PCHAR pszLogLine,
  354. IN DWORD dwLogLineSize
  355. )
  356. {
  357. CHAR * pCh;
  358. CHAR * szDateString, * szTimeString;
  359. getnewline:
  360. pCh = pszLogLine;
  361. if (fgets(pCh, dwLogLineSize, fpLogFile) == NULL)
  362. {
  363. return E_FAIL;
  364. }
  365. pCh = SkipWhite(pCh);
  366. if (('\n' == *pCh) || ('\0' == *pCh))
  367. {
  368. // Empty line. Get Next line
  369. goto getnewline;
  370. }
  371. //
  372. // We have a log line. Parse it.
  373. //
  374. // Format is:
  375. // Host UserName Date Time Service ComputerName ServerIP
  376. // msProcessingTime bytesR bytesS protocolStat Win32Stat
  377. // Operation Target Parameters
  378. //
  379. if ( NULL == (pCh = strtok(pCh,",")) )
  380. {
  381. return E_FAIL;
  382. }
  383. while (isspace((UCHAR)(*pCh))) pCh++;
  384. pInetLogLine->pszClientHostName = pCh;
  385. if ( NULL == (pCh = strtok(NULL,",")) )
  386. {
  387. return E_FAIL;
  388. }
  389. while (isspace((UCHAR)(*pCh))) pCh++;
  390. pInetLogLine->pszClientUserName = pCh;
  391. //
  392. // Date & Time.
  393. //
  394. if ( NULL == (pCh = strtok(NULL,",")) )
  395. {
  396. return E_FAIL;
  397. }
  398. while (isspace((UCHAR)(*pCh))) pCh++;
  399. szDateString = pCh;
  400. if ( NULL == (pCh = strtok(NULL,",")) )
  401. {
  402. return E_FAIL;
  403. }
  404. while (isspace((UCHAR)(*pCh))) pCh++;
  405. szTimeString = pCh;
  406. if ( ! ConvertASCDateToVariantDate(szDateString, szTimeString, &(pInetLogLine->DateTime)) )
  407. {
  408. return E_FAIL;
  409. }
  410. //
  411. // Service & Server information
  412. //
  413. if ( NULL == (pCh = strtok(NULL,",")) )
  414. {
  415. return E_FAIL;
  416. }
  417. while (isspace((UCHAR)(*pCh))) pCh++;
  418. pInetLogLine->pszSiteName = pCh;
  419. if ( NULL == (pCh = strtok(NULL,",")) )
  420. {
  421. return E_FAIL;
  422. }
  423. while (isspace((UCHAR)(*pCh))) pCh++;
  424. pInetLogLine->pszComputerName = pCh;
  425. if ( NULL == (pCh = strtok(NULL,",")) )
  426. {
  427. return E_FAIL;
  428. }
  429. while (isspace((UCHAR)(*pCh))) pCh++;
  430. pInetLogLine->pszServerAddress = pCh;
  431. //
  432. // Statistics - processing time, bytes recvd, bytes sent
  433. //
  434. if ( NULL == (pCh = strtok(NULL,",")) )
  435. {
  436. return E_FAIL;
  437. }
  438. while (isspace((UCHAR)(*pCh))) pCh++;
  439. pInetLogLine->pszTimeForProcessing = pCh;
  440. if ( NULL == (pCh = strtok(NULL,",")) )
  441. {
  442. return E_FAIL;
  443. }
  444. while (isspace((UCHAR)(*pCh))) pCh++;
  445. pInetLogLine->pszBytesRecvd = pCh;
  446. if ( NULL == (pCh = strtok(NULL,",")) )
  447. {
  448. return E_FAIL;
  449. }
  450. while (isspace((UCHAR)(*pCh))) pCh++;
  451. pInetLogLine->pszBytesSent = pCh;
  452. //
  453. // Status information - protocol, Win32
  454. //
  455. if ( NULL == (pCh = strtok(NULL,",")) )
  456. {
  457. return E_FAIL;
  458. }
  459. while (isspace((UCHAR)(*pCh))) pCh++;
  460. pInetLogLine->pszProtocolStatus = pCh;
  461. if ( NULL == (pCh = strtok(NULL,",")) )
  462. {
  463. return E_FAIL;
  464. }
  465. while (isspace((UCHAR)(*pCh))) pCh++;
  466. pInetLogLine->pszWin32Status = pCh;
  467. //
  468. // Request information - operation, target, parameters
  469. //
  470. if ( NULL == (pCh = strtok(NULL,",")) )
  471. {
  472. return E_FAIL;
  473. }
  474. while (isspace((UCHAR)(*pCh))) pCh++;
  475. pInetLogLine->pszOperation = pCh;
  476. if ( NULL == (pCh = strtok(NULL,",")) )
  477. {
  478. return E_FAIL;
  479. }
  480. while (isspace((UCHAR)(*pCh))) pCh++;
  481. pInetLogLine->pszTarget = pCh;
  482. if ( NULL == (pCh = strtok(NULL," ,\t\r\n")) )
  483. {
  484. return E_FAIL;
  485. }
  486. while (isspace((UCHAR)(*pCh))) pCh++;
  487. pInetLogLine->pszParameters = pCh;
  488. return S_OK;
  489. }
  490. // -----------------------------------------------------------------------------
  491. // -----------------------------------------------------------------------------
  492. HRESULT
  493. CASCLOG::WriteFileLogRecord(
  494. IN FILE *fpLogFile,
  495. IN ILogScripting *pILogScripting,
  496. IN bool
  497. )
  498. {
  499. HRESULT hr = E_FAIL;
  500. CHAR szLogLine[4096];
  501. DWORD dwIndex = 0;
  502. //
  503. // Format is:
  504. // Host, UserName, Date, Time, Service, ComputerName, ServerIP,
  505. // msProcessingTime, bytesR, bytesS, protocolStat, Win32Stat,
  506. // Operation, Target, Parameters,
  507. //
  508. VARIANT szHostName, szUserName, szServiceName, szComputerName;
  509. VARIANT szServerIP, szOperation, szTarget, szParameters;
  510. VARIANT DateTime;
  511. VARIANT lTimeForProcessing, lBytesSent, lBytesRecvd, lProtocolStatus, lWin32Status;
  512. SYSTEMTIME sysTime;
  513. CHAR rgchDateTime[ 32];
  514. if (SUCCEEDED(pILogScripting->get_ClientIP ( &szHostName)) &&
  515. SUCCEEDED(pILogScripting->get_UserName ( &szUserName)) &&
  516. SUCCEEDED(pILogScripting->get_DateTime ( &DateTime)) &&
  517. SUCCEEDED(pILogScripting->get_ServiceName ( &szServiceName)) &&
  518. SUCCEEDED(pILogScripting->get_ServerName ( &szComputerName)) &&
  519. SUCCEEDED(pILogScripting->get_ServerIP ( &szServerIP)) &&
  520. SUCCEEDED(pILogScripting->get_TimeTaken ( &lTimeForProcessing)) &&
  521. SUCCEEDED(pILogScripting->get_BytesReceived ( &lBytesRecvd)) &&
  522. SUCCEEDED(pILogScripting->get_BytesSent ( &lBytesSent)) &&
  523. SUCCEEDED(pILogScripting->get_ProtocolStatus( &lProtocolStatus)) &&
  524. SUCCEEDED(pILogScripting->get_Win32Status ( &lWin32Status)) &&
  525. SUCCEEDED(pILogScripting->get_Method ( &szOperation)) &&
  526. SUCCEEDED(pILogScripting->get_URIStem ( &szTarget)) &&
  527. SUCCEEDED(pILogScripting->get_URIQuery ( &szParameters)) &&
  528. VariantTimeToSystemTime( DateTime.date, &sysTime)
  529. )
  530. {
  531. m_DateTimeCache.GetFormattedDateTime( &sysTime, rgchDateTime);
  532. dwIndex = sprintf(szLogLine, "%ws, %ws, %s%ws, %ws, %ws,",
  533. GetBstrFromVariant( &szHostName),
  534. GetBstrFromVariant( &szUserName),
  535. rgchDateTime, // This guy already contains a trailing ", "
  536. GetBstrFromVariant( &szServiceName),
  537. GetBstrFromVariant( &szComputerName),
  538. GetBstrFromVariant( &szServerIP)
  539. );
  540. szLogLine[dwIndex++] = ' ';
  541. dwIndex += GetLongFromVariant( &lTimeForProcessing, szLogLine+dwIndex) ;
  542. szLogLine[dwIndex++] = ',';
  543. szLogLine[dwIndex++] = ' ';
  544. dwIndex += GetLongFromVariant( &lBytesRecvd, szLogLine+dwIndex);
  545. szLogLine[dwIndex++] = ',';
  546. szLogLine[dwIndex++] = ' ';
  547. dwIndex += GetLongFromVariant( &lBytesSent, szLogLine+dwIndex);
  548. szLogLine[dwIndex++] = ',';
  549. szLogLine[dwIndex++] = ' ';
  550. dwIndex += GetLongFromVariant( &lProtocolStatus, szLogLine+dwIndex);
  551. szLogLine[dwIndex++] = ',';
  552. szLogLine[dwIndex++] = ' ';
  553. dwIndex += GetLongFromVariant( &lWin32Status, szLogLine+dwIndex);
  554. sprintf( szLogLine+dwIndex ,", %ws, %ws, %ws",
  555. GetBstrFromVariant( &szOperation),
  556. GetBstrFromVariant( &szTarget),
  557. GetBstrFromVariant( &szParameters)
  558. );
  559. // Include a , at the end of the log record. See NOTE in
  560. // FormatLogBuffer for more details on why.
  561. fprintf(fpLogFile, "%s,\n", szLogLine);
  562. hr = S_OK;
  563. }
  564. return hr;
  565. }
  566. // -----------------------------------------------------------------------------
  567. // -----------------------------------------------------------------------------
  568. BOOL
  569. CASCLOG::ConvertASCDateToVariantDate(PCHAR szDateString, PCHAR szTimeString, DATE * pDateTime)
  570. {
  571. USES_CONVERSION;
  572. BOOL fSuccess = FALSE;
  573. HRESULT hr;
  574. LCID lcid;
  575. BSTR bstrDate;
  576. BSTR bstrTime;
  577. DATE dateTime;
  578. DATE dateDate;
  579. DECIMAL decDate;
  580. DECIMAL decTime;
  581. DECIMAL decDateTimeComposite;
  582. bstrDate = SysAllocString(A2OLE(szDateString));
  583. bstrTime = SysAllocString(A2OLE(szTimeString));
  584. if ((NULL == bstrDate) ||
  585. (NULL == bstrTime))
  586. {
  587. goto error_converting;
  588. }
  589. //
  590. // On IIS6, http.sys writes IIS log format always in US format
  591. //
  592. lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
  593. hr = VarDateFromStr(bstrTime, lcid, LOCALE_NOUSEROVERRIDE, &dateTime);
  594. if (FAILED(hr))
  595. {
  596. goto error_converting;
  597. }
  598. hr = VarDateFromStr(bstrDate, lcid, LOCALE_NOUSEROVERRIDE, &dateDate);
  599. if (FAILED(hr))
  600. {
  601. goto error_converting;
  602. }
  603. hr = VarDecFromDate(dateDate, &decDate);
  604. if (FAILED(hr))
  605. {
  606. goto error_converting;
  607. }
  608. hr = VarDecFromDate(dateTime, &decTime);
  609. if (FAILED(hr))
  610. {
  611. goto error_converting;
  612. }
  613. hr = VarDecAdd(&decDate, &decTime, &decDateTimeComposite);
  614. if (FAILED(hr))
  615. {
  616. goto error_converting;
  617. }
  618. hr = VarDateFromDec(&decDateTimeComposite, pDateTime);
  619. if (FAILED(hr))
  620. {
  621. goto error_converting;
  622. }
  623. fSuccess = TRUE;
  624. error_converting:
  625. if (NULL != bstrDate)
  626. {
  627. SysFreeString(bstrDate);
  628. bstrDate = NULL;
  629. }
  630. if (NULL != bstrTime)
  631. {
  632. SysFreeString(bstrTime);
  633. bstrTime = NULL;
  634. }
  635. return fSuccess;
  636. }
  637. // -----------------------------------------------------------------------------
  638. // -----------------------------------------------------------------------------