This commit is contained in:
watrabi
2025-09-18 17:55:52 -04:00
commit 977f1ff4b8
15030 changed files with 17324420 additions and 0 deletions
@@ -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
);