mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-11-04 00:08:00 +00:00
GEEKING
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include <synchapi.h>
|
||||
|
||||
class Clock
|
||||
{
|
||||
public:
|
||||
Clock() : m_heartbeats(0), m_wakeUps(0)
|
||||
{
|
||||
m_timerFrequency.QuadPart = 0;
|
||||
m_timerStart.QuadPart = 0;
|
||||
m_countPerPeriod.QuadPart = 0;
|
||||
}
|
||||
|
||||
~Clock()
|
||||
{
|
||||
}
|
||||
|
||||
void Initialize( UINT uPeriodMS )
|
||||
{
|
||||
if (!QueryPerformanceFrequency(&m_timerFrequency))
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
SetInterval(uPeriodMS);
|
||||
}
|
||||
|
||||
void SetInterval( UINT uPeriodMS )
|
||||
{
|
||||
m_countPerPeriod.QuadPart = ( m_timerFrequency.QuadPart * uPeriodMS ) / c_uOneSecondInMS;
|
||||
}
|
||||
|
||||
void SleepUntilNextHeartbeat()
|
||||
{
|
||||
LARGE_INTEGER timeOfNextHeartbeat = GetNextHeartbeat();
|
||||
LARGE_INTEGER timeNow;
|
||||
|
||||
m_heartbeats++; // so that the next one will be 20ms (or whatever) later...
|
||||
|
||||
if (!QueryPerformanceCounter(&timeNow))
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
LONGLONG timeDelta = timeOfNextHeartbeat.QuadPart - timeNow.QuadPart;
|
||||
|
||||
if ( timeDelta > 0 )
|
||||
{
|
||||
LONGLONG numberOfMilliSecondsSinceLast = c_uOneSecondInMS * timeDelta / m_timerFrequency.QuadPart;
|
||||
WCHAR text[200] = {0};
|
||||
swprintf_s( text, L"I Slept: %lld", numberOfMilliSecondsSinceLast );
|
||||
OutputDebugString( text );
|
||||
DWORD dwSleepTime = static_cast<DWORD>(numberOfMilliSecondsSinceLast);
|
||||
Sleep( dwSleepTime );
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WaitForEventsOrHeartbeat(HANDLE hObject, HANDLE hObject2)
|
||||
{
|
||||
LARGE_INTEGER liExpected = GetNextHeartbeat();
|
||||
LARGE_INTEGER liNow;
|
||||
|
||||
m_wakeUps = 0;
|
||||
m_heartbeats++; // so that the next one will be 20ms (or whatever) later...
|
||||
|
||||
if (!QueryPerformanceCounter(&liNow))
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
LONGLONG llDiff = liExpected.QuadPart - liNow.QuadPart;
|
||||
|
||||
HANDLE aObjects[2];
|
||||
aObjects[0] = hObject;
|
||||
aObjects[1] = hObject2;
|
||||
|
||||
if ( llDiff > 0 )
|
||||
{
|
||||
DWORD dwSleepTime = static_cast<DWORD>(c_uOneSecondInMS * llDiff / m_timerFrequency.QuadPart);
|
||||
m_wakeUps++;
|
||||
|
||||
return ::WaitForMultipleObjectsEx( 2, aObjects, false, dwSleepTime, FALSE );
|
||||
}
|
||||
|
||||
// we're already late, no need to wait, but we still need to test the object
|
||||
m_wakeUps++;
|
||||
return ::WaitForMultipleObjectsEx( 2, aObjects, false, 0, FALSE );
|
||||
}
|
||||
|
||||
UINT m_wakeUps;
|
||||
private:
|
||||
static const UINT c_uOneSecondInMS = 1000;
|
||||
UINT m_heartbeats;
|
||||
|
||||
|
||||
LARGE_INTEGER m_timerFrequency;
|
||||
LARGE_INTEGER m_timerStart;
|
||||
LARGE_INTEGER m_countPerPeriod;
|
||||
|
||||
LARGE_INTEGER GetNextHeartbeat()
|
||||
{
|
||||
LARGE_INTEGER timeExpected;
|
||||
|
||||
// Update Start
|
||||
if ( m_timerStart.QuadPart == 0 )
|
||||
{
|
||||
if (!QueryPerformanceCounter( &m_timerStart ))
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
m_heartbeats = 1; // we are on the very first heartbeat
|
||||
}
|
||||
|
||||
timeExpected.QuadPart = m_timerStart.QuadPart + (m_countPerPeriod.QuadPart * m_heartbeats);
|
||||
return timeExpected;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,525 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#include "pch.h"
|
||||
#include "Utils.h"
|
||||
#include "ISO8601.h"
|
||||
#include "Robuffer.h"
|
||||
#include "Configuration.h"
|
||||
|
||||
// for conversion from seconds (int) to TimeSpan (perhaps use _XTIME_TICKS_PER_TIME_T instead)
|
||||
#define TICKS_PER_SECOND 10000000i64
|
||||
|
||||
#define SECONDS_PER_DAY 86400
|
||||
|
||||
using namespace Microsoft::Xbox::Samples::NetworkMesh;
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Storage::Streams;
|
||||
|
||||
std::wstring&
|
||||
Utils::Replace(
|
||||
__inout std::wstring& strSource,
|
||||
__in PCWSTR pwszPattern,
|
||||
__in_opt PCWSTR pwszReplacement,
|
||||
__out_opt size_t* pnOccurrencesReplaced
|
||||
)
|
||||
{
|
||||
THROW_INVALIDARGUMENT_IF( Utils::IsNullOrEmptyString( pwszPattern ));
|
||||
const size_t nPatternLength = wcslen( pwszPattern );
|
||||
|
||||
return ReplaceSubstring<WCHAR>(
|
||||
strSource,
|
||||
pwszPattern,
|
||||
nPatternLength,
|
||||
( ( pwszReplacement != nullptr ) ? pwszReplacement : L"" ),
|
||||
pnOccurrencesReplaced
|
||||
);
|
||||
}
|
||||
|
||||
std::vector<std::wstring>
|
||||
Utils::StringSplit(
|
||||
__in const std::wstring& string,
|
||||
__in WCHAR seperator
|
||||
)
|
||||
{
|
||||
std::vector<std::wstring> vSubStrings;
|
||||
|
||||
if ( !string.empty() )
|
||||
{
|
||||
size_t posStart = 0, posFound = 0;
|
||||
while ( posFound != std::wstring::npos && posStart < string.length() )
|
||||
{
|
||||
posFound = string.find( seperator, posStart);
|
||||
if ( posFound != std::wstring::npos )
|
||||
{
|
||||
if ( posFound != posStart )
|
||||
{
|
||||
// this substring is not empty
|
||||
vSubStrings.push_back( string.substr( posStart, posFound - posStart ) );
|
||||
}
|
||||
posStart = posFound + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
vSubStrings.push_back( string.substr( posStart ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vSubStrings;
|
||||
}
|
||||
|
||||
Platform::String^
|
||||
Utils::DateTimeToString(
|
||||
__in Windows::Foundation::DateTime dateTime
|
||||
)
|
||||
{
|
||||
// It's OK for us not be able to handle BC time.
|
||||
THROW_HR_IF( dateTime.UniversalTime < 0, E_BOUNDS);
|
||||
|
||||
FILETIME fileTime;
|
||||
fileTime.dwLowDateTime = (dateTime.UniversalTime & (DWORD)-1);
|
||||
fileTime.dwHighDateTime = (dateTime.UniversalTime >> 32 & (DWORD)-1);
|
||||
|
||||
WCHAR dateString[ISO8601_MAX_CCH];
|
||||
THROW_IF_HR_FAILED(
|
||||
FILETIMEToISO8601W( &fileTime, false, dateString, ARRAYSIZE(dateString), FALSE)
|
||||
);
|
||||
|
||||
return ref new String(dateString);
|
||||
}
|
||||
|
||||
String^
|
||||
Utils::RemoveBracesFromGuidString(
|
||||
__in String^ guid
|
||||
)
|
||||
{
|
||||
std::wstring strGuid = guid->ToString()->Data();
|
||||
|
||||
if(strGuid.length() > 0 && strGuid[0] == L'{')
|
||||
{
|
||||
// Remove the {
|
||||
strGuid.erase(0, 1);
|
||||
}
|
||||
|
||||
if(strGuid.length() > 0 && strGuid[strGuid.length() - 1] == L'}')
|
||||
{
|
||||
// Remove the }
|
||||
strGuid.erase(strGuid.end() - 1, strGuid.end());
|
||||
}
|
||||
|
||||
return ref new String(strGuid.c_str());
|
||||
}
|
||||
|
||||
Windows::Foundation::TimeSpan
|
||||
Utils::ConvertMillisecondsToTimeSpan(
|
||||
__in uint64 milliseconds
|
||||
)
|
||||
{
|
||||
Windows::Foundation::TimeSpan ts;
|
||||
ts.Duration = (TICKS_PER_SECOND/1000) * milliseconds;
|
||||
return ts;
|
||||
}
|
||||
|
||||
Windows::Foundation::TimeSpan
|
||||
Utils::ConvertSecondsToTimeSpan(
|
||||
__in uint32 seconds
|
||||
)
|
||||
{
|
||||
Windows::Foundation::TimeSpan ts;
|
||||
ts.Duration = TICKS_PER_SECOND * seconds;
|
||||
return ts;
|
||||
}
|
||||
|
||||
uint32
|
||||
Utils::ConvertTimeSpanToSeconds(
|
||||
__in Windows::Foundation::TimeSpan timespan
|
||||
)
|
||||
{
|
||||
int64 seconds = timespan.Duration / TICKS_PER_SECOND;
|
||||
THROW_INVALIDARGUMENT_IF( seconds < 0 || seconds > UINT32_MAX );
|
||||
|
||||
return static_cast<uint32>(seconds);
|
||||
}
|
||||
|
||||
int64
|
||||
Utils::ConvertTimeSpanToMilliseconds(
|
||||
__in Windows::Foundation::TimeSpan timespan
|
||||
)
|
||||
{
|
||||
int64 milliseconds = timespan.Duration / (TICKS_PER_SECOND/1000);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
uint32
|
||||
Utils::ConvertTimeSpanToDays(
|
||||
__in Windows::Foundation::TimeSpan timespan
|
||||
)
|
||||
{
|
||||
int64 days = (timespan.Duration / TICKS_PER_SECOND) / SECONDS_PER_DAY;
|
||||
THROW_INVALIDARGUMENT_IF( days < 0 || days > UINT32_MAX );
|
||||
|
||||
return static_cast<uint32>(days);
|
||||
}
|
||||
|
||||
typedef union tagTU
|
||||
{
|
||||
FILETIME ft;
|
||||
ULARGE_INTEGER ui;
|
||||
} TU;
|
||||
|
||||
Windows::Foundation::TimeSpan Utils::GetCurrentTime()
|
||||
{
|
||||
SYSTEMTIME curTime = {0};
|
||||
GetSystemTime(&curTime);
|
||||
|
||||
HRESULT hr;
|
||||
TU ftTime = {0};
|
||||
if (SystemTimeToFileTime(&curTime, &ftTime.ft))
|
||||
{
|
||||
Windows::Foundation::TimeSpan ts;
|
||||
ts.Duration = ftTime.ui.QuadPart;
|
||||
return ts;
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||||
THROW_HR(hr);
|
||||
}
|
||||
}
|
||||
|
||||
Platform::String^ Utils::ConvertHResultToString( HRESULT hr )
|
||||
{
|
||||
WCHAR tmp[ 1024 ];
|
||||
_snwprintf_s( tmp, _countof( tmp ), _TRUNCATE, L"0x%0.8x", hr);
|
||||
return ref new Platform::String(tmp);
|
||||
}
|
||||
|
||||
Platform::String^ Utils::ConvertHResultToErrorName( HRESULT hr )
|
||||
{
|
||||
switch( hr )
|
||||
{
|
||||
// Generic errors
|
||||
case S_OK: return L"S_OK";
|
||||
case S_FALSE: return L"S_FALSE";
|
||||
case E_OUTOFMEMORY: return L"E_OUTOFMEMORY";
|
||||
case E_ACCESSDENIED: return L"E_ACCESSDENIED";
|
||||
case E_INVALIDARG: return L"E_INVALIDARG";
|
||||
case E_UNEXPECTED: return L"E_UNEXPECTED";
|
||||
case E_ABORT: return L"E_ABORT";
|
||||
case E_FAIL: return L"E_FAIL";
|
||||
case E_NOTIMPL: return L"E_NOTIMPL";
|
||||
case E_ILLEGAL_METHOD_CALL: return L"E_ILLEGAL_METHOD_CALL";
|
||||
case 0x8007274C: return L"WSATIMEOUT";
|
||||
|
||||
// Authentication specific errors
|
||||
case 0x87DD0003: return L"AM_E_XASD_UNEXPECTED";
|
||||
case 0x87DD0004: return L"AM_E_XASU_UNEXPECTED";
|
||||
case 0x87DD0005: return L"AM_E_XAST_UNEXPECTED";
|
||||
case 0x87DD0006: return L"AM_E_XSTS_UNEXPECTED";
|
||||
case 0x87DD0007: return L"AM_E_XDEVICE_UNEXPECTED";
|
||||
case 0x87DD0008: return L"AM_E_DEVMODE_NOT_AUTHORIZED";
|
||||
case 0x87DD0009: return L"AM_E_NOT_AUTHORIZED";
|
||||
case 0x87DD000A: return L"AM_E_FORBIDDEN";
|
||||
case 0x87DD000B: return L"AM_E_UNKNOWN_TARGET";
|
||||
case 0x87DD000C: return L"AM_E_NSAL_READ_FAILED";
|
||||
case 0x87DD000D: return L"AM_E_TITLE_NOT_AUTHENTICATED";
|
||||
case 0x87DD000E: return L"AM_E_TITLE_NOT_AUTHORIZED";
|
||||
case 0x87DD000F: return L"AM_E_DEVICE_NOT_AUTHENTICATED";
|
||||
case 0x87DD0010: return L"AM_E_INVALID_USER_INDEX";
|
||||
case 0x8015DC00: return L"XO_E_DEVMODE_NOT_AUTHORIZED";
|
||||
case 0x8015DC01: return L"XO_E_SYSTEM_UPDATE_REQUIRED";
|
||||
case 0x8015DC02: return L"XO_E_CONTENT_UPDATE_REQUIRED";
|
||||
case 0x8015DC03: return L"XO_E_ENFORCEMENT_BAN";
|
||||
case 0x8015DC04: return L"XO_E_THIRD_PARTY_BAN";
|
||||
case 0x8015DC05: return L"XO_E_ACCOUNT_PARENTALLY_RESTRICTED";
|
||||
case 0x8015DC06: return L"XO_E_DEVICE_SUBSCRIPTION_NOT_ACTIVATED";
|
||||
case 0x8015DC08: return L"XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED";
|
||||
case 0x8015DC09: return L"XO_E_ACCOUNT_CREATION_REQUIRED";
|
||||
case 0x8015DC0A: return L"XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED";
|
||||
case 0x8015DC0B: return L"XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED";
|
||||
case 0x8015DC0C: return L"XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED";
|
||||
case 0x8015DC0D: return L"XO_E_ACCOUNT_CURFEW";
|
||||
case 0x8015DC0E: return L"XO_E_ACCOUNT_ZEST_MAINTENANCE_REQUIRED";
|
||||
case 0x8015DC0F: return L"XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED";
|
||||
case 0x8015DC10: return L"XO_E_ACCOUNT_MAINTENANCE_REQUIRED";
|
||||
case 0x8015DC11: return L"XO_E_ACCOUNT_TYPE_NOT_ALLOWED";
|
||||
case 0x8015DC12: return L"XO_E_CONTENT_ISOLATION (Verify SCID / Sandbox)";
|
||||
case 0x8015DC13: return L"XO_E_ACCOUNT_NAME_CHANGE_REQUIRED";
|
||||
case 0x8015DC14: return L"XO_E_DEVICE_CHALLENGE_REQUIRED";
|
||||
case 0x8015DC20: return L"XO_E_EXPIRED_DEVICE_TOKEN";
|
||||
case 0x8015DC21: return L"XO_E_EXPIRED_TITLE_TOKEN";
|
||||
case 0x8015DC22: return L"XO_E_EXPIRED_USER_TOKEN";
|
||||
case 0x8015DC23: return L"XO_E_INVALID_DEVICE_TOKEN";
|
||||
case 0x8015DC24: return L"XO_E_INVALID_TITLE_TOKEN";
|
||||
case 0x8015DC25: return L"XO_E_INVALID_USER_TOKEN";
|
||||
|
||||
// HTTP specific errors
|
||||
case WEB_E_UNSUPPORTED_FORMAT: return L"WEB_E_UNSUPPORTED_FORMAT";
|
||||
case WEB_E_INVALID_XML: return L"WEB_E_INVALID_XML";
|
||||
case WEB_E_MISSING_REQUIRED_ELEMENT: return L"WEB_E_MISSING_REQUIRED_ELEMENT";
|
||||
case WEB_E_MISSING_REQUIRED_ATTRIBUTE: return L"WEB_E_MISSING_REQUIRED_ATTRIBUTE";
|
||||
case WEB_E_UNEXPECTED_CONTENT: return L"WEB_E_UNEXPECTED_CONTENT";
|
||||
case WEB_E_RESOURCE_TOO_LARGE: return L"WEB_E_RESOURCE_TOO_LARGE";
|
||||
case WEB_E_INVALID_JSON_STRING: return L"WEB_E_INVALID_JSON_STRING";
|
||||
case WEB_E_INVALID_JSON_NUMBER: return L"WEB_E_INVALID_JSON_NUMBER";
|
||||
case WEB_E_JSON_VALUE_NOT_FOUND: return L"WEB_E_JSON_VALUE_NOT_FOUND";
|
||||
case HTTP_E_STATUS_UNEXPECTED: return L"HTTP_E_STATUS_UNEXPECTED";
|
||||
case HTTP_E_STATUS_UNEXPECTED_REDIRECTION: return L"HTTP_E_STATUS_UNEXPECTED_REDIRECTION";
|
||||
case HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR: return L"HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR";
|
||||
case HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR: return L"HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR";
|
||||
case HTTP_E_STATUS_AMBIGUOUS: return L"HTTP_E_STATUS_AMBIGUOUS";
|
||||
case HTTP_E_STATUS_MOVED: return L"HTTP_E_STATUS_MOVED";
|
||||
case HTTP_E_STATUS_REDIRECT: return L"HTTP_E_STATUS_REDIRECT";
|
||||
case HTTP_E_STATUS_REDIRECT_METHOD: return L"HTTP_E_STATUS_REDIRECT_METHOD";
|
||||
case HTTP_E_STATUS_NOT_MODIFIED: return L"HTTP_E_STATUS_NOT_MODIFIED";
|
||||
case HTTP_E_STATUS_USE_PROXY: return L"HTTP_E_STATUS_USE_PROXY";
|
||||
case HTTP_E_STATUS_REDIRECT_KEEP_VERB: return L"HTTP_E_STATUS_REDIRECT_KEEP_VERB";
|
||||
case HTTP_E_STATUS_BAD_REQUEST: return L"HTTP_E_STATUS_BAD_REQUEST";
|
||||
case HTTP_E_STATUS_DENIED: return L"HTTP_E_STATUS_DENIED";
|
||||
case HTTP_E_STATUS_PAYMENT_REQ: return L"HTTP_E_STATUS_PAYMENT_REQ";
|
||||
case HTTP_E_STATUS_FORBIDDEN: return L"HTTP_E_STATUS_FORBIDDEN";
|
||||
case HTTP_E_STATUS_NOT_FOUND: return L"HTTP_E_STATUS_NOT_FOUND";
|
||||
case HTTP_E_STATUS_BAD_METHOD: return L"HTTP_E_STATUS_BAD_METHOD";
|
||||
case HTTP_E_STATUS_NONE_ACCEPTABLE: return L"HTTP_E_STATUS_NONE_ACCEPTABLE";
|
||||
case HTTP_E_STATUS_PROXY_AUTH_REQ: return L"HTTP_E_STATUS_PROXY_AUTH_REQ";
|
||||
case HTTP_E_STATUS_REQUEST_TIMEOUT: return L"HTTP_E_STATUS_REQUEST_TIMEOUT";
|
||||
case HTTP_E_STATUS_CONFLICT: return L"HTTP_E_STATUS_CONFLICT";
|
||||
case HTTP_E_STATUS_GONE: return L"HTTP_E_STATUS_GONE";
|
||||
case HTTP_E_STATUS_LENGTH_REQUIRED: return L"HTTP_E_STATUS_LENGTH_REQUIRED";
|
||||
case HTTP_E_STATUS_PRECOND_FAILED: return L"HTTP_E_STATUS_PRECOND_FAILED";
|
||||
case HTTP_E_STATUS_REQUEST_TOO_LARGE: return L"HTTP_E_STATUS_REQUEST_TOO_LARGE";
|
||||
case HTTP_E_STATUS_URI_TOO_LONG: return L"HTTP_E_STATUS_URI_TOO_LONG";
|
||||
case HTTP_E_STATUS_UNSUPPORTED_MEDIA: return L"HTTP_E_STATUS_UNSUPPORTED_MEDIA";
|
||||
case HTTP_E_STATUS_RANGE_NOT_SATISFIABLE: return L"HTTP_E_STATUS_RANGE_NOT_SATISFIABLE";
|
||||
case HTTP_E_STATUS_EXPECTATION_FAILED: return L"HTTP_E_STATUS_EXPECTATION_FAILED";
|
||||
case HTTP_E_STATUS_SERVER_ERROR: return L"HTTP_E_STATUS_SERVER_ERROR";
|
||||
case HTTP_E_STATUS_NOT_SUPPORTED: return L"HTTP_E_STATUS_NOT_SUPPORTED";
|
||||
case HTTP_E_STATUS_BAD_GATEWAY: return L"HTTP_E_STATUS_BAD_GATEWAY";
|
||||
case HTTP_E_STATUS_SERVICE_UNAVAIL: return L"HTTP_E_STATUS_SERVICE_UNAVAIL";
|
||||
case HTTP_E_STATUS_GATEWAY_TIMEOUT: return L"HTTP_E_STATUS_GATEWAY_TIMEOUT";
|
||||
case HTTP_E_STATUS_VERSION_NOT_SUP: return L"HTTP_E_STATUS_VERSION_NOT_SUP";
|
||||
|
||||
// WinINet specific errors
|
||||
case INET_E_INVALID_URL: return L"INET_E_INVALID_URL";
|
||||
case INET_E_NO_SESSION: return L"INET_E_NO_SESSION";
|
||||
case INET_E_CANNOT_CONNECT: return L"INET_E_CANNOT_CONNECT";
|
||||
case INET_E_RESOURCE_NOT_FOUND: return L"INET_E_RESOURCE_NOT_FOUND";
|
||||
case INET_E_OBJECT_NOT_FOUND: return L"INET_E_OBJECT_NOT_FOUND";
|
||||
case INET_E_DATA_NOT_AVAILABLE: return L"INET_E_DATA_NOT_AVAILABLE";
|
||||
case INET_E_DOWNLOAD_FAILURE: return L"INET_E_DOWNLOAD_FAILURE";
|
||||
case INET_E_AUTHENTICATION_REQUIRED: return L"INET_E_AUTHENTICATION_REQUIRED";
|
||||
case INET_E_NO_VALID_MEDIA: return L"INET_E_NO_VALID_MEDIA";
|
||||
case INET_E_CONNECTION_TIMEOUT: return L"INET_E_CONNECTION_TIMEOUT";
|
||||
case INET_E_INVALID_REQUEST: return L"INET_E_INVALID_REQUEST";
|
||||
case INET_E_UNKNOWN_PROTOCOL: return L"INET_E_UNKNOWN_PROTOCOL";
|
||||
case INET_E_SECURITY_PROBLEM: return L"INET_E_SECURITY_PROBLEM";
|
||||
case INET_E_CANNOT_LOAD_DATA: return L"INET_E_CANNOT_LOAD_DATA";
|
||||
case INET_E_CANNOT_INSTANTIATE_OBJECT: return L"INET_E_CANNOT_INSTANTIATE_OBJECT";
|
||||
case INET_E_INVALID_CERTIFICATE: return L"INET_E_INVALID_CERTIFICATE";
|
||||
case INET_E_REDIRECT_FAILED: return L"INET_E_REDIRECT_FAILED";
|
||||
case INET_E_REDIRECT_TO_DIR: return L"INET_E_REDIRECT_TO_DIR";
|
||||
}
|
||||
|
||||
return L"Unknown error";
|
||||
}
|
||||
|
||||
void
|
||||
Utils::LogExceptionDebugInfo(
|
||||
__in HRESULT hr,
|
||||
__in_opt PCWSTR pwszFunction,
|
||||
__in_opt PCWSTR pwszFile,
|
||||
__in uint32 uLine
|
||||
)
|
||||
{
|
||||
if( Configuration::IsAtDiagnosticsTraceLevel(XboxNetworkMeshDiagnosticsTraceLevel::Error) )
|
||||
{
|
||||
std::wstring info = L"[Exception]: HRESULT: ";
|
||||
info += Utils::ConvertHResultToString(hr)->Data();
|
||||
info += L"\n";
|
||||
if( pwszFunction != nullptr )
|
||||
{
|
||||
info += L"\t\tFunction: ";
|
||||
info += pwszFunction;
|
||||
info += L"\n";
|
||||
}
|
||||
if( pwszFile != nullptr )
|
||||
{
|
||||
info += L"\t\tFile:";
|
||||
info += pwszFile;
|
||||
info += L"(";
|
||||
info += uLine.ToString()->Data();
|
||||
info += L")\n";
|
||||
}
|
||||
|
||||
OutputDebugString( info.c_str() );
|
||||
Configuration::RaiseDebugOutput( ref new Platform::String(info.c_str()) );
|
||||
}
|
||||
}
|
||||
|
||||
Platform::String^ Utils::FormatString( LPCWSTR strMsg, ... )
|
||||
{
|
||||
WCHAR strBuffer[2048];
|
||||
|
||||
va_list args;
|
||||
va_start(args, strMsg);
|
||||
_vsnwprintf_s( strBuffer, 2048, _TRUNCATE, strMsg, args );
|
||||
strBuffer[2047] = L'\0';
|
||||
|
||||
va_end(args);
|
||||
|
||||
Platform::String^ str = ref new Platform::String(strBuffer);
|
||||
return str;
|
||||
}
|
||||
|
||||
Platform::String^ Utils::GetErrorString( HRESULT hr )
|
||||
{
|
||||
Platform::String^ str = Utils::FormatString(L" %s [0x%0.8x]", ConvertHResultToErrorName(hr)->Data(), hr );
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
void Utils::GetBufferBytes( __in Windows::Storage::Streams::IBuffer^ buffer, __out byte** ppOut )
|
||||
{
|
||||
if( ppOut == nullptr || buffer == nullptr )
|
||||
{
|
||||
throw ref new Platform::InvalidArgumentException();
|
||||
}
|
||||
*ppOut = nullptr;
|
||||
|
||||
Microsoft::WRL::ComPtr<IInspectable> srcBufferInspectable(reinterpret_cast<IInspectable*>( buffer ));
|
||||
Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> srcBufferByteAccess;
|
||||
srcBufferInspectable.As(&srcBufferByteAccess);
|
||||
srcBufferByteAccess->Buffer(ppOut);
|
||||
}
|
||||
|
||||
|
||||
Platform::String^ Utils::PrintSocketAddress(
|
||||
_In_ UINT32 sockaddrSize,
|
||||
_In_ const SOCKADDR* sockaddr
|
||||
)
|
||||
{
|
||||
int result;
|
||||
char hostname[256] = {0};
|
||||
char port[64] = {0};
|
||||
|
||||
ZeroMemory(hostname, sizeof(hostname));
|
||||
result = getnameinfo(sockaddr,
|
||||
sockaddrSize,
|
||||
hostname,
|
||||
sizeof(hostname),
|
||||
port,
|
||||
sizeof( port ),
|
||||
(NI_NUMERICHOST | NI_NUMERICSERV));
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
return Utils::FormatString(
|
||||
L"[%hs]:%hs",
|
||||
hostname,
|
||||
port
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = WSAGetLastError();
|
||||
return Utils::FormatString( L"PrintSocketAddress: %d", result );
|
||||
}
|
||||
}
|
||||
|
||||
Platform::String^ Utils::PrintSecureDeviceAssociation(
|
||||
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
|
||||
bool includeLocal,
|
||||
bool includeRemote
|
||||
)
|
||||
{
|
||||
if( association == nullptr )
|
||||
{
|
||||
return L"null";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Platform::String^ textLocal;
|
||||
if( includeLocal )
|
||||
{
|
||||
SOCKADDR_STORAGE localSocketAddress;
|
||||
Platform::ArrayReference<BYTE> localSocketAddressBytesFromAssociationInTemplate(
|
||||
(BYTE*) &localSocketAddress,
|
||||
sizeof(localSocketAddress));
|
||||
association->GetLocalSocketAddressBytes(localSocketAddressBytesFromAssociationInTemplate);
|
||||
|
||||
textLocal = PrintSocketAddress( sizeof(localSocketAddress), (SOCKADDR*) &localSocketAddress );
|
||||
}
|
||||
|
||||
Platform::String^ textRemote;
|
||||
if( includeRemote )
|
||||
{
|
||||
SOCKADDR_STORAGE remoteSocketAddress;
|
||||
Platform::ArrayReference<BYTE> remoteSocketAddressBytesFromAssociationInTemplate(
|
||||
(BYTE*) &remoteSocketAddress,
|
||||
sizeof(remoteSocketAddress));
|
||||
association->GetRemoteSocketAddressBytes(remoteSocketAddressBytesFromAssociationInTemplate);
|
||||
|
||||
textRemote = PrintSocketAddress( sizeof(remoteSocketAddress), (SOCKADDR*) &remoteSocketAddress );
|
||||
}
|
||||
|
||||
if( includeLocal && includeRemote )
|
||||
{
|
||||
return Utils::FormatString( L"Local:%s Remote:%s", textLocal->Data(), textRemote->Data());
|
||||
}
|
||||
else
|
||||
{
|
||||
return Utils::FormatString( L"%s%s", textLocal->Data(), textRemote->Data());
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return L"Failure printing address";
|
||||
}
|
||||
}
|
||||
|
||||
Platform::String^ Utils::GetThreadDescription( Platform::String^ desc )
|
||||
{
|
||||
Platform::String^ apartmentType = GetApartmentTypeString();
|
||||
DWORD dwThreadId = GetCurrentThreadId();
|
||||
return Utils::FormatString( L"%s: ThreadID:%d [%s]", desc->Data(), dwThreadId, apartmentType->Data() );
|
||||
}
|
||||
|
||||
Platform::String^ Utils::GetApartmentTypeString()
|
||||
{
|
||||
APTTYPE at;
|
||||
APTTYPEQUALIFIER atq;
|
||||
::CoGetApartmentType(&at, &atq);
|
||||
|
||||
Platform::String^ desc;
|
||||
switch (at)
|
||||
{
|
||||
case APTTYPE_CURRENT: desc = L"CURRENT"; break;
|
||||
case APTTYPE_STA: desc = L"STA"; break;
|
||||
case APTTYPE_MTA: desc = L"MTA"; break;
|
||||
case APTTYPE_NA: desc = L"NA"; break;
|
||||
case APTTYPE_MAINSTA: desc = L"MAINSTA"; break;
|
||||
default: desc = L"UNKNOWN"; break;
|
||||
}
|
||||
|
||||
switch (atq)
|
||||
{
|
||||
case APTTYPEQUALIFIER_NONE: desc += L" NONE"; break;
|
||||
case APTTYPEQUALIFIER_IMPLICIT_MTA: desc += L" IMPLICIT_MTA"; break;
|
||||
case APTTYPEQUALIFIER_NA_ON_MTA: desc += L" NA_ON_MTA"; break;
|
||||
case APTTYPEQUALIFIER_NA_ON_STA: desc += L" NA_ON_STA"; break;
|
||||
case APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA: desc += L" NA_ON_IMPLICIT_MTA"; break;
|
||||
case APTTYPEQUALIFIER_NA_ON_MAINSTA: desc += L" NA_ON_MAINSTA"; break;
|
||||
default: desc += L" UNKNOWN"; break;
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#pragma once
|
||||
|
||||
namespace Microsoft { namespace Xbox { namespace Samples { namespace NetworkMesh {
|
||||
|
||||
enum WebErrorStatus
|
||||
{
|
||||
WebErrorStatus_Unknown = 0,
|
||||
WebErrorStatus_CertificateCommonNameIsIncorrect = 1,
|
||||
WebErrorStatus_CertificateExpired = 2,
|
||||
WebErrorStatus_CertificateContainsErrors = 3,
|
||||
WebErrorStatus_CertificateRevoked = 4,
|
||||
WebErrorStatus_CertificateIsInvalid = 5,
|
||||
WebErrorStatus_ServerUnreachable = 6,
|
||||
WebErrorStatus_Timeout = 7,
|
||||
WebErrorStatus_ErrorHttpInvalidServerResponse = 8,
|
||||
WebErrorStatus_ConnectionAborted = 9,
|
||||
WebErrorStatus_ConnectionReset = 10,
|
||||
WebErrorStatus_Disconnected = 11,
|
||||
WebErrorStatus_HttpToHttpsOnRedirection = 12,
|
||||
WebErrorStatus_HttpsToHttpOnRedirection = 13,
|
||||
WebErrorStatus_CannotConnect = 14,
|
||||
WebErrorStatus_HostNameNotResolved = 15,
|
||||
WebErrorStatus_OperationCanceled = 16,
|
||||
WebErrorStatus_RedirectFailed = 17,
|
||||
WebErrorStatus_UnexpectedStatusCode = 18,
|
||||
WebErrorStatus_UnexpectedRedirection = 19,
|
||||
WebErrorStatus_UnexpectedClientError = 20,
|
||||
WebErrorStatus_UnexpectedServerError = 21,
|
||||
WebErrorStatus_MultipleChoices = 300,
|
||||
WebErrorStatus_MovedPermanently = 301,
|
||||
WebErrorStatus_Found = 302,
|
||||
WebErrorStatus_SeeOther = 303,
|
||||
WebErrorStatus_NotModified = 304,
|
||||
WebErrorStatus_UseProxy = 305,
|
||||
WebErrorStatus_TemporaryRedirect = 307,
|
||||
WebErrorStatus_BadRequest = 400,
|
||||
WebErrorStatus_Unauthorized = 401,
|
||||
WebErrorStatus_PaymentRequired = 402,
|
||||
WebErrorStatus_Forbidden = 403,
|
||||
WebErrorStatus_NotFound = 404,
|
||||
WebErrorStatus_MethodNotAllowed = 405,
|
||||
WebErrorStatus_NotAcceptable = 406,
|
||||
WebErrorStatus_ProxyAuthenticationRequired = 407,
|
||||
WebErrorStatus_RequestTimeout = 408,
|
||||
WebErrorStatus_Conflict = 409,
|
||||
WebErrorStatus_Gone = 410,
|
||||
WebErrorStatus_LengthRequired = 411,
|
||||
WebErrorStatus_PreconditionFailed = 412,
|
||||
WebErrorStatus_RequestEntityTooLarge = 413,
|
||||
WebErrorStatus_RequestUriTooLong = 414,
|
||||
WebErrorStatus_UnsupportedMediaType = 415,
|
||||
WebErrorStatus_RequestedRangeNotSatisfiable = 416,
|
||||
WebErrorStatus_ExpectationFailed = 417,
|
||||
WebErrorStatus_InternalServerError = 500,
|
||||
WebErrorStatus_NotImplemented = 501,
|
||||
WebErrorStatus_BadGateway = 502,
|
||||
WebErrorStatus_ServiceUnavailable = 503,
|
||||
WebErrorStatus_GatewayTimeout = 504,
|
||||
WebErrorStatus_HttpVersionNotSupported = 505
|
||||
};
|
||||
|
||||
private class Utils
|
||||
{
|
||||
|
||||
public:
|
||||
static inline bool IsNullOrEmptyString(__in_opt LPCWSTR pcwsz)
|
||||
{
|
||||
return (pcwsz == NULL) || (pcwsz[0] == L'\0');
|
||||
}
|
||||
|
||||
// Searches for a pattern in the source string and replace all occurrences of it with another string.
|
||||
// Pattern must be non-empty
|
||||
// Replacement can be empty, in which case all occurrences of Pattern are deleted
|
||||
static std::wstring&
|
||||
Replace(
|
||||
__inout std::wstring& strSource,
|
||||
__in PCWSTR pwszPattern,
|
||||
__in_opt PCWSTR pwszReplacement,
|
||||
__out_opt size_t* pnOccurrencesReplaced = nullptr
|
||||
);
|
||||
|
||||
static std::vector<std::wstring>
|
||||
StringSplit(
|
||||
__in const std::wstring& string,
|
||||
__in WCHAR seperator
|
||||
);
|
||||
|
||||
static Platform::String^
|
||||
DateTimeToString(
|
||||
__in Windows::Foundation::DateTime dateTime
|
||||
);
|
||||
|
||||
static Platform::String^
|
||||
RemoveBracesFromGuidString(
|
||||
__in Platform::String^ guidString
|
||||
);
|
||||
|
||||
static Windows::Foundation::TimeSpan
|
||||
ConvertSecondsToTimeSpan(
|
||||
__in uint32 seconds
|
||||
);
|
||||
|
||||
static Windows::Foundation::TimeSpan
|
||||
ConvertMillisecondsToTimeSpan(
|
||||
__in uint64 milliseconds
|
||||
);
|
||||
|
||||
static uint32
|
||||
ConvertTimeSpanToSeconds(
|
||||
__in Windows::Foundation::TimeSpan timespan
|
||||
);
|
||||
|
||||
static int64
|
||||
ConvertTimeSpanToMilliseconds(
|
||||
__in Windows::Foundation::TimeSpan timespan
|
||||
);
|
||||
|
||||
static uint32
|
||||
ConvertTimeSpanToDays(
|
||||
__in Windows::Foundation::TimeSpan timespan
|
||||
);
|
||||
|
||||
static Windows::Foundation::TimeSpan GetCurrentTime();
|
||||
|
||||
static Platform::String^
|
||||
ConvertHResultToString( HRESULT hr );
|
||||
|
||||
static Platform::String^
|
||||
ConvertHResultToErrorName( HRESULT hr );
|
||||
|
||||
static void
|
||||
LogExceptionDebugInfo(
|
||||
__in HRESULT hr,
|
||||
__in_opt PCWSTR pwszFunction,
|
||||
__in_opt PCWSTR pwszFile,
|
||||
__in uint32 uLine
|
||||
);
|
||||
|
||||
static Platform::String^ FormatString( LPCWSTR strMsg, ... );
|
||||
|
||||
static Platform::String^ GetErrorString( HRESULT hr );
|
||||
|
||||
static void GetBufferBytes( __in Windows::Storage::Streams::IBuffer^ buffer, __out byte** ppOut );
|
||||
|
||||
static Platform::String^ PrintSocketAddress(
|
||||
_In_ UINT32 sockaddrSize,
|
||||
_In_ const SOCKADDR* sockaddr
|
||||
);
|
||||
|
||||
static Platform::String^ PrintSecureDeviceAssociation(
|
||||
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
|
||||
bool includeLocal,
|
||||
bool includeRemote
|
||||
);
|
||||
|
||||
static Platform::String^ GetThreadDescription( Platform::String^ desc );
|
||||
|
||||
private:
|
||||
static Platform::String^ GetApartmentTypeString();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static std::basic_string<T>&
|
||||
ReplaceSubstring(
|
||||
__inout std::basic_string<T>& strSource,
|
||||
__in_ecount_z(nPatternLength+1) const T* pszPattern,
|
||||
__in const size_t nPatternLength,
|
||||
__in_z const T* pszReplacement,
|
||||
__out_opt size_t* pnOccurrencesReplaced
|
||||
)
|
||||
{
|
||||
THROW_INVALIDARGUMENT_IF_NULL( pszPattern );
|
||||
THROW_INVALIDARGUMENT_IF_NULL( pszReplacement );
|
||||
|
||||
if ( pnOccurrencesReplaced != nullptr )
|
||||
{
|
||||
*pnOccurrencesReplaced = 0;
|
||||
}
|
||||
|
||||
size_t nReplaced = 0;
|
||||
|
||||
if ( nPatternLength > 0 )
|
||||
{
|
||||
// Search the string backward for the given pattern first
|
||||
size_t nPos = strSource.rfind( pszPattern );
|
||||
|
||||
while ( nPos != std::basic_string<T>::npos )
|
||||
{
|
||||
strSource.replace( nPos, nPatternLength, pszReplacement );
|
||||
++nReplaced;
|
||||
|
||||
// Find the given pattern first
|
||||
|
||||
if ( nPos == 0 )
|
||||
{
|
||||
// There is nothing left to look at, break
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the next match starting from the last replaced position
|
||||
nPos = strSource.rfind( pszPattern, nPos - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( pnOccurrencesReplaced != nullptr )
|
||||
{
|
||||
*pnOccurrencesReplaced = nReplaced;
|
||||
}
|
||||
|
||||
return strSource;
|
||||
}
|
||||
|
||||
}}}}
|
||||
|
||||
@@ -0,0 +1,740 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
// Functions to convert dates back and forth to the ISO8601 format
|
||||
// http://www.iso.org/iso/en/prods-services/popstds/datesandtime.html
|
||||
|
||||
#include "pch.h"
|
||||
#include "ISO8601.h"
|
||||
|
||||
#define ISO8601_MAX_USED_CCH 26 // Max amount of characters when generating ISO 8601 strings: YYYY-MM-DDThh:mm:ss.ssssZ + terminating zero
|
||||
|
||||
|
||||
// This table defines different "types" of characters for use as the columns
|
||||
// of the state table:
|
||||
// 0 - invalid character
|
||||
// 1 - number
|
||||
// 2 - '-'
|
||||
// 3 - date-time separator ('T', 't' and ' ')
|
||||
// 4 - ':'
|
||||
// 5 - UTC zone ('Z' and 'z')
|
||||
// 6 - '+'
|
||||
// 7 - second-fraction separator ('.' and ',')
|
||||
|
||||
static const unsigned char iso8601chartable[256] =
|
||||
{
|
||||
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 (00 - 0f)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 (10 - 1f)
|
||||
3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 2, 7, 0, // 2 (20 - 2f)
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 0, 0, 0, 0, // 3 (30 - 3f)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 (40 - 4f)
|
||||
0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, // 5 (50 - 5f)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 (60 - 6f)
|
||||
0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, // 7 (70 - 7f)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 (80 - 8f)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 (90 - 9f)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a (a0 - af)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // b (b0 - bf)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c (c0 - cf)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // d (d0 - df)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e (e0 - ef)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // f (f0 - ff)
|
||||
};
|
||||
|
||||
// Parsing state table is made up of WORD values with this meaning:
|
||||
//
|
||||
// | action | | next state |
|
||||
// |_|_|_|_|_|_|_|_| |_|_|_|_|_|_|_|_|
|
||||
// HIBYTE LOBYTE
|
||||
|
||||
#define STENTRY WORD
|
||||
#define S(a,b) MAKEWORD(b,a)
|
||||
#define ACTION(a) HIBYTE(a)
|
||||
#define STATE(a) LOBYTE(a)
|
||||
|
||||
// Actions
|
||||
#define _OK_ 0x00 // valid character
|
||||
#define _NXT 0x01 // end of a segment, move to next
|
||||
#define _TZM 0x02 // starting '-' time zone offset
|
||||
#define _TZP 0x03 // starting '+' time zone offset
|
||||
#define _MSC 0x04 // millisecond digit
|
||||
#define _TZU 0x05 // time zone UTC
|
||||
#define _ERR 0x80 // error, invalid character
|
||||
#define ___ERROR____ MAKEWORD(0x00, _ERR)
|
||||
|
||||
// State table
|
||||
#define STATE_TABLE_DIM 8
|
||||
static const STENTRY iso8601StateTable[][STATE_TABLE_DIM] =
|
||||
{
|
||||
// unknown number '-' 'T' ':' 'Z' '+' '.'
|
||||
___ERROR____, S(_OK_,0x01), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x00 year
|
||||
___ERROR____, S(_OK_,0x02), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x01
|
||||
___ERROR____, S(_OK_,0x03), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x02
|
||||
___ERROR____, S(_NXT,0x04), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x03
|
||||
___ERROR____, S(_OK_,0x06), S(_OK_,0x05), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x04 month
|
||||
___ERROR____, S(_OK_,0x06), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x05
|
||||
___ERROR____, S(_NXT,0x07), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x06
|
||||
___ERROR____, S(_OK_,0x09), S(_OK_,0x08), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x07 day
|
||||
___ERROR____, S(_OK_,0x09), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x08
|
||||
___ERROR____, S(_NXT,0x0a), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x09
|
||||
___ERROR____, S(_OK_,0x0c), ___ERROR____, S(_OK_,0x0b), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0a hour
|
||||
___ERROR____, S(_OK_,0x0c), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0b
|
||||
___ERROR____, S(_NXT,0x0d), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0c
|
||||
___ERROR____, S(_OK_,0x0f), S(_TZM,0x15), ___ERROR____, S(_OK_,0x0e), S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x0d min
|
||||
___ERROR____, S(_OK_,0x0f), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0e
|
||||
___ERROR____, S(_NXT,0x10), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0f
|
||||
___ERROR____, S(_OK_,0x12), S(_TZM,0x15), ___ERROR____, S(_OK_,0x11), S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x10 sec
|
||||
___ERROR____, S(_OK_,0x12), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x11
|
||||
___ERROR____, S(_NXT,0x13), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x12
|
||||
___ERROR____, ___ERROR____, S(_TZM,0x15), ___ERROR____, ___ERROR____, S(_TZU,0x1b), S(_TZP,0x15), S(_OK_,0x14), //0x13 '.' or 'Z' or '+/-'
|
||||
___ERROR____, S(_MSC,0x14), S(_TZM,0x15), ___ERROR____, ___ERROR____, S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x14 fragment of a second
|
||||
___ERROR____, S(_OK_,0x16), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x15 TZ offset - hour
|
||||
___ERROR____, S(_NXT,0x17), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x16
|
||||
___ERROR____, S(_OK_,0x19), ___ERROR____, ___ERROR____, S(_OK_,0x18), ___ERROR____, ___ERROR____, ___ERROR____, //0x17 TZ offset - minute
|
||||
___ERROR____, S(_OK_,0x19), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x18
|
||||
___ERROR____, S(_NXT,0x1a), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x19
|
||||
___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x1a offset done - parsing done
|
||||
___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x1b UTC zone done - parsing done
|
||||
};
|
||||
|
||||
|
||||
#define IsLeapYear(YEARS) ( \
|
||||
(((YEARS) % 400 == 0) || \
|
||||
((YEARS) % 100 != 0) && ((YEARS) % 4 == 0)) ? \
|
||||
TRUE \
|
||||
: \
|
||||
FALSE \
|
||||
)
|
||||
|
||||
static HRESULT _GetNumDaysForYearMonth(
|
||||
WORD wYear,
|
||||
WORD wMonth,
|
||||
__out WORD *pwDays)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (NULL == pwDays ||
|
||||
wMonth < 1 || wMonth > 12 ||
|
||||
wYear < 1601 || wYear > 9999)
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
static const WORD s_wNumDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
*pwDays = s_wNumDays[wMonth - 1];
|
||||
// Check for leap year
|
||||
if ((wMonth == 2) && IsLeapYear(wYear))
|
||||
{
|
||||
(*pwDays)++;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
#define ONE_SECOND 10000000ui64 // number of 100-nanosecond intervals in a second
|
||||
#define ONE_MINUTE (ONE_SECOND * 60) // number of 100-nanosecond intervals in a minute
|
||||
#define ONE_HOUR (ONE_MINUTE * 60) // number of 100-nanosecond intervals in an hour
|
||||
|
||||
typedef union tagTU
|
||||
{
|
||||
FILETIME ft;
|
||||
ULARGE_INTEGER ui;
|
||||
} TU;
|
||||
|
||||
static HRESULT _AddTZOffset(
|
||||
Iso8601ParsingStage ips,
|
||||
WORD wValue,
|
||||
int iTZDirection,
|
||||
__inout SYSTEMTIME *pSysTime)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
_ASSERTE((ips == IPS_TZHOUR && wValue < 24) || (ips == IPS_TZMINUTE && wValue < 60));
|
||||
_ASSERTE(iTZDirection == 1 || iTZDirection == -1);
|
||||
|
||||
// Convert SYSTEMTIME to FILETIME to perform date-time arithmetic
|
||||
TU ftTime = {0};
|
||||
if (SystemTimeToFileTime(pSysTime, &ftTime.ft))
|
||||
{
|
||||
ULARGE_INTEGER ulOffset;
|
||||
ulOffset.QuadPart = (ips == IPS_TZHOUR) ? ONE_HOUR * wValue : ONE_MINUTE * wValue;
|
||||
if (iTZDirection > 0)
|
||||
{
|
||||
ftTime.ui.QuadPart += ulOffset.QuadPart;
|
||||
}
|
||||
else
|
||||
{
|
||||
ftTime.ui.QuadPart -= ulOffset.QuadPart;
|
||||
}
|
||||
if (!FileTimeToSystemTime(&ftTime.ft, pSysTime))
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT _CheckValueAndAddToSysTime(
|
||||
Iso8601ParsingStage ips,
|
||||
WORD wValue,
|
||||
WORD wMSDigits,
|
||||
int iTZDirection,
|
||||
__inout SYSTEMTIME *pSysTime)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
WORD wHiLimit = 0;
|
||||
WORD wLoLimit = 0;
|
||||
WORD *pDateWord = NULL;
|
||||
|
||||
_ASSERTE((iTZDirection == 0 && ips != IPS_TZHOUR && ips != IPS_TZMINUTE) ||
|
||||
(iTZDirection != 0 && (ips == IPS_TZHOUR || ips == IPS_TZMINUTE)));
|
||||
_ASSERTE((wMSDigits == 0 && ips != IPS_MILLISECOND) ||
|
||||
(wMSDigits > 0 && wMSDigits < 4 && ips == IPS_MILLISECOND));
|
||||
|
||||
switch (ips)
|
||||
{
|
||||
case IPS_YEAR:
|
||||
wLoLimit = 1601;
|
||||
wHiLimit = 9999;
|
||||
pDateWord = (WORD *) &(pSysTime->wYear);
|
||||
break;
|
||||
case IPS_MONTH:
|
||||
wLoLimit = 1;
|
||||
wHiLimit = 12;
|
||||
pDateWord = (WORD *) &(pSysTime->wMonth);
|
||||
break;
|
||||
case IPS_DAY:
|
||||
wLoLimit = 1;
|
||||
hr = _GetNumDaysForYearMonth(pSysTime->wYear, pSysTime->wMonth, &wHiLimit);
|
||||
_ASSERTE(SUCCEEDED(hr)); // internal call, should never fail
|
||||
pDateWord = (WORD *) &(pSysTime->wDay);
|
||||
break;
|
||||
case IPS_HOUR:
|
||||
wLoLimit = 0;
|
||||
wHiLimit = 23;
|
||||
pDateWord = (WORD *) &(pSysTime->wHour);
|
||||
break;
|
||||
case IPS_MINUTE:
|
||||
wLoLimit = 0;
|
||||
wHiLimit = 59;
|
||||
pDateWord = (WORD *) &(pSysTime->wMinute);
|
||||
break;
|
||||
case IPS_SECOND:
|
||||
wLoLimit = 0;
|
||||
wHiLimit = 59;
|
||||
pDateWord = (WORD *) &(pSysTime->wSecond);
|
||||
break;
|
||||
case IPS_MILLISECOND:
|
||||
wLoLimit = 0;
|
||||
wHiLimit = 999;
|
||||
while (wMSDigits++ < 3)
|
||||
{
|
||||
wValue *= 10;
|
||||
}
|
||||
pDateWord = (WORD *) &(pSysTime->wMilliseconds);
|
||||
break;
|
||||
case IPS_TZHOUR:
|
||||
wLoLimit = 0;
|
||||
wHiLimit = 23; // valid offsets appear to be -12 to +14, but we'll allow any valid hour value
|
||||
pDateWord = (WORD *) &(pSysTime->wHour);
|
||||
break;
|
||||
case IPS_TZMINUTE:
|
||||
wLoLimit = 0;
|
||||
wHiLimit = 59;
|
||||
pDateWord = (WORD *) &(pSysTime->wMinute);
|
||||
break;
|
||||
default:
|
||||
hr = E_UNEXPECTED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
_ASSERTE(NULL != pDateWord);
|
||||
if (wValue < wLoLimit || wValue > wHiLimit)
|
||||
{
|
||||
hr = E_ABORT;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ASSERTE(*pDateWord == 0 || ips >= IPS_MILLISECOND);
|
||||
if (ips != IPS_TZHOUR && ips != IPS_TZMINUTE)
|
||||
{
|
||||
*pDateWord = wValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ASSERTE(iTZDirection == 1 || iTZDirection == -1);
|
||||
// handle offset rollover - can affect entire date
|
||||
hr = _AddTZOffset(ips, wValue, iTZDirection, pSysTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT _iso8601ToSysTime(
|
||||
const char *pszisoDate,
|
||||
__out SYSTEMTIME *pSysTime,
|
||||
__out Iso8601ParsingStage *pips)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (NULL == pszisoDate ||
|
||||
NULL == pSysTime ||
|
||||
NULL == pips ||
|
||||
*pszisoDate == '\0')
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
ZeroMemory(pSysTime, sizeof(SYSTEMTIME));
|
||||
*pips = IPS_INVALID;
|
||||
|
||||
BYTE action = 0;
|
||||
BYTE state = 0;
|
||||
WORD DateWord = 0;
|
||||
Iso8601ParsingStage ips = IPS_YEAR;
|
||||
int iTZDirection = 0;
|
||||
WORD wMSDigits = 0;
|
||||
|
||||
// Main state machine loop
|
||||
while(*pszisoDate && SUCCEEDED(hr))
|
||||
{
|
||||
unsigned char code = iso8601chartable[*pszisoDate];
|
||||
|
||||
// Prevent overflows - sanity check
|
||||
if ((code >= STATE_TABLE_DIM) ||
|
||||
(state >= ARRAYSIZE(iso8601StateTable)))
|
||||
{
|
||||
hr = E_UNEXPECTED;
|
||||
break;
|
||||
}
|
||||
|
||||
STENTRY stValue = iso8601StateTable[state][code];
|
||||
state = STATE(stValue);
|
||||
action = ACTION(stValue);
|
||||
|
||||
switch(action)
|
||||
{
|
||||
case _OK_: // input OK, valid character
|
||||
case _NXT: // finish piece and advance to next stage, valid character
|
||||
{
|
||||
if (code == 1)
|
||||
{
|
||||
DateWord = (DateWord * 10) + (*pszisoDate - '0');
|
||||
}
|
||||
|
||||
if (action == _NXT)
|
||||
{
|
||||
hr = _CheckValueAndAddToSysTime(ips, DateWord, 0 /*wMSDigits*/, iTZDirection, pSysTime);
|
||||
DateWord = 0;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*pips = ips;
|
||||
ips = Iso8601ParsingStage(int(ips) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case _TZM: // start '-' timezone offset
|
||||
iTZDirection = 1;
|
||||
DateWord = 0;
|
||||
*pips = ips;
|
||||
ips = IPS_TZHOUR;
|
||||
break;
|
||||
case _TZP: // start '+' timezone offset
|
||||
iTZDirection = -1;
|
||||
DateWord = 0;
|
||||
*pips = ips;
|
||||
ips = IPS_TZHOUR;
|
||||
break;
|
||||
case _MSC: // process millisecond digit
|
||||
_ASSERTE(code == 1 && ips == IPS_MILLISECOND);
|
||||
wMSDigits++;
|
||||
if (wMSDigits < 4)
|
||||
{
|
||||
DateWord = (DateWord * 10) + (*pszisoDate - '0');
|
||||
|
||||
hr = _CheckValueAndAddToSysTime(ips, DateWord, wMSDigits, iTZDirection, pSysTime);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*pips = IPS_MILLISECOND;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case _TZU: // 'Z' for UTC zone
|
||||
*pips = IPS_TZUTC;
|
||||
ips = IPS_TZUTC;
|
||||
break;
|
||||
case _ERR: // error, invalid character
|
||||
_ASSERTE(state == 0x00);
|
||||
hr = E_ABORT;
|
||||
break;
|
||||
default:
|
||||
hr = E_UNEXPECTED;
|
||||
break;
|
||||
}
|
||||
|
||||
pszisoDate++;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr) && *pszisoDate == '\0')
|
||||
{
|
||||
if (action != _NXT && action != _MSC && action != _TZU)
|
||||
{
|
||||
hr = E_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
if (hr != S_OK && hr != E_ABORT)
|
||||
{
|
||||
*pips = IPS_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
_ASSERTE(hr != E_UNEXPECTED);
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT _iso8601ToFileTime(
|
||||
const char *pszisoDate,
|
||||
__out FILETIME *pftTime,
|
||||
__out Iso8601ParsingStage *pips)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
SYSTEMTIME stTime = {0};
|
||||
|
||||
hr = _iso8601ToSysTime(pszisoDate, &stTime, pips);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
if (!SystemTimeToFileTime(&stTime, pftTime))
|
||||
{
|
||||
hr = E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT _SysTimeToiso8601(
|
||||
__in SYSTEMTIME *pstTime,
|
||||
__in BOOL fGeneratePartial,
|
||||
__out_ecount(cch) char *pszBuf,
|
||||
__in ULONG cch,
|
||||
__in BOOL fUseShortTimeFormat = FALSE
|
||||
)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (NULL == pstTime ||
|
||||
NULL == pszBuf ||
|
||||
cch < ISO8601_MAX_USED_CCH)
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
if (pstTime->wYear < 1601 || // FILETIME cannot handle less
|
||||
pstTime->wYear > 9999 || // ISO8601 has four digits for a year
|
||||
pstTime->wMonth > 12 ||
|
||||
pstTime->wDay > 31 ||
|
||||
pstTime->wHour > 24 ||
|
||||
pstTime->wMinute > 59 ||
|
||||
pstTime->wSecond > 59 ||
|
||||
pstTime->wMilliseconds > 999)
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
WORD wDays = 0;
|
||||
hr = _GetNumDaysForYearMonth(pstTime->wYear, pstTime->wMonth, &wDays);
|
||||
if (SUCCEEDED(hr) && pstTime->wDay > wDays)
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
pszBuf[0] = static_cast<char>((pstTime->wYear / 1000) + '0');
|
||||
pszBuf[1] = static_cast<char>(((pstTime->wYear / 100) % 10) + '0');
|
||||
pszBuf[2] = static_cast<char>(((pstTime->wYear / 10) % 10) + '0');
|
||||
pszBuf[3] = static_cast<char>(((pstTime->wYear) % 10) + '0');
|
||||
if (pstTime->wMonth > 0 ||
|
||||
!fGeneratePartial)
|
||||
{
|
||||
pszBuf[4] = '-';
|
||||
pszBuf[5] = static_cast<char>((pstTime->wMonth / 10) + '0');
|
||||
pszBuf[6] = static_cast<char>((pstTime->wMonth % 10) + '0');
|
||||
if (pstTime->wDay > 0 ||
|
||||
!fGeneratePartial)
|
||||
{
|
||||
pszBuf[7] = '-';
|
||||
pszBuf[8] = static_cast<char>((pstTime->wDay / 10) + '0');
|
||||
pszBuf[9] = static_cast<char>((pstTime->wDay % 10) + '0');
|
||||
if (pstTime->wHour != 0 ||
|
||||
pstTime->wMinute != 0 ||
|
||||
pstTime->wSecond != 0 ||
|
||||
pstTime->wMilliseconds != 0 ||
|
||||
!fGeneratePartial)
|
||||
{
|
||||
pszBuf[10] = 'T';
|
||||
pszBuf[11] = static_cast<char>(pstTime->wHour / 10 + '0');
|
||||
pszBuf[12] = static_cast<char>((pstTime->wHour % 10) + '0');
|
||||
pszBuf[13] = ':';
|
||||
pszBuf[14] = static_cast<char>(pstTime->wMinute / 10 + '0');
|
||||
pszBuf[15] = static_cast<char>((pstTime->wMinute % 10) + '0');
|
||||
pszBuf[16] = ':';
|
||||
pszBuf[17] = static_cast<char>(pstTime->wSecond / 10 + '0');
|
||||
pszBuf[18] = static_cast<char>((pstTime->wSecond % 10) + '0');
|
||||
if ( !fUseShortTimeFormat && ( pstTime->wMilliseconds != 0 ) )
|
||||
{
|
||||
// YYYY-MM-DDThh:mm:ss.ssssZ
|
||||
pszBuf[19] = '.';
|
||||
pszBuf[20] = static_cast<char>(pstTime->wMilliseconds / 100 + '0');
|
||||
pszBuf[21] = static_cast<char>(((pstTime->wMilliseconds / 10) % 10) + '0');
|
||||
pszBuf[22] = static_cast<char>((pstTime->wMilliseconds % 10) + '0');
|
||||
|
||||
// pad the last digit of millisecond with 0
|
||||
pszBuf[23] = '0';
|
||||
pszBuf[24] = 'Z';
|
||||
pszBuf[25] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// YYYY-MM-DDThh:mm:ssZ
|
||||
pszBuf[19] = 'Z';
|
||||
pszBuf[20] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// YYYY-MM-DD
|
||||
pszBuf[10] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// YYYY-MM
|
||||
pszBuf[7] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// YYYY
|
||||
pszBuf[4] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT _FileTimeToiso8601(
|
||||
const FILETIME *pftTime,
|
||||
BOOL fGeneratePartial,
|
||||
__out_ecount(cch) char *pszBuf,
|
||||
ULONG cch,
|
||||
BOOL fUseShortTimeFormat
|
||||
)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
SYSTEMTIME stTime = {0};
|
||||
|
||||
if (NULL == pftTime)
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
if (FileTimeToSystemTime( pftTime, &stTime))
|
||||
{
|
||||
hr = _SysTimeToiso8601( &stTime, fGeneratePartial, pszBuf, cch, fUseShortTimeFormat );
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT FILETIMEToISO8601W(
|
||||
__in const FILETIME* pft,
|
||||
__in BOOL fGeneratePartial,
|
||||
__out_ecount(cchISO8601) PWSTR pszISO8601,
|
||||
__in size_t cchISO8601,
|
||||
__in BOOL fUseShortTimeFormat
|
||||
)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
CHAR aszISO8601[ISO8601_MAX_USED_CCH];
|
||||
hr = _FileTimeToiso8601( pft, fGeneratePartial, aszISO8601, ARRAYSIZE(aszISO8601), fUseShortTimeFormat );
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
int cchSHAnsiToUnicode;
|
||||
hr = SizeTToInt(cchISO8601, &cchSHAnsiToUnicode);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ( ISO8601_MAX_USED_CCH >= ::MultiByteToWideChar(
|
||||
CP_ACP,
|
||||
0,
|
||||
aszISO8601,
|
||||
-1,
|
||||
pszISO8601, cchSHAnsiToUnicode) ) ? S_OK : E_UNEXPECTED;
|
||||
_ASSERTE(SUCCEEDED(hr));
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT ISO8601ToFILETIMEW(
|
||||
__in PCWSTR pszISO8601,
|
||||
__out FILETIME* pft,
|
||||
__out Iso8601ParsingStage *pips)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
CHAR aszISO8601[ISO8601_MAX_CCH];
|
||||
|
||||
if( !::WideCharToMultiByte(
|
||||
CP_ACP,
|
||||
0,
|
||||
pszISO8601,
|
||||
-1,
|
||||
aszISO8601,
|
||||
ISO8601_MAX_CCH,
|
||||
NULL,
|
||||
NULL ) )
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = _iso8601ToFileTime(aszISO8601, pft, pips);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT SYSTEMTIMEToISO8601ExW(
|
||||
__in const SYSTEMTIME* pst,
|
||||
__in BOOL fGeneratePartial,
|
||||
__out_ecount(cchISO8601) PWSTR pszISO8601,
|
||||
__in size_t cchISO8601)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
SYSTEMTIME st = *pst;
|
||||
CHAR aszISO8601[ISO8601_MAX_USED_CCH];
|
||||
hr = _SysTimeToiso8601(&st, fGeneratePartial, aszISO8601, ARRAYSIZE(aszISO8601));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
int cchSHAnsiToUnicode;
|
||||
hr = SizeTToInt(cchISO8601, &cchSHAnsiToUnicode);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ( ISO8601_MAX_USED_CCH >= ::MultiByteToWideChar(
|
||||
CP_ACP,
|
||||
0,
|
||||
aszISO8601,
|
||||
-1,
|
||||
pszISO8601,
|
||||
cchSHAnsiToUnicode ) ) ? S_OK : E_UNEXPECTED;
|
||||
_ASSERTE(SUCCEEDED(hr));
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT SYSTEMTIMEToISO8601W(
|
||||
__in const SYSTEMTIME* pst,
|
||||
__out_ecount(cchISO8601) PWSTR pszISO8601,
|
||||
__in size_t cchISO8601)
|
||||
{
|
||||
return SYSTEMTIMEToISO8601ExW(pst, FALSE /*fGeneratePartial*/, pszISO8601, cchISO8601);
|
||||
}
|
||||
|
||||
HRESULT ISO8601ToSYSTEMTIMEExW(
|
||||
__in PCWSTR pszISO8601,
|
||||
__out SYSTEMTIME* pst,
|
||||
__out Iso8601ParsingStage *pips)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
CHAR aszISO8601[ISO8601_MAX_CCH];
|
||||
|
||||
if( !::WideCharToMultiByte(
|
||||
CP_ACP,
|
||||
0,
|
||||
pszISO8601,
|
||||
-1,
|
||||
aszISO8601,
|
||||
ISO8601_MAX_CCH,
|
||||
NULL,
|
||||
NULL ) )
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = _iso8601ToSysTime(aszISO8601, pst, pips);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT ISO8601ToSYSTEMTIMEW(
|
||||
__in PCWSTR pszISO8601,
|
||||
__out SYSTEMTIME* pst)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
Iso8601ParsingStage ips = IPS_INVALID;
|
||||
|
||||
hr = ISO8601ToSYSTEMTIMEExW(pszISO8601, pst, &ips);
|
||||
|
||||
// Fix up less-than-full-date
|
||||
if (SUCCEEDED(hr) && ips < IPS_DAY)
|
||||
{
|
||||
if (ips < IPS_DAY)
|
||||
{
|
||||
pst->wDay = 1;
|
||||
}
|
||||
if (ips < IPS_MONTH)
|
||||
{
|
||||
pst->wMonth = 1;
|
||||
}
|
||||
hr = S_OK;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
#pragma once
|
||||
|
||||
// Functions to convert dates back and forth to the ISO8601 format
|
||||
// (http://www.iso.org/iso/en/prods-services/popstds/datesandtime.html)
|
||||
//
|
||||
// Supported dates are from 1601-01-01 (SYSTEMTIME and FILETIME limitation) to
|
||||
// 9999-12-31 (ISO8601 limitation)
|
||||
|
||||
// Longest form of ISO8601 is 40 chars + 1 for terminating zero
|
||||
#define ISO8601_MAX_CCH 41
|
||||
|
||||
// Iso8601ParsingStage enum
|
||||
//
|
||||
// This enumeration is design so values can be compared.
|
||||
// ISO8601 dates look like this: YYYY-MM-DDThh:mm:ss.sss+/-hh:mm
|
||||
// So e.g. by parsing the date and then asking if parse stage was
|
||||
// <IPS_HOUR you can tell if string contained any time at all, or just the date
|
||||
enum Iso8601ParsingStage
|
||||
{
|
||||
IPS_INVALID = -1,
|
||||
IPS_YEAR = 0,
|
||||
IPS_MONTH,
|
||||
IPS_DAY,
|
||||
IPS_HOUR,
|
||||
IPS_MINUTE,
|
||||
IPS_SECOND,
|
||||
IPS_MILLISECOND,
|
||||
IPS_TZHOUR,
|
||||
IPS_TZMINUTE,
|
||||
IPS_TZUTC,
|
||||
};
|
||||
|
||||
HRESULT
|
||||
FILETIMEToISO8601W(
|
||||
__in const FILETIME* pft,
|
||||
__in BOOL fGeneratePartial,
|
||||
__out_ecount(cchISO8601) PWSTR pszISO8601,
|
||||
__in size_t cchISO8601,
|
||||
__in BOOL fUseShortTimeFormat = FALSE
|
||||
);
|
||||
|
||||
HRESULT
|
||||
ISO8601ToFILETIMEW(
|
||||
__in PCWSTR pszISO8601,
|
||||
__out FILETIME* pft,
|
||||
__out Iso8601ParsingStage* pips
|
||||
);
|
||||
|
||||
HRESULT
|
||||
SYSTEMTIMEToISO8601ExW(
|
||||
__in const SYSTEMTIME* pst,
|
||||
__in BOOL fGeneratePartial,
|
||||
__out_ecount(cchISO8601) PWSTR pszISO8601,
|
||||
__in size_t cchISO8601
|
||||
);
|
||||
|
||||
HRESULT
|
||||
SYSTEMTIMEToISO8601W(
|
||||
__in const SYSTEMTIME* pst,
|
||||
__out_ecount(cchISO8601) PWSTR pszISO8601,
|
||||
__in size_t cchISO8601
|
||||
);
|
||||
|
||||
HRESULT
|
||||
ISO8601ToSYSTEMTIMEExW(
|
||||
__in PCWSTR pszISO8601,
|
||||
__out SYSTEMTIME* pst,
|
||||
__out Iso8601ParsingStage* pips
|
||||
);
|
||||
|
||||
HRESULT ISO8601ToSYSTEMTIMEW(
|
||||
__in PCWSTR pszISO8601,
|
||||
__out SYSTEMTIME* pst
|
||||
);
|
||||
Reference in New Issue
Block a user