708 lines
17 KiB
C++
708 lines
17 KiB
C++
//+--------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1999
|
|
//
|
|
// File: lsreport.cpp
|
|
//
|
|
// Contents: LSReport engine - complete back end
|
|
//
|
|
// History: 06-10-99 t-BStern Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "lsreport.h"
|
|
#include "lsrepdef.h"
|
|
#include <time.h>
|
|
#include <oleauto.h>
|
|
|
|
TCHAR noExpire[NOEXPIRE_LENGTH] = { 0 };
|
|
TCHAR header[HEADER_LENGTH] = { 0 };
|
|
|
|
TCHAR szTemp[TYPESTR_LENGTH] = { 0 };
|
|
TCHAR szActive[TYPESTR_LENGTH] = { 0 };
|
|
TCHAR szUpgrade[TYPESTR_LENGTH] = { 0 };
|
|
TCHAR szRevoked[TYPESTR_LENGTH] = { 0 };
|
|
TCHAR szPending[TYPESTR_LENGTH] = { 0 };
|
|
TCHAR szConcur[TYPESTR_LENGTH] = { 0 };
|
|
TCHAR szUnknown[TYPESTR_LENGTH] = { 0 };
|
|
|
|
//-----------------------------
|
|
DWORD
|
|
GetPageSize( VOID ) {
|
|
|
|
static DWORD dwPageSize = 0;
|
|
|
|
if ( !dwPageSize ) {
|
|
|
|
SYSTEM_INFO sysInfo = { 0 };
|
|
|
|
GetSystemInfo( &sysInfo ); // cannot fail.
|
|
|
|
dwPageSize = sysInfo.dwPageSize;
|
|
|
|
}
|
|
|
|
return dwPageSize;
|
|
|
|
}
|
|
|
|
/*++**************************************************************
|
|
NAME: MyVirtualAlloc
|
|
|
|
as Malloc, but automatically protects the last page of the
|
|
allocation. This simulates pageheap behavior without requiring
|
|
it.
|
|
|
|
MODIFIES: ppvData -- receives memory
|
|
|
|
TAKES: dwSize -- minimum amount of data to get
|
|
|
|
RETURNS: TRUE when the function succeeds.
|
|
FALSE otherwise.
|
|
LASTERROR: not set
|
|
Free with MyVirtualFree
|
|
|
|
|
|
**************************************************************--*/
|
|
|
|
BOOL
|
|
MyVirtualAlloc( IN DWORD dwSize,
|
|
OUT PVOID *ppvData )
|
|
{
|
|
|
|
PBYTE pbData;
|
|
DWORD dwTotalSize;
|
|
PVOID pvLastPage;
|
|
|
|
// ensure that we allocate one extra page
|
|
|
|
dwTotalSize = dwSize / GetPageSize();
|
|
if( dwSize % GetPageSize() ) {
|
|
dwTotalSize ++;
|
|
}
|
|
|
|
// this is the guard page
|
|
dwTotalSize++;
|
|
dwTotalSize *= GetPageSize();
|
|
|
|
// do the alloc
|
|
|
|
pbData = (PBYTE) VirtualAlloc( NULL, // don't care where
|
|
dwTotalSize,
|
|
MEM_COMMIT |
|
|
MEM_TOP_DOWN,
|
|
PAGE_READWRITE );
|
|
|
|
if ( pbData ) {
|
|
|
|
pbData += dwTotalSize;
|
|
|
|
// find the LAST page.
|
|
|
|
pbData -= GetPageSize();
|
|
|
|
pvLastPage = pbData;
|
|
|
|
// now, carve out a chunk for the caller:
|
|
|
|
pbData -= dwSize;
|
|
|
|
// last, protect the last page:
|
|
|
|
if ( VirtualProtect( pvLastPage,
|
|
1, // protect the page containing the last byte
|
|
PAGE_NOACCESS,
|
|
&dwSize ) ) {
|
|
|
|
*ppvData = pbData;
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
VirtualFree( pbData, 0, MEM_RELEASE );
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
MyVirtualFree( IN PVOID pvData )
|
|
{
|
|
|
|
VirtualFree( pvData, 0, MEM_RELEASE );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns TRUE on success.
|
|
|
|
BOOL
|
|
InitLSReportStrings(VOID)
|
|
{
|
|
return (
|
|
LoadString(NULL, IDS_HEADER_TEXT, header, HEADER_LENGTH) &&
|
|
|
|
LoadString(NULL, IDS_NO_EXPIRE, noExpire, NOEXPIRE_LENGTH) &&
|
|
|
|
LoadString(NULL, IDS_TEMPORARY_LICENSE, szTemp, TYPESTR_LENGTH) &&
|
|
LoadString(NULL, IDS_ACTIVE_LICENSE, szActive, TYPESTR_LENGTH) &&
|
|
LoadString(NULL, IDS_UPGRADED_LICENSE, szUpgrade, TYPESTR_LENGTH) &&
|
|
LoadString(NULL, IDS_REVOKED_LICENSE, szRevoked, TYPESTR_LENGTH) &&
|
|
LoadString(NULL, IDS_PENDING_LICENSE, szPending, TYPESTR_LENGTH) &&
|
|
LoadString(NULL, IDS_CONCURRENT_LICENSE, szConcur, TYPESTR_LENGTH) &&
|
|
LoadString(NULL, IDS_UNKNOWN_LICENSE, szUnknown, TYPESTR_LENGTH)
|
|
);
|
|
}
|
|
|
|
typedef DWORD (WINAPI* PTLSGETLASTERRORFIXED) (
|
|
TLS_HANDLE hHandle,
|
|
LPTSTR *pszBuffer,
|
|
PDWORD pdwErrCode
|
|
);
|
|
|
|
RPC_STATUS
|
|
TryGetLastError(PCONTEXT_HANDLE hBinding,
|
|
LPTSTR *pszBuffer)
|
|
{
|
|
RPC_STATUS status;
|
|
DWORD dwErrCode;
|
|
HINSTANCE hModule = LoadLibrary(L"mstlsapi.dll");
|
|
|
|
if (hModule)
|
|
{
|
|
PTLSGETLASTERRORFIXED pfnGetLastErrorFixed = (PTLSGETLASTERRORFIXED) GetProcAddress(hModule,"TLSGetLastErrorFixed");
|
|
|
|
if (pfnGetLastErrorFixed)
|
|
{
|
|
status = pfnGetLastErrorFixed(hBinding,pszBuffer,&dwErrCode);
|
|
if(status == RPC_S_OK && dwErrCode == ERROR_SUCCESS && pszBuffer != NULL)
|
|
{
|
|
FreeLibrary(hModule);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
FreeLibrary(hModule);
|
|
}
|
|
|
|
{
|
|
|
|
LPTSTR lpszError = NULL;
|
|
status = ERROR_NOACCESS;
|
|
try
|
|
{
|
|
if ( !MyVirtualAlloc( ( TLS_ERROR_LENGTH ) * sizeof( TCHAR ),
|
|
(PVOID*) &lpszError ) )
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
DWORD uSize = TLS_ERROR_LENGTH ;
|
|
|
|
memset(lpszError, 0, ( TLS_ERROR_LENGTH ) * sizeof( TCHAR ));
|
|
|
|
|
|
status = TLSGetLastError(hBinding,uSize,lpszError,&dwErrCode);
|
|
if(status == RPC_S_OK && dwErrCode == ERROR_SUCCESS)
|
|
{
|
|
*pszBuffer = (LPTSTR)MIDL_user_allocate((TLS_ERROR_LENGTH+1)*sizeof(TCHAR));
|
|
|
|
if (NULL != *pszBuffer)
|
|
{
|
|
_tcscpy(*pszBuffer,lpszError);
|
|
}
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
status = ERROR_NOACCESS;
|
|
}
|
|
|
|
if(lpszError)
|
|
MyVirtualFree(lpszError);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
// Given a keypack and a machine to connect to, read every license in that kp.
|
|
// Is not called directly.
|
|
|
|
DWORD
|
|
LicenseLoop(
|
|
IN FILE *OutFile,
|
|
IN LPWSTR szName, // who owns this keypack?
|
|
IN DWORD kpID, // which keypack
|
|
IN LPCTSTR szProductDesc,
|
|
IN BOOL bTempOnly,
|
|
IN const PSYSTEMTIME stStart,
|
|
IN const PSYSTEMTIME stEnd,
|
|
IN BOOL fUseLimits,
|
|
IN BOOL fHwid) // are the above 2 parms valid
|
|
{
|
|
TLS_HANDLE subHand;
|
|
DWORD dwStatus;
|
|
DWORD dwErrCode = ERROR_SUCCESS;
|
|
WCHAR *msg = NULL;
|
|
LSLicense lsl;
|
|
|
|
subHand = TLSConnectToLsServer(szName);
|
|
|
|
if (subHand == NULL)
|
|
{
|
|
// The machine suddenly went away.
|
|
|
|
ShowError(GetLastError(), NULL, TRUE);
|
|
dwErrCode = ERROR_BAD_CONNECT;
|
|
}
|
|
else
|
|
{
|
|
lsl.dwKeyPackId = kpID;
|
|
dwStatus = TLSLicenseEnumBegin(
|
|
subHand,
|
|
LSLICENSE_SEARCH_KEYPACKID,
|
|
TRUE,
|
|
&lsl,
|
|
&dwErrCode);
|
|
|
|
if (dwErrCode != ERROR_SUCCESS)
|
|
{
|
|
TryGetLastError(subHand, &msg);
|
|
|
|
if (NULL != msg)
|
|
{
|
|
_fputts(msg, stderr);
|
|
|
|
MIDL_user_free(msg);
|
|
}
|
|
return dwErrCode;
|
|
}
|
|
else if (dwStatus)
|
|
{
|
|
return dwStatus;
|
|
}
|
|
do {
|
|
dwStatus = TLSLicenseEnumNext(subHand, &lsl, &dwErrCode);
|
|
if ((dwStatus == RPC_S_OK) && (dwErrCode == ERROR_SUCCESS)) {
|
|
if ((lsl.ucLicenseStatus == LSLICENSE_STATUS_TEMPORARY) ||
|
|
!bTempOnly) { // Does it fit the temp. requirements?
|
|
// We want to print if at any of the following are true:
|
|
// a) There are no limits
|
|
// b) Issued between stStart and stEnd
|
|
// c) Expired between stStart and stEnd
|
|
// d) issued before stStart and expired after stEnd
|
|
if (!fUseLimits // case a
|
|
|| ((CompDate(lsl.ftIssueDate, stStart) >= 0) &&
|
|
(CompDate(lsl.ftIssueDate, stEnd) <= 0)) // case b
|
|
|| ((CompDate(lsl.ftExpireDate, stStart) >= 0) &&
|
|
(CompDate(lsl.ftExpireDate, stEnd) <= 0)) // case c
|
|
|| ((CompDate(lsl.ftIssueDate, stStart) <= 0) &&
|
|
(CompDate(lsl.ftExpireDate, stEnd) >= 0))) // case d
|
|
{
|
|
PrintLicense(szName, // print it.
|
|
&lsl,
|
|
szProductDesc,
|
|
OutFile,
|
|
fHwid);
|
|
} // end check cases
|
|
} // end check for temp license
|
|
} // end good getnext
|
|
} while ((dwStatus == RPC_S_OK) && (dwErrCode == ERROR_SUCCESS));
|
|
|
|
if (dwStatus != RPC_S_OK)
|
|
{
|
|
return ShowError(dwStatus, NULL, TRUE);
|
|
}
|
|
|
|
if (dwErrCode != LSERVER_I_NO_MORE_DATA)
|
|
{
|
|
TryGetLastError(subHand, &msg);
|
|
if (NULL != msg)
|
|
{
|
|
_fputts(msg, stderr);
|
|
|
|
MIDL_user_free(msg);
|
|
|
|
msg = NULL;
|
|
}
|
|
}
|
|
|
|
TLSLicenseEnumEnd(subHand, &dwErrCode);
|
|
|
|
if (dwErrCode != ERROR_SUCCESS)
|
|
{
|
|
TryGetLastError(subHand, &msg);
|
|
if (NULL != msg)
|
|
{
|
|
_fputts(msg, stderr);
|
|
|
|
MIDL_user_free(msg);
|
|
}
|
|
}
|
|
|
|
TLSDisconnectFromServer(subHand);
|
|
}
|
|
return dwErrCode;
|
|
}
|
|
|
|
// Given a machine to connect to, iterate through the keypacks.
|
|
// Is not called directly.
|
|
DWORD
|
|
KeyPackLoop(
|
|
IN FILE *OutFile,
|
|
IN LPWSTR szName, // machine to connect to
|
|
IN BOOL bTempOnly,
|
|
IN const PSYSTEMTIME stStart,
|
|
IN const PSYSTEMTIME stEnd,
|
|
IN BOOL fUseLimits,
|
|
IN BOOL fHwid) // do we care about the previous 2 parms?
|
|
{
|
|
TLS_HANDLE hand;
|
|
DWORD dwStatus, dwErrCode;
|
|
LSKeyPack lskpKeyPack;
|
|
TCHAR *msg = NULL;
|
|
|
|
hand = TLSConnectToLsServer(szName);
|
|
if (hand == NULL)
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
memset(&lskpKeyPack, 0, sizeof(lskpKeyPack));
|
|
lskpKeyPack.dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
|
|
dwStatus = TLSKeyPackEnumBegin(hand,
|
|
LSKEYPACK_SEARCH_ALL,
|
|
FALSE,
|
|
&lskpKeyPack,
|
|
&dwErrCode);
|
|
if (dwErrCode != ERROR_SUCCESS)
|
|
{
|
|
return dwErrCode;
|
|
}
|
|
if (dwStatus != RPC_S_OK)
|
|
{
|
|
return dwStatus;
|
|
}
|
|
do {
|
|
dwStatus = TLSKeyPackEnumNext(hand, &lskpKeyPack, &dwErrCode);
|
|
if ((dwStatus == RPC_S_OK) && (dwErrCode == ERROR_SUCCESS))
|
|
{
|
|
LicenseLoop(OutFile,
|
|
szName,
|
|
lskpKeyPack.dwKeyPackId,
|
|
lskpKeyPack.szProductDesc,
|
|
bTempOnly,
|
|
stStart,
|
|
stEnd,
|
|
fUseLimits,
|
|
fHwid);
|
|
}
|
|
} while ((dwStatus == RPC_S_OK) && (dwErrCode == ERROR_SUCCESS));
|
|
if (dwStatus != RPC_S_OK)
|
|
{
|
|
return ShowError(dwStatus, NULL, TRUE);
|
|
}
|
|
if (dwErrCode != LSERVER_I_NO_MORE_DATA)
|
|
{
|
|
TryGetLastError(hand, &msg);
|
|
if (NULL != msg)
|
|
{
|
|
_fputts(msg, stderr);
|
|
|
|
MIDL_user_free(msg);
|
|
|
|
msg = NULL;
|
|
}
|
|
}
|
|
TLSKeyPackEnumEnd(hand, &dwErrCode);
|
|
if (dwErrCode != ERROR_SUCCESS)
|
|
{
|
|
TryGetLastError(hand, &msg);
|
|
if (NULL != msg)
|
|
{
|
|
_fputts(msg, stderr);
|
|
|
|
MIDL_user_free(msg);
|
|
}
|
|
}
|
|
TLSDisconnectFromServer(hand);
|
|
return dwErrCode;
|
|
}
|
|
|
|
// If bTempOnly is FALSE, all licenses will be dumped to the file. Otherwise,
|
|
// only Temporary licenses will be written. This is the one function to call
|
|
// to do all of the program's magic.
|
|
DWORD
|
|
ExportLicenses(
|
|
IN FILE *OutFile, // must be opened for writing first
|
|
IN PServerHolder pshServers,
|
|
IN BOOL fTempOnly,
|
|
IN const PSYSTEMTIME stStart,
|
|
IN const PSYSTEMTIME stEnd,
|
|
IN BOOL fUseLimits,
|
|
IN BOOL fHwid) // are the above 2 parms valid?
|
|
{
|
|
DWORD i;
|
|
DWORD dwStatus;
|
|
DWORD dwRetVal = ERROR_SUCCESS;
|
|
|
|
_fputts(header, OutFile);
|
|
for (i = 0; i < pshServers->dwCount; i++) {
|
|
dwStatus = KeyPackLoop(OutFile,
|
|
pshServers->pszNames[i],
|
|
fTempOnly,
|
|
stStart,
|
|
stEnd,
|
|
fUseLimits,
|
|
fHwid);
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
INT_PTR arg;
|
|
|
|
dwRetVal = dwStatus;
|
|
arg = (INT_PTR)pshServers->pszNames[i];
|
|
ShowError(IDS_BAD_LOOP, &arg, FALSE);
|
|
ShowError(dwStatus, NULL, TRUE);
|
|
}
|
|
}
|
|
if (dwRetVal == ERROR_SUCCESS)
|
|
{
|
|
// Show a success banner.
|
|
|
|
ShowError(ERROR_SUCCESS, NULL, TRUE);
|
|
}
|
|
return dwRetVal;
|
|
}
|
|
|
|
// Performs actual output. of must be open.
|
|
// Not called directly.
|
|
VOID
|
|
PrintLicense(
|
|
IN LPCWSTR szName, // server allocating this license
|
|
IN const LPLSLicense p,
|
|
IN LPCTSTR szProductDesc,
|
|
IN FILE *of,
|
|
IN BOOL fHwid)
|
|
{
|
|
// All of these are used solely to convert a time_t to a short date.
|
|
BSTR bszDate;
|
|
UDATE uDate;
|
|
DATE Date;
|
|
HRESULT hr;
|
|
LPTSTR szType;
|
|
TCHAR tc;
|
|
|
|
|
|
|
|
// server name
|
|
_fputts(szName, of);
|
|
|
|
// license ID and keypack ID
|
|
_ftprintf(of, _T("\t%d\t%d\t"),
|
|
p->dwLicenseId,
|
|
p->dwKeyPackId);
|
|
|
|
// license holder (machine)
|
|
_fputts(p->szMachineName, of);
|
|
_fputtc('\t', of);
|
|
|
|
// license requestor (username)
|
|
_fputts(p->szUserName, of);
|
|
_fputtc('\t', of);
|
|
|
|
// Print issue date in locale-appropriate way
|
|
UnixTimeToSystemTime((const time_t)p->ftIssueDate, &uDate.st);
|
|
|
|
hr = VarDateFromUdate(&uDate, 0, &Date);
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
hr = VarBstrFromDate(Date, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), VAR_DATEVALUEONLY, &bszDate);
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_fputts(bszDate, of);
|
|
SysFreeString(bszDate);
|
|
_fputtc('\t', of);
|
|
|
|
// print either "No Expiration" or locale-nice expiration date
|
|
if (0x7FFFFFFF == p->ftExpireDate)
|
|
{
|
|
_fputts(noExpire, of);
|
|
}
|
|
else
|
|
{
|
|
UnixTimeToSystemTime((const time_t)p->ftExpireDate, &uDate.st);
|
|
|
|
hr = VarDateFromUdate(&uDate, 0, &Date);
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
hr = VarBstrFromDate(Date, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), VAR_DATEVALUEONLY, &bszDate);
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_fputts(bszDate, of);
|
|
SysFreeString(bszDate);
|
|
}
|
|
_fputtc('\t', of);
|
|
|
|
// Assign the right kind of text for the type of license,
|
|
// and then print the license type.
|
|
switch (p->ucLicenseStatus) {
|
|
case LSLICENSE_STATUS_TEMPORARY:
|
|
szType = szTemp;
|
|
break;
|
|
case LSLICENSE_STATUS_ACTIVE:
|
|
szType = szActive;
|
|
break;
|
|
case LSLICENSE_STATUS_UPGRADED:
|
|
szType = szUpgrade;
|
|
break;
|
|
case LSLICENSE_STATUS_REVOKE:
|
|
szType = szRevoked;
|
|
break;
|
|
case LSLICENSE_STATUS_PENDING:
|
|
szType = szPending;
|
|
break;
|
|
case LSLICENSE_STATUS_CONCURRENT:
|
|
szType = szConcur;
|
|
break;
|
|
case LSLICENSE_STATUS_UNKNOWN:
|
|
// Fall through
|
|
default:
|
|
szType = szUnknown;
|
|
}
|
|
_fputts(szType, of);
|
|
_fputtc('\t', of);
|
|
|
|
// Print the description
|
|
_fputts(szProductDesc, of);
|
|
_fputtc('\t', of);
|
|
|
|
|
|
//Doughu: Representation algorithm
|
|
//We have only 36 informational chars I0 through I35 due to szHWID being TCHAR[37]
|
|
//and we have to represent this in client HWID format, which is:
|
|
//0xI0I10000I2I3, 0xI4I5I6I7I8I9I10I11, 0xI12I13I14I15I16I17I18I19, 0xI20I21I22I23I24I25I26I27, 0xI28I29I30I31I32I33I34I35
|
|
|
|
if (fHwid)
|
|
{
|
|
for (int i=0; (((tc=p->szHWID[i])!=NULL)&&(i<36)); i++)
|
|
{
|
|
//this if statement is for prepending 0x
|
|
if (i==0)
|
|
{
|
|
_fputtc('0', of);_fputtc('x', of);
|
|
}
|
|
|
|
_fputtc(tc,of);
|
|
|
|
//this if statement is for 4 zeroes that need to be print since they were masked
|
|
if (i==1)
|
|
{
|
|
_fputtc('0', of); _fputtc('0', of); _fputtc('0', of); _fputtc('0', of);
|
|
}
|
|
|
|
//this if statement is for I3, I11, I19 and I26 values where we put comma followed by space and 0x
|
|
if((((i+5)%8)==0) &&( i!=35))
|
|
{
|
|
_fputtc(',', of); _fputtc(' ', of); _fputtc('0', of); _fputtc('x', of);
|
|
}
|
|
}
|
|
_fputtc('\n', of);
|
|
}
|
|
else
|
|
{
|
|
_fputtc('\n', of);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns <0 if when is before st, ==0 if they are the same date, and
|
|
// >0 if when is after st.
|
|
int CompDate(
|
|
IN DWORD when, // treated as a time_t
|
|
IN const PSYSTEMTIME st)
|
|
{
|
|
time_t when_t;
|
|
|
|
//
|
|
// time_t is 64 bits in win64. Convert, being careful to sign extend.
|
|
//
|
|
|
|
when_t = (time_t)((LONG)(when));
|
|
struct tm *t = localtime(&when_t);
|
|
|
|
if ((t->tm_year+1900) < st->wYear) {
|
|
return -1;
|
|
}
|
|
if ((t->tm_year+1900) > st->wYear) {
|
|
return 1;
|
|
}
|
|
if ((t->tm_mon+1) < st->wMonth) {
|
|
return -1;
|
|
}
|
|
if ((t->tm_mon+1) > st->wMonth) {
|
|
return 1;
|
|
}
|
|
if (t->tm_mday < st->wDay) {
|
|
return -1;
|
|
}
|
|
if (t->tm_mday > st->wDay) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
// From the Platform SDK.
|
|
void
|
|
UnixTimeToFileTime(
|
|
IN time_t t,
|
|
OUT LPFILETIME pft)
|
|
{
|
|
// Note that LONGLONG is a 64-bit value
|
|
LONGLONG ll;
|
|
|
|
ll = Int32x32To64(t, 10000000) + 116444736000000000;
|
|
pft->dwLowDateTime = (DWORD)ll;
|
|
pft->dwHighDateTime = (DWORD)(ll >> 32);
|
|
}
|
|
|
|
// Also from the Platform SDK.
|
|
void
|
|
UnixTimeToSystemTime(
|
|
IN time_t t,
|
|
OUT LPSYSTEMTIME pst)
|
|
{
|
|
FILETIME ft;
|
|
FILETIME ftloc;
|
|
|
|
|
|
UnixTimeToFileTime(t, &ft);
|
|
FileTimeToLocalFileTime(&ft, &ftloc);
|
|
FileTimeToSystemTime(&ftloc, pst);
|
|
}
|