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

962 lines
32 KiB

/*
coguid.cpp - self contained GUID allocator module
Bob Atkinson ([email protected]) June 1993
Modified for temporary use by billm April 1994
This file contains all that is necessary to generate GUIDs with high
frequency and robustness without a network card on WIN32.
We allocate a pseudo-random node id based
on machine state.
There is only one public API in this file: HrCreateGuidNoNet().
The following are relevant reference documents:
Project 802: Local and Metropolitan Area Network Standard
Draft Standard P802.1A/D10 1 April 1990
Prepared by the IEEE 802.1
(Describes IEEE address allocation)
DEC / HP
Network Computing Architecture
Remote Procedure Call RunTime Extensions Specification
Version OSF TX1.0.11 Steven Miller July 23, 1992
(Chapter 10 describes UUID allocation)
A word about "GUID" vs "UUID" vs ... In fact, they're all the SAME THING.
Meaning that, once allocated, they're all interoperable / comparable / etc.
The standard describes a memory layout for a 16-byte structure (long, word,
word, array of bytes) which gets around byte order issues. It then goes on
to describe three different "variants" of allocation algorithm for these 16
byte structures; each variant is encoded by certain high order bits in the
"clockSeqHiAndReserved" byte.
Variant 0 is (I believe) the historical Apollo allocation algorithm.
Variant 1 is what is implemented here.
Variant 2 is created according to the "Microsoft GUID specification."
Careful: Despite the name here being HrCreateGuidNoNet() we are NOT allocating
according to Variant 2; we are using Variant 1. Variant 2 works by having
a range of the bits be a (MS allocated, for now) authority identifier, and
the remaining bits be whatever that authority wants. Variant 1, by
contrast, has a precise standard for how all the bits are allocated. But
as the resulting 16 bytes are in fact all mutually compatible, this
confusion in terminology is of no actual consequence.
Variant 1 is allocated as follows. First, Variant 1 allocates four bits
as a "version" field. Here we implement according to version 1; version 2
is defined for "UUIDs genereated for OSF DCE Security purposes, conformant
to this specification, but substuting a Unix id value for the timeLow
value." I know of no other legal versions that have been allocated.
The other fields of Variant 1 are as follows. The high 6 bytes are the
IEEE allocated node id on which the allocator is running. The low eight
bytes are the current "time": we are to take the current time as
avialable to the precision of milliseconds and multiply by 10,000, thus
giving a logical precision of 100 ns. Within these lower bits, we are to
sequentially increment a count as we allocate guids. Thus, the maximum rate
at which we can allocate is indeed 1 GUID / 100 ns. The remaining two bytes
are used for a "clock sequence". The intent of the clock sequence is to
provide some protection against the real clock going backwards in time.
We initially randomly allocate the clock sequence, and then increment it
each time we detect the clock going backwards (the last time used and the
current clock sequence are stored in the registry).
Presently (93.06.11) this implementation contains byte-order sensitivities,
particularly in the 64-bit arithmetic helper routines below. This
implementation is also not suitable for use on a premptive system.
This function is only called when UuidCreate() fails.
*/
#include "_apipch.h"
#ifndef STATIC
#ifdef DEBUG
#define STATIC
#else
#define STATIC static
#endif
#endif
#ifdef WIN32
#define INTERNAL STATIC HRESULT __stdcall
#define INTERNAL_(type) STATIC type __stdcall
//==============================================================
// Start of 64 bit arithmetic utility class
//==============================================================
INTERNAL_(BOOL)
FLessThanOrEqualFTs(FILETIME ft1, FILETIME ft2)
{
if (ft1.dwHighDateTime < ft2.dwHighDateTime)
return TRUE;
else if (ft1.dwHighDateTime == ft2.dwHighDateTime)
return ft1.dwLowDateTime <= ft2.dwLowDateTime;
else
return FALSE;
}
INTERNAL_(FILETIME)
FtAddUShort(FILETIME ft1, USHORT ush)
{
FILETIME ft;
ft.dwLowDateTime = ft1.dwLowDateTime + ush;
ft.dwHighDateTime = ft1.dwHighDateTime +
((ft.dwLowDateTime < ft1.dwLowDateTime ||
ft.dwLowDateTime < ush) ?
1L : 0L);
return ft;
}
//==============================================================
// End of 64 bit arithmetic utility
//==============================================================
#pragma pack(1)
struct _NODEID // machine identifier
{
union {
BYTE rgb[6];
WORD rgw[3];
};
};
#pragma pack()
typedef struct _NODEID NODEID;
typedef USHORT CLKSEQ;
typedef CLKSEQ FAR * PCLKSEQ;
#define clkseqNil ((CLKSEQ)-1)
struct _UDBK // data from which a block of UUIDs can be generated
{
DWORD timeLowNext; // lower bound of block of time values
DWORD timeLowLast; // upper bound of block of time values
DWORD timeHigh; // high dword of timeLowXXXX
CLKSEQ clkseq; // the clock sequence
NODEID nodeid;
};
typedef struct _UDBK UDBK;
INTERNAL_(BOOL) FLessThanOrEqualFTs(FILETIME ft1, FILETIME ft2);
INTERNAL_(FILETIME) FtAddUShort(FILETIME ft1, USHORT ush);
INTERNAL GetPseudoRandomNodeId(NODEID*);
INTERNAL_(void) GetCurrentTimeULong64(FILETIME *);
INTERNAL GetNextBlockOTime(PCLKSEQ pClockSeq, FILETIME *pftCur);
INTERNAL ReadRegistry (PCLKSEQ pClockSeqPrev, FILETIME *pftPrev);
INTERNAL InitRegistry (PCLKSEQ pClockSeqPrev, FILETIME *pftPrev);
INTERNAL WriteRegistry(CLKSEQ, CLKSEQ, const FILETIME);
INTERNAL_(LONG) CountFilesInDirectory(LPCSTR szDirPath);
INTERNAL_(void) FromHexString(LPCSTR sz, LPVOID rgb, USHORT cb);
INTERNAL_(void) ToHexString(LPVOID rgb, USHORT cb, LPSTR sz);
INTERNAL_(WORD) Cyc(WORD w);
INTERNAL_(void) ShiftNodeid(NODEID FAR* pnodeid);
#ifdef MAC
OSErr __pascal GetDirName(short vRefNum, long ulDirID, StringPtr name);
int MacCountFiles(StringPtr pathName, short vRefNum, long parID);
#endif
// We amortize the overhead cost of allocating UUIDs by returning them in
// time-grouped blocks to the actual CoCreateGUID routine. This two-level
// grouping is largely historical, having been derived from the original
// NT UuidCreate() routine, but has been retained here with the thought that
// the efficiencies gained will be needed in future premptive system (Windows 95).
static const ULONG cuuidBuffer = 1000; // how many uuids to get per registry hit.
static const ULONG cuuidReturnMax = 100; // how many max to return on each GetUDBK.
static const DWORD dwMax = 0xFFFFFFFF; // largest legal DWORD
//================================================================================
// Start of meat of implementation
//================================================================================
INTERNAL GetUDBK(UDBK *pudbk)
// Init the given UDBK so that a bunch of UUIDs can be generated therefrom. This
// routine hits the system registry and the disk, and so is somewhat slow. But we
// amortize the cost over the block of UUIDs that are returned.
{
HRESULT hr;
ULONG cuuidReturn;
ULONG cuuidLeftInBuffer;
FILETIME ftCur;
// These next block of variables in effect comprise the internal state of
// the UUID generator. Notice that this works only in a shared-data space
// DLL world, such as Win3.1. In non-shared environments, this will
// need to be done differently, perhaps by putting this all in a server process.
// In a premptively scheduled system, this function is all a critical section.
static DWORD timeLowFirst = 0;
static DWORD timeLowLast = 0;
static CLKSEQ clkseq;
static DWORD timeHigh;
static NODEID nodeid = { 0, 0, 0, 0, 0, 0 };
cuuidLeftInBuffer = timeLowLast - timeLowFirst;
if (cuuidLeftInBuffer == 0) {
// Our buffer of uuid space is empty, or this is the first time in to this routine.
// Get another block of time from which we can generate UUIDs.
hr = GetNextBlockOTime(&clkseq, &ftCur);
if (hr != NOERROR) return hr;
if (nodeid.rgw[0] == 0 && nodeid.rgw[1] == 0 && nodeid.rgw[2] == 0) {
hr = GetPseudoRandomNodeId(&nodeid);
if (hr != NOERROR) return hr;
}
timeHigh = ftCur.dwHighDateTime;
// Set the buffer bottom. Return few enough so that we don't wrap the low dw.
timeLowFirst = ftCur.dwLowDateTime;
timeLowLast = (timeLowFirst > (dwMax - cuuidBuffer)) ? dwMax : timeLowFirst + cuuidBuffer;
cuuidLeftInBuffer = timeLowLast - timeLowFirst;
}
cuuidReturn = (cuuidLeftInBuffer < cuuidReturnMax) ? cuuidLeftInBuffer : cuuidReturnMax;
// Set the output values and bump the usage count
pudbk->timeLowNext = timeLowFirst;
timeLowFirst += cuuidReturn;
pudbk->timeLowLast = timeLowFirst;
pudbk->timeHigh = timeHigh;
pudbk->clkseq = clkseq;
pudbk->nodeid = nodeid;
return NOERROR;
}
INTERNAL_(void) GetCurrentTimeUlong64(FILETIME *pft)
// Return the current time (# of 100 nanoseconds intervals since 1/1/1601).
// Make sure that we never return the same time twice by high-frequency calls
// to this function.
//
{
static FILETIME ftPrev = {0};
SYSTEMTIME syst;
GetSystemTime(&syst);
if (!SystemTimeToFileTime(&syst, pft))
{
TrapSz1("Error %08lX calling SystemTimeToFileTime", GetLastError());
pft->dwLowDateTime = 0;
pft->dwHighDateTime =0;
}
if (memcmp(pft, &ftPrev, sizeof(FILETIME)) == 0)
*pft = FtAddUShort(*pft, 1);
memcpy(&ftPrev, pft, sizeof(FILETIME));
}
INTERNAL GetNextBlockOTime(PCLKSEQ pclkseq, FILETIME *pft)
// Updates, and potentially create, the registry entries that maintain
// the block of time values for UUIDs that we've created. The algorithm
// is basically:
// If the registry entries don't exist, then create them. Use
// a random number for the clock sequence.
// If the entries do exist, the dig out of them the previous
// clock sequence and previous time allocated. If the previous time
// is greater than the current time then bump (and store) the
// clock sequence.
{
FILETIME ftRegistry;
HRESULT hr;
CLKSEQ clkseqPrev;
GetCurrentTimeUlong64(pft);
hr = ReadRegistry(pclkseq, &ftRegistry);
if (hr != NOERROR)
return InitRegistry(pclkseq, pft);
// If the clock's gone backwards, bump the clock seq. The clock
// seq is only 14 bits significant; don't use more.
clkseqPrev = *pclkseq;
if (FLessThanOrEqualFTs(*pft, ftRegistry)) {
clkseqPrev = clkseqNil;
*pclkseq += 1;
if (*pclkseq == 16384) // 2^14
*pclkseq = 0;
}
return WriteRegistry(*pclkseq, clkseqPrev, *pft);
}
// Use a private ini file for now. This will go away when CoCreateGuid
// is available on NT and Windows 95.
static const char szDataKey[] = "CoCreateGuid";
static const char szClkSeq[] = "PreviousClockSequence"; // same as UUIDGEN.EXE
static const char szTime[] = "PreviousTimeAllocated"; // same as UUIDGEN.EXE
static const char szNodeId[] = "NodeId";
static const char szBlank[] = ""; // used for default GetPrivateProfileString values
static const char szProfileFile[] = "mapiuid.ini";
#define CCHHEXBUFFERMAX 32
INTERNAL ReadRegistry(PCLKSEQ pclkseq, FILETIME *pft)
// Read the previous values of the clock sequence and the time from
// the registry, if they are there. If they are not, then return
// an error.
{
SCODE sc = S_OK;
LONG cch = 0;
char szHexBuffer[CCHHEXBUFFERMAX];
// use our private ini file
cch = CCHHEXBUFFERMAX;
cch = GetPrivateProfileString(szDataKey, szClkSeq, szBlank,
szHexBuffer, (int)cch, szProfileFile);
if (cch == 0 || cch >= CCHHEXBUFFERMAX) {
sc = MAPI_E_DISK_ERROR;
goto ErrRet;
}
FromHexString(szHexBuffer, pclkseq, sizeof(CLKSEQ));
cch = CCHHEXBUFFERMAX;
cch = GetPrivateProfileString(szDataKey, szTime, szBlank,
szHexBuffer, (int)cch, szProfileFile);
if (cch == 0 || cch >= CCHHEXBUFFERMAX) {
sc = MAPI_E_DISK_ERROR;
goto ErrRet;
}
FromHexString(szHexBuffer, pft, sizeof(FILETIME));
// Fall through to ErrRet
ErrRet:
return ResultFromScode(sc);
}
INTERNAL InitRegistry(PCLKSEQ pclkseq, FILETIME *pft)
// Invent a new clock sequence using a pseudo random number. Then
// write the clock sequence and the current time to the registry.
{
LONG cfile;
#ifdef MAC
short vRefNum;
long ulDirID;
Str32 stDirName;
FindFolder((short)kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder,
&vRefNum, &ulDirID);
GetDirName(vRefNum, ulDirID, stDirName);
cfile = MacCountFiles(stDirName, vRefNum, ulDirID);
#else
const int cchWindowsDir = 145; // 144 is recommended size according to SDK
LPSTR szWindowsDir = NULL;
if ( FAILED(MAPIAllocateBuffer(cchWindowsDir, (LPVOID *) &szWindowsDir))
|| GetWindowsDirectory(szWindowsDir, cchWindowsDir) == 0)
goto ErrRet;
// For the clock sequence, we use the number of files in the current
// windows directory, on the theory that that this is highly sensitive
// to the exact set of applications that have been installed on this
// particular machine.
cfile = CountFilesInDirectory(szWindowsDir);
if (cfile == -1)
goto ErrRet;
MAPIFreeBuffer(szWindowsDir);
szWindowsDir = NULL;
goto NormRet;
ErrRet:
MAPIFreeBuffer(szWindowsDir);
return ResultFromScode(MAPI_E_CALL_FAILED);
NormRet:
#endif // MAC
*pclkseq = (CLKSEQ)Cyc((WORD)cfile);
// Also use ms since boot so as to get a more time-varying value
*pclkseq ^= (CLKSEQ)Cyc((WORD)GetTickCount());
*pclkseq &= 16384-1; // only allow 14 bits of significance in clock seq
GetCurrentTimeUlong64(pft);
return WriteRegistry(*pclkseq, clkseqNil, *pft);
}
INTERNAL WriteRegistry(CLKSEQ clkseq, CLKSEQ clkseqPrev, const FILETIME ft)
// Write the clock sequence and time into the registry so that we can
// retrieve it later on a subsequent reboot. clkseqPrev is passed so that
// we can avoid writing the clock sequence if in fact we know it to be
// currently valid. This was measured as important for performance.
{
SCODE sc = S_OK;
char szHexBuffer[CCHHEXBUFFERMAX];
if (clkseq != clkseqPrev) { // don't write if clock sequence same (often is)
ToHexString((LPVOID)&clkseq, sizeof(CLKSEQ), szHexBuffer);
if (!WritePrivateProfileString(szDataKey, szClkSeq, szHexBuffer, szProfileFile)) {
sc = MAPI_E_DISK_ERROR;
goto ErrRet;
}
}
ToHexString((LPVOID)&ft, sizeof(FILETIME), szHexBuffer);
if (!WritePrivateProfileString(szDataKey, szTime, szHexBuffer, szProfileFile)) {
sc = MAPI_E_DISK_ERROR;
goto ErrRet;
}
ErrRet:
WritePrivateProfileString(NULL,NULL,NULL,szProfileFile); // flush the ini cache
return ResultFromScode(sc);
}
#ifdef MAC
#define GET_DIR_INFO -1
int MacCountFiles(StringPtr pathName, short vRefNum, long parID)
// Return the number of files in the specified Mac directory.
{
CInfoPBRec paramBlk;
paramBlk.hFileInfo.ioNamePtr = pathName; // Pascal string
paramBlk.hFileInfo.ioVRefNum = vRefNum;
// not necessary for full pathname
paramBlk.hFileInfo.ioDirID = paramBlk.dirInfo.ioDrParID = parID;
paramBlk.hFileInfo.ioFDirIndex = GET_DIR_INFO;
PBGetCatInfoSync(&paramBlk);
return(((DirInfo *) &paramBlk)->ioDrNmFls);
}
#endif
INTERNAL_(LONG) CountFilesInDirectory(LPCSTR szDirPath)
// Return the number of files in this directory. The path may or may not
// currently end with a slash.
{
int cfile = 0;
#ifndef MAC
LPCSTR szStar = "*.*";
char chLast = szDirPath[lstrlen(szDirPath)-1];
LPSTR szPath;
WIN32_FIND_DATA ffd;
HANDLE hFile;
if (FAILED(MAPIAllocateBuffer(lstrlen(szDirPath) +1 +lstrlen(szStar) +1,
(LPVOID *) &szPath)))
return -1;
lstrcpy(szPath, szDirPath);
/***
#ifdef DBCS
chLast = *(SzGPrev(szDirPath, szDirPath+lstrlen(szDirPath)));
#endif
***/
// Get the last character in above szDirPath
{
LPCSTR lp = szDirPath;
while(*lp)
{
chLast = *lp;
lp = CharNext(lp);
}
}
if (!(chLast == '\\' || chLast == '/'))
lstrcat(szPath, "\\");
lstrcat(szPath, szStar);
ffd.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
hFile = FindFirstFile(szPath, &ffd);
if (hFile != INVALID_HANDLE_VALUE)
{
cfile++;
while (FindNextFile(hFile, &ffd))
cfile++;
FindClose(hFile);
}
MAPIFreeBuffer(szPath);
#else
FSSpec pfss;
if (UnwrapFile(szDirPath, &pfss))
cfile = MacCountFiles(pfss.name, pfss.vRefNum, pfss.parID);
else
cfile = Random();
#endif
return cfile;
}
#pragma warning (disable:4616) // warning number out of range
#pragma warning (disable:4704) // in-line assembler precludes global optimizations
INTERNAL_(void) ShiftNodeid(NODEID FAR* pnodeid)
// Shift the nodeid so as to get a randomizing effect
{
// Rotate the whole NODEID left one bit. NODEIDs are 6 bytes long.
#if !defined(M_I8086) && !defined(M_I286) && !defined(M_I386) && !defined(_M_IX86)
BYTE bTmp;
BYTE bOld=0;
/* Compilers complain about conversions here. Pragma the warning off. */
#pragma warning(disable:4244) // possible data loss in conversion
bTmp = pnodeid->rgb[5];
pnodeid->rgb[5] = (pnodeid->rgb[5] << 1) + bOld;
bOld = (bTmp & 0x80);
bTmp = pnodeid->rgb[4];
pnodeid->rgb[4] = (pnodeid->rgb[4] << 1) + bOld;
bOld = (bTmp & 0x80);
bTmp = pnodeid->rgb[3];
pnodeid->rgb[3] = (pnodeid->rgb[3] << 1) + bOld;
bOld = (bTmp & 0x80);
bTmp = pnodeid->rgb[2];
pnodeid->rgb[2] = (pnodeid->rgb[2] << 1) + bOld;
bOld = (bTmp & 0x80);
bTmp = pnodeid->rgb[1];
pnodeid->rgb[1] = (pnodeid->rgb[1] << 1) + bOld;
bOld = (bTmp & 0x80);
bTmp = pnodeid->rgb[0];
pnodeid->rgb[0] = (pnodeid->rgb[0] << 1) + bOld;
bOld = (bTmp & 0x80);
pnodeid->rgb[5] = pnodeid->rgb[5] + bOld;
#pragma warning(default:4244)
#else
_asm {
mov ebx, pnodeid
sal WORD PTR [ebx], 1 // low order bit now zero
rcl WORD PTR [ebx+2], 1
rcl WORD PTR [ebx+4], 1
// Now put bit that fell off end into low order bit
adc WORD PTR [ebx],0 // add carry bit back in
}
#endif
}
INTERNAL_(WORD) Cyc(WORD w)
// Randomizing function used to distribute random state values uniformly
// in 0..65535 before use.
{
// // // Use one iteration of the library random number generator.
// // srand(w);
// // return rand();
//
// The following is what this would actually do, taken from the library
// source. It really isn't very good.
// return (WORD)( ((((long)w) * 214013L + 2531011L) >> 16) & 0x7fff );
// Really what we do: use the random number generator presented in
// CACM Oct 1988 Vol 31 Number 10 p 1195, slightly optimized since
// we are only interested in 16bit input/seed values.
const LONG a = 16807L;
const LONG m = 2147483647L; // 2^31 -1. Is prime.
const LONG q = 127773L; // m div a
const LONG r = 2386L; // m mod a
LONG seed = (LONG)w + 1L; // +1 so as to avoid problems with zero
// LONG hi = seed / q; // seed div q. Here always zero, since seed < q.
// LONG lo = seed % q; // seed mod q. Here always seed.
LONG test = a*seed; // a * lo - r * hi
if (test > 0)
seed = test;
else
seed = test + m;
// In a true random number generator, what we do now is scale the bits
// to return a floating point number in the range 0..1. However, we have
// no need here for that degree of quality of number sequence, and we
// wish to avoid the floating point calculations. Therefore we simply xor
// the words together.
// // p1193, top right column: seed is in the range 1..m-1, inclusive
// return (double)seed / m; // what the text recommends
// return (double)(seed-1) / (m-1); // variation: allows zero as legal value
return (WORD) (LOWORD(seed) ^ HIWORD(seed)); // use all the bits
}
INTERNAL GenerateNewNodeId(NODEID* pnodeid)
// Can't get from net. Generate one. We do this by using
// various statistics files in certain key directories on
// the machine.
{
// REVIEW: Consider not bothering to init the NODEID, thus getting
// random state from RAM?
#ifndef MAC
// Not including this should help make up for some of the (here)
// randomizing funcitons the MAC doesn't support.
memset(pnodeid, 0, sizeof(*pnodeid));
{ // BLOCK
// First, merge in random state generated from the file system
DWORD dwSectPerClust;
DWORD dwBytesPerSect;
DWORD dwFreeClust;
DWORD dwClusters;
(void) GetDiskFreeSpace(NULL, &dwSectPerClust, &dwBytesPerSect,
&dwFreeClust, &dwClusters);
pnodeid->rgw[0] ^= Cyc(LOWORD(dwBytesPerSect));
pnodeid->rgw[1] ^= Cyc(HIWORD(dwBytesPerSect));
pnodeid->rgw[2] ^= Cyc(HIWORD(dwClusters));
ShiftNodeid(pnodeid);
pnodeid->rgw[0] ^= Cyc(LOWORD(dwFreeClust));
pnodeid->rgw[1] ^= Cyc(HIWORD(dwFreeClust));
pnodeid->rgw[2] ^= Cyc(LOWORD(dwClusters));
} // BLOCK
#else
{ // BLOCK
ParamBlockRec paramBlk = {0};
paramBlk.volumeParam.ioVolIndex = 1;
PBGetVInfoSync(&paramBlk);
pnodeid->rgw[0] ^= Cyc(LOWORD(paramBlk.volumeParam.ioVAlBlkSiz));
pnodeid->rgw[1] ^= Cyc(HIWORD(paramBlk.volumeParam.ioVAlBlkSiz));
pnodeid->rgw[2] ^= Cyc(HIWORD(paramBlk.volumeParam.ioVNmAlBlks));
ShiftNodeid(pnodeid);
pnodeid->rgw[0] ^= Cyc(LOWORD(paramBlk.volumeParam.ioVFrBlk));
pnodeid->rgw[1] ^= Cyc(HIWORD(paramBlk.volumeParam.ioVFrBlk));
pnodeid->rgw[2] ^= Cyc(LOWORD(paramBlk.volumeParam.ioVNmAlBlks));
} // BLOCK
#endif
{ // BLOCK
// Next, mix in other stuff.
// As we generate and *store* the nodeid, using the time should not
// cause corellation problems with the fact that the time is also
// used as part of the fundamental uuid generation algorithm.
MEMORYSTATUS ms;
FILETIME ft;
DWORD dw;
POINT pt;
#ifndef MAC
LPVOID lpv;
#else
PSN psn;
DWORD dwFeature;
Gestalt(gestaltOSAttr, &dwFeature);
if (BitTst(&dwFeature, 31 - gestaltTempMemSupport))
{
// If temporary memory is available.
ms.dwAvailPhys = (DWORD) TempFreeMem();
ms.dwAvailVirtual = (DWORD) TempMaxMem(&ms.dwAvailVirtual);
ms.dwAvailPageFile = (DWORD) TempTopMem();
}
else
{
// If temporary memory is not available.
ms.dwAvailPhys = (DWORD) TickCount();
GetDateTime(&ms.dwAvailVirtual);
}
#endif
#ifndef MAC
ms.dwLength = sizeof(MEMORYSTATUS);
GlobalMemoryStatus(&ms);
#endif
ShiftNodeid(pnodeid);
GetCurrentTimeUlong64(&ft);
pnodeid->rgw[0] ^= Cyc(HIWORD(ft.dwHighDateTime)); // Use hi-order six bytes as time is *10000
pnodeid->rgw[1] ^= Cyc(LOWORD(ft.dwHighDateTime));
pnodeid->rgw[2] ^= Cyc(HIWORD(ft.dwLowDateTime));
ShiftNodeid(pnodeid);
pnodeid->rgw[0] ^= Cyc(LOWORD(ms.dwAvailPhys));
pnodeid->rgw[1] ^= Cyc(LOWORD(ms.dwAvailVirtual));
pnodeid->rgw[2] ^= Cyc(LOWORD(ms.dwAvailPageFile));
ShiftNodeid(pnodeid);
pnodeid->rgw[0] ^= Cyc(HIWORD(ms.dwAvailPhys));
pnodeid->rgw[1] ^= Cyc(HIWORD(ms.dwAvailVirtual));
pnodeid->rgw[2] ^= Cyc(HIWORD(ms.dwAvailPageFile));
ShiftNodeid(pnodeid);
dw = GetTickCount();
pnodeid->rgw[0] ^= Cyc(HIWORD(dw)); // Time (ms) since boot
pnodeid->rgw[1] ^= Cyc(LOWORD(dw));
#ifndef MAC
pnodeid->rgw[2] ^= Cyc(LOWORD(CountClipboardFormats()));// Number of items on the clipboard
#endif
GetCursorPos(&pt); // Cursor Position
ShiftNodeid(pnodeid);
pnodeid->rgw[0] ^= Cyc((WORD)(pt.x));
pnodeid->rgw[1] ^= Cyc((WORD)(pt.y));
#ifdef MAC
MacGetCurrentProcess(&psn);
pnodeid->rgw[2] ^= Cyc(LOWORD(psn.lowLongOfPSN));
#else
pnodeid->rgw[2] ^= Cyc(LOWORD((DWORD)GetCurrentThread())); // Current thread we're running in
#endif
ShiftNodeid(pnodeid);
#ifdef MAC
pnodeid->rgw[0] ^= Cyc(HIWORD(psn.lowLongOfPSN));
pnodeid->rgw[1] ^= Cyc(LOWORD(psn.highLongOfPSN));
#else
pnodeid->rgw[0] ^= Cyc(HIWORD(GetCurrentThread()));
pnodeid->rgw[1] ^= Cyc((WORD)GetOEMCP()); // sensitive to different countries
#endif
pnodeid->rgw[2] ^= Cyc((WORD)GetSystemMetrics(SM_SWAPBUTTON)); // different for lefties vs righties
ShiftNodeid(pnodeid);
#ifndef MAC
lpv = GetEnvironmentStrings();
pnodeid->rgw[0] ^= Cyc(HIWORD((DWORD)lpv));
pnodeid->rgw[1] ^= Cyc(LOWORD((DWORD)lpv));
#endif
pnodeid->rgw[2] ^= Cyc(HIWORD(GetCursor()));
ShiftNodeid(pnodeid);
#ifdef MAC
GetCursorPos(&pt);
#else
GetCaretPos(&pt);
#endif
pnodeid->rgw[0] ^= Cyc((WORD)(pt.x));
pnodeid->rgw[1] ^= Cyc((WORD)(pt.y));
pnodeid->rgw[2] ^= Cyc(LOWORD((DWORD)GetCursor()));
ShiftNodeid(pnodeid);
pnodeid->rgw[0] ^= Cyc((WORD)(DWORD)GetDesktopWindow());
pnodeid->rgw[1] ^= Cyc((WORD)(DWORD)GetActiveWindow());
#ifndef MAC
pnodeid->rgw[2] ^= Cyc((WORD)(DWORD)GetModuleHandle("OLE32"));
#endif
} // BLOCK
/* The following exerpts are taken from
Project 802: Local and Metropolitan Area Network Standard
Draft Standard P802.1A/D10 1 April 1990
Prepared by the IEEE 802.1
and is available in MS technical library. The key point about this is the
second LSB in the first byte of a real IEEE address is always zero.
Page 18:
"5. Universal Addresses and Protocol Identifiers
The IEEE makes it possible for organizations to employ unique individual
LAN MAC addresses, group addresses, and protocol identifiers. It does so by
assigning organizationally unique identifiers, which are 24 bits in length.
[...] Though the organizationally unique identifiers are 24 bits in length,
their true address space is 22 bits. The first bit can be set to 1 or 0
depending on the application. The second bit for all assignments is zero.
The remaining 22 bits [...] result in 2**22 (approximately
4 million identifiers.
[...] The multicast bit is the least significant bit of the first octet, A.
[...] 5.1 Organizationally Unique Identifier
[...] The organizationally unique identifier is 24 bits in length and its
bit pattern is shown below. Organizationally unique identifiers are
assigned as 24 bit values with both values (0,1) being assigned to the
first bit and the second bit being set to 0 indicates that the assignment
is universal. Organizationally unique identifiers with the second bit set
to 1 are locally assigned and have no relationship to the IEEE-assigned
values (as described herein).
The organizationally unique identifier is defined to be:
1st bit 24th bit
| |
a b c d e ....... x y
| |
| Always set to zero
Bit can be set to 0 or 1 depending on application [application here is
noting at all to do with what we, MS, call an application]
[...] 5.2 48-Bit Universal LAN Mac Addresses
[...] A 48 bit universal address consists of two parts. The first 24 bits
correspond to the organizationally unique identifier as assigned by the
IEEE except that the assignee may set the first bit to 1 for group
addresses or set it to 0 for individual addresses. The second part,
comprising the remaining 24 bits, is administered locally by the assignee.
[...]
octet:
0 1 2 3 4 5
0011 0101 0111 1011 0001 0010 0000 0000 0000 0000 0000 0001
|
First bit transmitted on the LAN medium. (Also the Individual/Group
Address Bit.) The hexadecimal representation is: AC-DE-48-00-00-80
The Individual/Group (I/G) Address Bit (1st bit of octet 0) is used to
identify the destination address either as an individual or as a group
address. If the Individual/Group Address Bit is 0, it indicates that
the address field contains an individual address. If this bit is 1, the
address field contains a group address that identifies one or more (or
all) stations connected to the LAN. The all-stations broadcast address
is a special, pre-defined group address of all 1's.
The Universally or Locally Administered Address Bit (2nd bit of octet 0)
is the bit directly following the I/G bit. This bit indicates whether
the address has been assigned by a local or universal administrator.
Universally administered addresses have this bit set to 0. If this bit
is set to 1, the entire address (i.e.: 48 bits) has been locally administered."
*/
pnodeid->rgb[0] |= 2; // Ensure that this is a locally administered address
pnodeid->rgb[0] &= ~1; // For future expandability: ensure one bit is
// always zero.
return NOERROR;
}
INTERNAL GetPseudoRandomNodeId(NODEID* pnodeid)
// Use the same nodeid we did last time if it's there; otherwise,
// make a new one.
{
HRESULT hr = NOERROR;
char szHexBuffer[CCHHEXBUFFERMAX];
LONG cch = CCHHEXBUFFERMAX;
// See if we already have a nodeid registered
cch = GetPrivateProfileString(szDataKey, szNodeId, szBlank,
szHexBuffer, (int)cch, szProfileFile);
if (cch != 0 && cch < CCHHEXBUFFERMAX) {
FromHexString(szHexBuffer, pnodeid, sizeof(*pnodeid));
} else {
// If we don't presently have a nodeid registered, make one, then register it
hr = GenerateNewNodeId(pnodeid);
if (hr != NOERROR) goto Exit;
ToHexString(pnodeid, sizeof(*pnodeid), szHexBuffer);
if (WritePrivateProfileString(szDataKey, szNodeId, szHexBuffer,szProfileFile)) {
WritePrivateProfileString(NULL,NULL,NULL,szProfileFile); // flush ini cache
} else {
hr = ResultFromScode(REGDB_E_WRITEREGDB);
goto Exit;
}
}
Exit:
return hr;
}
//========================================================================
INTERNAL_(unsigned char) ToUpper(unsigned char ch)
{
if (ch >= 'a' && ch <= 'z')
return (unsigned char)(ch - 'a' + 'A');
else
return ch;
}
INTERNAL_(BYTE) FromHex(unsigned char ch)
{
BYTE b = (BYTE) (ToUpper(ch) - '0');
if (b > 9)
b -= 'A' -'9' -1;
return b;
}
INTERNAL_(void) FromHexString(LPCSTR sz, LPVOID pv, USHORT cb)
// Set the value of this array of bytes from the given hex string
{
BYTE FAR *rgb = (BYTE FAR*)pv;
const char FAR *pch = sz;
memset(rgb, 0, cb);
if ((lstrlen(pch) & 1) != 0)
rgb[0] = FromHex(*pch++); // Odd length; do the leading nibble separately
while (*pch != '\0') {
BYTE b = FromHex(*pch++); // get next nibble
b = (BYTE)((b<<4) | FromHex(*pch++)); // and the next
MoveMemory(&rgb[1], &rgb[0], cb-1); // shift us over one byte
rgb[0] = b;
}
}
INTERNAL_(unsigned char) ToHex(BYTE b)
{
b &= 0x0f;
if (b > 9)
return (BYTE)(b -10 + 'A');
else
return (BYTE)(b -0 + '0');
}
INTERNAL_(void) ToHexString(LPVOID pv, USHORT cb, LPSTR sz)
// sz must be at least 2*cb +1 characters long
{
const BYTE FAR *rgb = (const BYTE FAR *)pv;
const int ibLast = cb-1;
int ib;
for (ib = ibLast; ib >= 0; ib--) {
sz[(ibLast-ib)*2] = ToHex((BYTE)(rgb[ib]>>4));
sz[(ibLast-ib)*2+1] = ToHex(rgb[ib]);
}
sz[(ibLast+1)*2] = '\0';
}
//========================================================================
// NOTE: As much as it might appear, this structure definition is NOT byte
// order sensitive. That is, the structure definition and the field
// manipulations that we use are in fact correct on both little and big
// endian machines.
#pragma pack(1)
struct _INTERNALUUID
{
ULONG timeLow;
USHORT timeMid;
USHORT timeHighAndVersion;
BYTE clkseqHighAndReserved;
BYTE clkseqLow;
BYTE nodeid[6];
};
#pragma pack()
typedef struct _INTERNALUUID INTERNALUUID;
enum {
// Constants used below for manipulating fields in the UUID
uuidReserved = 0x80, // we are a variant 1 UUID
uuidVersion = 0x1000, // version 1 (high nibble significant)
};
//=================================================================
STDAPI HrCreateGuidNoNet(GUID FAR *pguid)
// This is the only public function in this file.
// Return a newly allocated GUID.
{
static UDBK udbk; // We rely on static initialization to zero
HRESULT hr;
INTERNALUUID FAR* puuid = (INTERNALUUID FAR *)pguid;
if (udbk.timeLowNext == udbk.timeLowLast)
{
if ((hr = GetUDBK(&udbk)) != NOERROR)
return hr;
}
puuid->timeLow = udbk.timeLowNext++;
puuid->timeMid = (USHORT)(udbk.timeHigh & 0xffff);
puuid->timeHighAndVersion =
(USHORT) (((USHORT)((udbk.timeHigh >> 16) & 0x0fff))
| ((USHORT) uuidVersion));
puuid->clkseqHighAndReserved =
(BYTE)((BYTE) ((udbk.clkseq >> 8) & 0x3f)
| (BYTE) uuidReserved);
puuid->clkseqLow = (BYTE)(udbk.clkseq & 0xff);
memcpy(&puuid->nodeid[0], &udbk.nodeid.rgb[0], sizeof(NODEID));
return hrSuccess;
}
#endif /* WIN32 only */