1648 lines
33 KiB
C++
1648 lines
33 KiB
C++
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
|
|
Module Name:
|
|
|
|
misc
|
|
|
|
Abstract:
|
|
|
|
This module contains an interesting collection of routines that are
|
|
generally useful in the Calais context, but don't seem to fit anywhere else.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/14/1996
|
|
|
|
Environment:
|
|
|
|
Win32, C++ w/ Exceptions
|
|
|
|
Notes:
|
|
|
|
?Notes?
|
|
|
|
--*/
|
|
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
#include <Windows.h>
|
|
#include <SCardLib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <tchar.h>
|
|
|
|
|
|
/*++
|
|
|
|
MemCompare:
|
|
|
|
This routine compares memory sections.
|
|
|
|
Arguments:
|
|
|
|
pbOne supplies the address of the first block of memory
|
|
|
|
pbTwo supplies the address of the second block of memory
|
|
|
|
cbLength supplies the length of the two memory segments.
|
|
|
|
Return Value:
|
|
|
|
the difference between the first two differing bytes, or zero if they're the
|
|
identical.
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/26/1996
|
|
|
|
--*/
|
|
|
|
int
|
|
MemCompare(
|
|
IN LPCBYTE pbOne,
|
|
IN LPCBYTE pbTwo,
|
|
IN DWORD cbLength)
|
|
{
|
|
for (DWORD index = 0; index < cbLength; index += 1)
|
|
{
|
|
if (*pbOne++ != *pbTwo++)
|
|
return (int)*(--pbOne) - (int)*(--pbTwo);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
MStrAdd:
|
|
|
|
This method adds a string to the end of a multistring contained in a
|
|
CBuffer. The CBuffer may be empty, in which case its value becomes a
|
|
multistring with the single string element.
|
|
|
|
Arguments:
|
|
|
|
bfMsz supplies the multistring to be modified.
|
|
|
|
szAdd supplies the string to append.
|
|
|
|
Return Value:
|
|
|
|
the number of strings in the resulting multistring.
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 1/29/1997
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MStrAdd(
|
|
IN OUT CBuffer &bfMsz,
|
|
IN LPCSTR szAdd)
|
|
{
|
|
DWORD dwLen, dwAddLen;
|
|
CBuffer bfTmp;
|
|
|
|
dwLen = bfMsz.Length();
|
|
if (0 < dwLen)
|
|
{
|
|
ASSERT(2 * sizeof(TCHAR) <= dwLen);
|
|
ASSERT(0 == *(LPCTSTR)(bfMsz.Access(dwLen - sizeof(TCHAR))));
|
|
ASSERT(0 == *(LPCTSTR)(bfMsz.Access(dwLen - 2 * sizeof(TCHAR))));
|
|
dwLen -= sizeof(TCHAR);
|
|
}
|
|
|
|
dwAddLen = MoveString(bfTmp, szAdd);
|
|
bfMsz.Presize((dwLen + dwAddLen + 1) * sizeof(TCHAR), TRUE);
|
|
bfMsz.Resize(dwLen, TRUE); // Trim one trailing NULL, if any.
|
|
bfMsz.Append(bfTmp.Access(), dwAddLen * sizeof(TCHAR));
|
|
bfMsz.Append((LPBYTE)TEXT("\000"), sizeof(TCHAR));
|
|
return MStrLen(bfMsz);
|
|
}
|
|
|
|
DWORD
|
|
MStrAdd(
|
|
IN OUT CBuffer &bfMsz,
|
|
IN LPCWSTR szAdd)
|
|
{
|
|
DWORD dwLen, dwAddLen;
|
|
CBuffer bfTmp;
|
|
|
|
dwLen = bfMsz.Length();
|
|
if (0 < dwLen)
|
|
{
|
|
ASSERT(2 * sizeof(TCHAR) <= dwLen);
|
|
ASSERT(0 == *(LPCTSTR)(bfMsz.Access(dwLen - sizeof(TCHAR))));
|
|
ASSERT(0 == *(LPCTSTR)(bfMsz.Access(dwLen - 2 * sizeof(TCHAR))));
|
|
dwLen -= sizeof(TCHAR);
|
|
}
|
|
|
|
dwAddLen = MoveString(bfTmp, szAdd);
|
|
bfMsz.Presize((dwLen + dwAddLen + 2) * sizeof(TCHAR), TRUE);
|
|
bfMsz.Resize(dwLen, TRUE); // Trim one trailing NULL, if any.
|
|
bfMsz.Append(bfTmp.Access(), dwAddLen * sizeof(TCHAR));
|
|
bfMsz.Append((LPBYTE)TEXT("\000"), sizeof(TCHAR));
|
|
return MStrLen(bfMsz);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
MStrLen:
|
|
|
|
This routine determines the length of a Multi-string, in characters.
|
|
|
|
Arguments:
|
|
|
|
mszString supplies the string to compute the length of.
|
|
|
|
Return Value:
|
|
|
|
The length of the string, in characters, including trailing zeroes.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/14/1996
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MStrLen(
|
|
LPCSTR mszString)
|
|
{
|
|
DWORD dwLen, dwTotLen = 0;
|
|
|
|
for (;;)
|
|
{
|
|
dwLen = lstrlenA(&mszString[dwTotLen]);
|
|
dwTotLen += dwLen + 1;
|
|
if (0 == dwLen)
|
|
break;
|
|
}
|
|
if (2 > dwTotLen)
|
|
dwTotLen = 2; // Include the second trailing null character.
|
|
return dwTotLen;
|
|
}
|
|
|
|
DWORD
|
|
MStrLen(
|
|
LPCWSTR mszString)
|
|
{
|
|
DWORD dwLen, dwTotLen = 0;
|
|
|
|
for (;;)
|
|
{
|
|
dwLen = lstrlenW(&mszString[dwTotLen]);
|
|
dwTotLen += dwLen + 1;
|
|
if (0 == dwLen)
|
|
break;
|
|
}
|
|
if (2 > dwTotLen)
|
|
dwTotLen = 2; // Include the second trailing null character.
|
|
return dwTotLen;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
FirstString:
|
|
|
|
This routine returns a pointer to the first string in a multistring, or NULL
|
|
if there aren't any.
|
|
|
|
Arguments:
|
|
|
|
szMultiString - This supplies the address of the current position within a
|
|
Multi-string structure.
|
|
|
|
Return Value:
|
|
|
|
The address of the first null-terminated string in the structure, or NULL if
|
|
there are no strings.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/25/1996
|
|
|
|
--*/
|
|
|
|
LPCTSTR
|
|
FirstString(
|
|
IN LPCTSTR szMultiString)
|
|
{
|
|
LPCTSTR szFirst = NULL;
|
|
|
|
try
|
|
{
|
|
if (0 != *szMultiString)
|
|
szFirst = szMultiString;
|
|
}
|
|
catch (...) {}
|
|
|
|
return szFirst;
|
|
}
|
|
|
|
|
|
|
|
/*++
|
|
|
|
NextString:
|
|
|
|
In some cases, the Smartcard API returns multiple strings, separated by Null
|
|
characters, and terminated by two null characters in a row. This routine
|
|
simplifies access to such structures. Given the current string in a
|
|
multi-string structure, it returns the next string, or NULL if no other
|
|
strings follow the current string.
|
|
|
|
Arguments:
|
|
|
|
szMultiString - This supplies the address of the current position within a
|
|
Multi-string structure.
|
|
|
|
Return Value:
|
|
|
|
The address of the next Null-terminated string in the structure, or NULL if
|
|
no more strings follow.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 8/12/1996
|
|
|
|
--*/
|
|
|
|
LPCTSTR
|
|
NextString(
|
|
IN LPCTSTR szMultiString)
|
|
{
|
|
LPCTSTR szNext;
|
|
|
|
try
|
|
{
|
|
DWORD cchLen = lstrlen(szMultiString);
|
|
if (0 == cchLen)
|
|
szNext = NULL;
|
|
else
|
|
{
|
|
szNext = szMultiString + cchLen + 1;
|
|
if (0 == *szNext)
|
|
szNext = NULL;
|
|
}
|
|
}
|
|
|
|
catch (...)
|
|
{
|
|
szNext = NULL;
|
|
}
|
|
|
|
return szNext;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
StringIndex:
|
|
|
|
In some cases, the Smartcard API returns multiple strings, separated by Null
|
|
characters, and terminated by two null characters in a row. This routine
|
|
simplifies access to such structures. Given the starting address of a
|
|
multi-string structure, it returns the nth string in the structure, where n
|
|
is a zero-based index. If the supplied value for n exceeds the number of
|
|
strings in the structure, NULL is returned.
|
|
|
|
Arguments:
|
|
|
|
szMultiString - This supplies the address of the Multi-string structure.
|
|
|
|
dwIndex - This supplies the index value into the structure.
|
|
|
|
Return Value:
|
|
|
|
The address of the specified Null-terminated string in the structure, or
|
|
NULL if dwIndex indexes beyond the end of the structure.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 8/12/1996
|
|
|
|
--*/
|
|
|
|
LPCTSTR
|
|
StringIndex(
|
|
IN LPCTSTR szMultiString,
|
|
IN DWORD dwIndex)
|
|
{
|
|
LPCTSTR szCurrent = szMultiString;
|
|
|
|
try
|
|
{
|
|
DWORD index;
|
|
for (index = 0; (index < dwIndex) && (NULL != szCurrent); index += 1)
|
|
szCurrent = NextString(szCurrent);
|
|
}
|
|
|
|
catch (...)
|
|
{
|
|
szCurrent = NULL;
|
|
}
|
|
|
|
return szCurrent;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
MStringCount:
|
|
|
|
This routine returns the count of the number of strings in a multistring
|
|
|
|
Arguments:
|
|
|
|
mszInString supplies the input string to be sorted.
|
|
|
|
Return Value:
|
|
|
|
The count of strings
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Author:
|
|
|
|
Ross Garmoe (v-rossg) 12/05/1996
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MStringCount(
|
|
LPCTSTR mszInString)
|
|
{
|
|
LPCTSTR szCurrent;
|
|
DWORD cStr = 0;
|
|
|
|
//
|
|
// Count the strings
|
|
//
|
|
|
|
for (szCurrent = FirstString(mszInString);
|
|
NULL != szCurrent;
|
|
szCurrent = NextString(szCurrent))
|
|
cStr++;
|
|
|
|
return (cStr);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
MStringSort:
|
|
|
|
This routine rearranges a multistring so that the elements are sorted and
|
|
duplicates are eliminated.
|
|
|
|
Arguments:
|
|
|
|
mszInString supplies the input string to be sorted.
|
|
|
|
bfOutString receives the sorted string.
|
|
|
|
Return Value:
|
|
|
|
Count of strings in the multistring
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/25/1996
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MStringSort(
|
|
LPCTSTR mszInString,
|
|
CBuffer &bfOutString)
|
|
{
|
|
LPCTSTR szCurrent;
|
|
LPCTSTR szTmp;
|
|
CDynamicArray<const TCHAR> rgszElements;
|
|
DWORD ix, jx, kx, nMax;
|
|
int nDiff;
|
|
|
|
|
|
//
|
|
// Set up for the sort.
|
|
//
|
|
|
|
for (szCurrent = FirstString(mszInString);
|
|
NULL != szCurrent;
|
|
szCurrent = NextString(szCurrent))
|
|
rgszElements.Add(szCurrent);
|
|
|
|
|
|
//
|
|
// Do a simple bubble sort, eliminating duplicates. (We don't use qsort
|
|
// here, to ensure that the Run-time library doesn't get pulled in.)
|
|
//
|
|
|
|
nMax = rgszElements.Count();
|
|
if (0 == nMax)
|
|
{
|
|
bfOutString.Set((LPCBYTE)TEXT("\000"), 2 * sizeof(TCHAR));
|
|
return (nMax); // No elements implies nothing to do.
|
|
}
|
|
for (ix = 0; ix < nMax; ix += 1)
|
|
{
|
|
for (jx = nMax - 1; ix < jx; jx -= 1)
|
|
{
|
|
nDiff = lstrcmpi(rgszElements[jx - 1], rgszElements[jx]);
|
|
if (0 < nDiff)
|
|
{
|
|
szTmp = rgszElements.Get(jx - 1);
|
|
rgszElements.Set(jx - 1, rgszElements.Get(jx));
|
|
rgszElements.Set(jx, szTmp);
|
|
}
|
|
else if (0 == nDiff)
|
|
{
|
|
for (kx = jx; kx < nMax - 1; kx += 1)
|
|
rgszElements.Set(kx, rgszElements.Get(kx + 1));
|
|
rgszElements.Set(nMax -1, NULL);
|
|
nMax -= 1;
|
|
}
|
|
// else 0 > nDiff, which is what we want.
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Write the sorted strings to the output buffer.
|
|
//
|
|
|
|
jx = 0;
|
|
for (ix = 0; ix < nMax; ix += 1)
|
|
jx += lstrlen(rgszElements[ix]) + 1;
|
|
bfOutString.Presize((jx + 2) * sizeof(TCHAR));
|
|
bfOutString.Reset();
|
|
|
|
for (ix = 0; ix < nMax; ix += 1)
|
|
{
|
|
szTmp = rgszElements[ix];
|
|
bfOutString.Append(
|
|
(LPCBYTE)szTmp,
|
|
(lstrlen(szTmp) + 1) * sizeof(TCHAR));
|
|
}
|
|
bfOutString.Append((LPCBYTE)TEXT("\000"), sizeof(TCHAR));
|
|
return (nMax);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
MStringMerge:
|
|
|
|
This routine merges two Multistrings into a single multistring without
|
|
duplicate entries.
|
|
|
|
Arguments:
|
|
|
|
mszOne supplies the first multistring.
|
|
|
|
mszTwo supplies the secong multistring.
|
|
|
|
bfOutString receives the combined strings.
|
|
|
|
Return Value:
|
|
|
|
Count of strings in the multistring
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/25/1996
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MStringMerge(
|
|
LPCTSTR mszOne,
|
|
LPCTSTR mszTwo,
|
|
CBuffer &bfOutString)
|
|
{
|
|
DWORD dwLenOne = (MStrLen(mszOne) - 1) * sizeof(TCHAR);
|
|
DWORD dwLenTwo = MStrLen(mszTwo) * sizeof(TCHAR);
|
|
CBuffer bfTmp;
|
|
|
|
bfTmp.Presize((dwLenOne + dwLenTwo) * sizeof(TCHAR));
|
|
bfTmp.Set((LPCBYTE)mszOne, dwLenOne);
|
|
bfTmp.Append((LPCBYTE)mszTwo, dwLenTwo);
|
|
|
|
return MStringSort((LPCTSTR)bfTmp.Access(), bfOutString);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
MStringCommon:
|
|
|
|
This routine finds strings which are common to both supplied multistrings,
|
|
and returns the list of commonalities.
|
|
|
|
Arguments:
|
|
|
|
mszOne supplies the first multistring.
|
|
|
|
mszTwo supplies the secong multistring.
|
|
|
|
bfOutString receives the intersection of the strings.
|
|
|
|
Return Value:
|
|
|
|
Count of strings in the multistring
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/25/1996
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MStringCommon(
|
|
LPCTSTR mszOne,
|
|
LPCTSTR mszTwo,
|
|
CBuffer &bfOutString)
|
|
{
|
|
CBuffer bfOne, bfTwo;
|
|
LPCTSTR szOne, szTwo;
|
|
DWORD dwStrings = 0;
|
|
int nDiff;
|
|
|
|
bfOutString.Reset();
|
|
MStringSort(mszOne, bfOne);
|
|
MStringSort(mszTwo, bfTwo);
|
|
szOne = FirstString(bfOne);
|
|
szTwo = FirstString(bfTwo);
|
|
|
|
while ((NULL != szOne) && (NULL != szTwo))
|
|
{
|
|
nDiff = lstrcmpi(szOne, szTwo);
|
|
if (0 > nDiff)
|
|
szOne = NextString(szOne);
|
|
else if (0 < nDiff)
|
|
szTwo = NextString(szTwo);
|
|
else // a match!
|
|
{
|
|
bfOutString.Append(
|
|
(LPCBYTE)szOne,
|
|
(lstrlen(szOne) + 1) * sizeof(TCHAR));
|
|
szOne = NextString(szOne);
|
|
szTwo = NextString(szTwo);
|
|
dwStrings += 1;
|
|
}
|
|
}
|
|
if (0 == dwStrings)
|
|
bfOutString.Append((LPCBYTE)TEXT("\000"), 2 * sizeof(TCHAR));
|
|
else
|
|
bfOutString.Append((LPCBYTE)TEXT("\000"), sizeof(TCHAR));
|
|
return dwStrings;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
MStringRemove:
|
|
|
|
This routine scans the first supplied multistring, removing any entries that
|
|
exist in the second string.
|
|
|
|
Arguments:
|
|
|
|
mszOne supplies the first multistring.
|
|
|
|
mszTwo supplies the secong multistring.
|
|
|
|
bfOutString receives the value of the first string without the second
|
|
string.
|
|
|
|
Return Value:
|
|
|
|
Number of strings in output buffer
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/25/1996
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MStringRemove(
|
|
LPCTSTR mszOne,
|
|
LPCTSTR mszTwo,
|
|
CBuffer &bfOutString)
|
|
{
|
|
CBuffer bfOne, bfTwo;
|
|
LPCTSTR szOne, szTwo;
|
|
int nDiff;
|
|
DWORD cStr = 0;
|
|
|
|
bfOutString.Reset();
|
|
MStringSort(mszOne, bfOne);
|
|
MStringSort(mszTwo, bfTwo);
|
|
szOne = FirstString(bfOne);
|
|
szTwo = FirstString(bfTwo);
|
|
|
|
while ((NULL != szOne) && (NULL != szTwo))
|
|
{
|
|
nDiff = lstrcmpi(szOne, szTwo);
|
|
if (0 > nDiff)
|
|
{
|
|
bfOutString.Append(
|
|
(LPCBYTE)szOne,
|
|
(lstrlen(szOne) + 1) * sizeof(TCHAR));
|
|
szOne = NextString(szOne);
|
|
cStr++;
|
|
}
|
|
else if (0 < nDiff)
|
|
{
|
|
szTwo = NextString(szTwo);
|
|
}
|
|
else // a match!
|
|
{
|
|
szOne = NextString(szOne);
|
|
szTwo = NextString(szTwo);
|
|
}
|
|
}
|
|
while (NULL != szOne)
|
|
{
|
|
bfOutString.Append(
|
|
(LPCBYTE)szOne,
|
|
(lstrlen(szOne) + 1) * sizeof(TCHAR));
|
|
szOne = NextString(szOne);
|
|
cStr++;
|
|
}
|
|
bfOutString.Append(
|
|
(LPCBYTE)TEXT("\000"),
|
|
(DWORD)(0 == cStr ? 2 * sizeof(TCHAR) :sizeof(TCHAR)));
|
|
return cStr;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
ParseAtr:
|
|
|
|
This routine parses an ATR string.
|
|
|
|
Arguments:
|
|
|
|
pbAtr supplies the ATR string.
|
|
|
|
pdwAtrLen receives the length of the ATR string. This is an optional
|
|
parameter, and may be NULL.
|
|
|
|
pdwHistOffset receives the offset into the ATR string at which the history
|
|
string starts; i.e., the history string is at pbAtr[*pdwOffset].
|
|
|
|
pcbHisory receives the length of the history string, in bytes.
|
|
|
|
cbMaxLen supplies the maximum length of this ATR string. Typically this is
|
|
33, but you can restrict it to less by setting this parameter.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Valid ATR
|
|
FALSE - Invalid ATR
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/14/1996
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
ParseAtr(
|
|
LPCBYTE pbAtr,
|
|
LPDWORD pdwAtrLen,
|
|
LPDWORD pdwHistOffset,
|
|
LPDWORD pcbHistory,
|
|
DWORD cbMaxLen)
|
|
{
|
|
static const BYTE rgbYMap[] = {
|
|
0, // 0000
|
|
1, // 0001
|
|
1, // 0010
|
|
2, // 0011
|
|
1, // 0100
|
|
2, // 0101
|
|
2, // 0110
|
|
3, // 0111
|
|
1, // 1000
|
|
2, // 1001
|
|
2, // 1010
|
|
3, // 1011
|
|
2, // 1100
|
|
3, // 1101
|
|
3, // 1110
|
|
4 }; // 1111
|
|
DWORD dwHistLen, dwHistOffset, dwTDLen, dwIndex, dwAtrLen;
|
|
BOOL fTck = FALSE;
|
|
|
|
|
|
ASSERT(33 >= cbMaxLen);
|
|
try
|
|
{
|
|
|
|
|
|
//
|
|
// Get the ATR string, if any.
|
|
//
|
|
|
|
if ((0x3b != pbAtr[0]) && (0x3f != pbAtr[0]))
|
|
throw (DWORD)ERROR_NOT_SUPPORTED;
|
|
dwHistLen = pbAtr[1] & 0x0f;
|
|
dwIndex = 1;
|
|
dwTDLen = 0;
|
|
for (;;)
|
|
{
|
|
dwIndex += dwTDLen;
|
|
dwTDLen = rgbYMap[(pbAtr[dwIndex] >> 4) & 0x0f];
|
|
if (cbMaxLen < dwIndex + dwTDLen + dwHistLen)
|
|
throw (DWORD)ERROR_INVALID_DATA;
|
|
if (0 == dwTDLen)
|
|
break;
|
|
if (0 != (pbAtr[dwIndex] & 0x80))
|
|
{
|
|
if (0 != (pbAtr[dwIndex + dwTDLen] & 0x0f))
|
|
fTck = TRUE;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
dwIndex += dwTDLen + 1;
|
|
dwHistOffset = dwIndex;
|
|
dwAtrLen = dwIndex + dwHistLen + (fTck ? 1 : 0);
|
|
if (cbMaxLen < dwAtrLen)
|
|
throw (DWORD)ERROR_INVALID_DATA;
|
|
if (fTck)
|
|
{
|
|
BYTE bXor = 0;
|
|
for (dwIndex = 1; dwIndex < dwAtrLen; dwIndex += 1)
|
|
bXor ^= pbAtr[dwIndex];
|
|
if (0 != bXor)
|
|
throw (DWORD)ERROR_INVALID_DATA;
|
|
}
|
|
}
|
|
|
|
catch (...)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Let the caller in on what we know.
|
|
//
|
|
|
|
if (NULL != pdwAtrLen)
|
|
*pdwAtrLen = dwAtrLen;
|
|
if (NULL != pdwHistOffset)
|
|
*pdwHistOffset = dwHistOffset;
|
|
if (NULL != pcbHistory)
|
|
*pcbHistory = dwHistLen;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
AtrCompare:
|
|
|
|
This routine compares two ATRs for equality, given an optional ATR mask. If
|
|
the mask is supplied, ATR1 XORed against the mask must match ATR2.
|
|
|
|
Arguments:
|
|
|
|
pbAtr1 supplies the first ATR.
|
|
|
|
pbAtr2 supplies the second ATR,
|
|
|
|
pbMask supplies the ATR mask associated with the 2nd ATR. If this
|
|
parameter is NULL, no mask is used.
|
|
|
|
cbAtr2 supplies the length of ATR2 and it's mask. This value may be zero
|
|
if the length should be derived from ATR2.
|
|
|
|
Return Value:
|
|
|
|
TRUE - They are identical
|
|
FALSE - They differ.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/25/1996
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
AtrCompare(
|
|
LPCBYTE pbAtr1,
|
|
LPCBYTE pbAtr2,
|
|
LPCBYTE pbMask,
|
|
DWORD cbAtr2)
|
|
{
|
|
DWORD dwAtr1Len = 0;
|
|
DWORD dwAtr2Len = 0;
|
|
|
|
|
|
//
|
|
// Trivial checks.
|
|
//
|
|
|
|
if (!ParseAtr(pbAtr1, &dwAtr1Len))
|
|
return FALSE; // Invalid ATR.
|
|
if ((NULL == pbMask) || (0 == cbAtr2))
|
|
{
|
|
if (!ParseAtr(pbAtr2, &dwAtr2Len))
|
|
return FALSE; // Invalid ATR.
|
|
if ((0 != cbAtr2) && (dwAtr2Len != cbAtr2))
|
|
return FALSE; // Lengths don't match.
|
|
if (dwAtr1Len != dwAtr2Len)
|
|
return FALSE; // Different lengths.
|
|
}
|
|
else
|
|
{
|
|
dwAtr2Len = cbAtr2;
|
|
if (dwAtr1Len != dwAtr2Len)
|
|
return FALSE; // Different lengths.
|
|
}
|
|
|
|
|
|
//
|
|
// Apply the mask, if any.
|
|
//
|
|
|
|
if (NULL != pbMask)
|
|
{
|
|
for (DWORD index = 0; index < dwAtr2Len; index += 1)
|
|
{
|
|
if ((pbAtr1[index] & pbMask[index]) != pbAtr2[index])
|
|
return FALSE; // Byte mismatch.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (DWORD index = 0; index < dwAtr2Len; index += 1)
|
|
{
|
|
if (pbAtr1[index] != pbAtr2[index])
|
|
return FALSE; // Byte mismatch.
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we get here, they match.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/*++
|
|
|
|
MoveString:
|
|
|
|
This routine moves an ASCII or UNICODE string into a buffer, converting to
|
|
the character set in use.
|
|
|
|
Arguments:
|
|
|
|
bfDst receives the string, converted to TCHARs, and NULL terminated.
|
|
|
|
szSrc supplies the original string.
|
|
|
|
dwLength supplies the length of the string, with or without trailing
|
|
nulls, in characters. A -1 value implies the length should be
|
|
computed based on a trailing null.
|
|
|
|
Return Value:
|
|
|
|
The actual number of characters in the resultant string, including the
|
|
trailing null.
|
|
|
|
Throws:
|
|
|
|
Errors encountered, as DWORDS.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 2/12/1997
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MoveString(
|
|
CBuffer &bfDst,
|
|
LPCSTR szSrc,
|
|
DWORD dwLength)
|
|
{
|
|
if ((DWORD)(-1) == dwLength)
|
|
dwLength = lstrlenA(szSrc);
|
|
else
|
|
{
|
|
while ((0 < dwLength) && (0 == szSrc[dwLength - 1]))
|
|
dwLength -= 1;
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
DWORD dwResultLength;
|
|
|
|
dwResultLength =
|
|
MultiByteToWideChar(
|
|
GetACP(),
|
|
MB_PRECOMPOSED | MB_USEGLYPHCHARS,
|
|
szSrc,
|
|
dwLength,
|
|
NULL,
|
|
0);
|
|
if (0 == dwLength)
|
|
throw GetLastError();
|
|
bfDst.Presize((dwResultLength + 1) * sizeof(TCHAR));
|
|
dwResultLength =
|
|
MultiByteToWideChar(
|
|
GetACP(),
|
|
MB_PRECOMPOSED | MB_USEGLYPHCHARS,
|
|
szSrc,
|
|
dwLength,
|
|
(LPTSTR)bfDst.Access(),
|
|
bfDst.Space()/sizeof(TCHAR) - 1);
|
|
if (0 == dwLength)
|
|
throw GetLastError();
|
|
bfDst.Resize(dwResultLength * sizeof(TCHAR), TRUE);
|
|
dwLength = dwResultLength;
|
|
#else
|
|
bfDst.Presize((dwLength + 1) * sizeof(TCHAR));
|
|
bfDst.Set((LPCBYTE)szSrc, dwLength * sizeof(TCHAR));
|
|
#endif
|
|
bfDst.Append((LPCBYTE)(TEXT("\000")), sizeof(TCHAR));
|
|
dwLength += 1;
|
|
return dwLength;
|
|
}
|
|
|
|
DWORD
|
|
MoveString(
|
|
CBuffer &bfDst,
|
|
LPCWSTR szSrc,
|
|
DWORD dwLength)
|
|
{
|
|
if ((DWORD)(-1) == dwLength)
|
|
dwLength = lstrlenW(szSrc);
|
|
else
|
|
{
|
|
while ((0 < dwLength) && (0 == szSrc[dwLength - 1]))
|
|
dwLength -= 1;
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
DWORD dwResultLength =
|
|
WideCharToMultiByte(
|
|
GetACP(),
|
|
WC_COMPOSITECHECK,
|
|
szSrc,
|
|
dwLength,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
if (0 == dwResultLength)
|
|
throw GetLastError();
|
|
bfDst.Presize((dwResultLength + 1) * sizeof(TCHAR));
|
|
dwResultLength =
|
|
WideCharToMultiByte(
|
|
GetACP(),
|
|
WC_COMPOSITECHECK,
|
|
szSrc,
|
|
dwLength,
|
|
(LPSTR)bfDst.Access(),
|
|
bfDst.Space()/sizeof(TCHAR) - 1,
|
|
NULL,
|
|
NULL);
|
|
if (0 == dwResultLength)
|
|
throw GetLastError();
|
|
bfDst.Resize(dwResultLength * sizeof(TCHAR), TRUE);
|
|
dwLength = dwResultLength;
|
|
#else
|
|
bfDst.Presize((dwLength + 1) * sizeof(TCHAR));
|
|
bfDst.Set((LPCBYTE)szSrc, dwLength * sizeof(TCHAR));
|
|
#endif
|
|
bfDst.Append((LPCBYTE)(TEXT("\000")), sizeof(TCHAR));
|
|
dwLength += 1;
|
|
return dwLength;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
MoveToAnsiString:
|
|
|
|
This routine moves the internal string representation to an ANSI output
|
|
buffer.
|
|
|
|
Arguments:
|
|
|
|
szDst receives the output string. It must be sufficiently large enough to
|
|
handle the string. If this parameter is NULL, then the number of
|
|
characters required to hold the result is returned.
|
|
|
|
szSrc supplies the input string.
|
|
|
|
cchLength supplies the length of the input string, with or without trailing
|
|
nulls. A -1 value implies the length should be computed based on a
|
|
trailing null.
|
|
|
|
Return Value:
|
|
|
|
The length of the resultant string, in characters, including the trailing
|
|
null.
|
|
|
|
Throws:
|
|
|
|
Errors as DWORD status codes.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 2/14/1997
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MoveToAnsiString(
|
|
LPSTR szDst,
|
|
LPCTSTR szSrc,
|
|
DWORD cchLength)
|
|
{
|
|
if ((DWORD)(-1) == cchLength)
|
|
cchLength = lstrlen(szSrc);
|
|
else
|
|
{
|
|
while ((0 < cchLength) && (0 == szSrc[cchLength - 1]))
|
|
cchLength -= 1;
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
if (0 == *szSrc)
|
|
cchLength = 1;
|
|
else if (NULL == szDst)
|
|
{
|
|
cchLength =
|
|
WideCharToMultiByte(
|
|
GetACP(),
|
|
WC_COMPOSITECHECK,
|
|
szSrc,
|
|
cchLength,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
if (0 == cchLength)
|
|
throw GetLastError();
|
|
cchLength += 1;
|
|
}
|
|
else
|
|
{
|
|
cchLength =
|
|
WideCharToMultiByte(
|
|
GetACP(),
|
|
WC_COMPOSITECHECK,
|
|
szSrc,
|
|
cchLength,
|
|
szDst,
|
|
cchLength,
|
|
NULL,
|
|
NULL);
|
|
if (0 == cchLength)
|
|
throw GetLastError();
|
|
szDst[cchLength++] = 0;
|
|
}
|
|
#else
|
|
if (0 < cchLength)
|
|
{
|
|
cchLength += 1;
|
|
if (NULL != szDst)
|
|
CopyMemory(szDst, szSrc, cchLength * sizeof(TCHAR));
|
|
}
|
|
#endif
|
|
return cchLength;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
MoveToUnicodeString:
|
|
|
|
This routine moves the internal string representation to a UNICODE output
|
|
buffer.
|
|
|
|
Arguments:
|
|
|
|
szDst receives the output string. It must be sufficiently large enough to
|
|
handle the string. If this parameter is NULL, then the number of
|
|
characters required to hold the result is returned.
|
|
|
|
szSrc supplies the input string.
|
|
|
|
cchLength supplies the length of the input string, with or without trailing
|
|
nulls. A -1 value implies the length should be computed based on a
|
|
trailing null.
|
|
|
|
Return Value:
|
|
|
|
The length of the resultant string, in characters, including the trailing
|
|
null.
|
|
|
|
Throws:
|
|
|
|
Errors as DWORD status codes.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 2/14/1997
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MoveToUnicodeString(
|
|
LPWSTR szDst,
|
|
LPCTSTR szSrc,
|
|
DWORD cchLength)
|
|
{
|
|
if ((DWORD)(-1) == cchLength)
|
|
cchLength = lstrlen(szSrc);
|
|
else
|
|
{
|
|
while ((0 < cchLength) && (0 == szSrc[cchLength - 1]))
|
|
cchLength -= 1;
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
if (0 == *szSrc)
|
|
cchLength = 1;
|
|
else if (NULL == szDst)
|
|
{
|
|
cchLength =
|
|
MultiByteToWideChar(
|
|
GetACP(),
|
|
MB_PRECOMPOSED | MB_USEGLYPHCHARS,
|
|
szSrc,
|
|
cchLength,
|
|
NULL,
|
|
0);
|
|
if (0 == cchLength)
|
|
throw GetLastError();
|
|
cchLength += 1;
|
|
}
|
|
else
|
|
{
|
|
cchLength =
|
|
MultiByteToWideChar(
|
|
GetACP(),
|
|
MB_PRECOMPOSED | MB_USEGLYPHCHARS,
|
|
szSrc,
|
|
cchLength,
|
|
szDst,
|
|
cchLength);
|
|
if (0 == cchLength)
|
|
throw GetLastError();
|
|
szDst[cchLength++] = 0;
|
|
}
|
|
#else
|
|
cchLength += 1;
|
|
if (NULL != szDst)
|
|
CopyMemory(szDst, szSrc, cchLength * sizeof(TCHAR));
|
|
#endif
|
|
return cchLength;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
MoveToAnsiMultistring:
|
|
|
|
This routine moves the internal multistring representation to an ANSI output
|
|
buffer.
|
|
|
|
Arguments:
|
|
|
|
szDst receives the output string. It must be sufficiently large enough to
|
|
handle the multistring. If this parameter is NULL, then the number of
|
|
characters required to hold the result is returned.
|
|
|
|
szSrc supplies the input multistring.
|
|
|
|
cchLength supplies the length of the input string, in characters, with or
|
|
without trailing nulls. A -1 value implies the length should be
|
|
computed based on a double trailing null.
|
|
|
|
Return Value:
|
|
|
|
The length of the resultant string, in characters, including the trailing
|
|
nulls.
|
|
|
|
Throws:
|
|
|
|
Errors as DWORD status codes.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 2/17/1997
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MoveToAnsiMultiString(
|
|
LPSTR mszDst,
|
|
LPCTSTR mszSrc,
|
|
DWORD cchLength)
|
|
{
|
|
DWORD dwLen;
|
|
|
|
if ((DWORD)(-1) == cchLength)
|
|
cchLength = MStrLen(mszSrc);
|
|
dwLen = MoveToAnsiString(mszDst, mszSrc, cchLength);
|
|
if (0 == dwLen)
|
|
{
|
|
if (NULL != mszDst)
|
|
mszDst[0] = mszDst[1] = 0;
|
|
dwLen = 2;
|
|
}
|
|
else
|
|
{
|
|
if (NULL != mszDst)
|
|
mszDst[dwLen] = 0;
|
|
dwLen += 1;
|
|
}
|
|
return dwLen;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
MoveToUnicodeMultistring:
|
|
|
|
This routine moves the internal multistring representation to a
|
|
Unicode output buffer.
|
|
|
|
Arguments:
|
|
|
|
szDst receives the output string. It must be sufficiently large enough to
|
|
handle the multistring. If this parameter is NULL, then the number of
|
|
characters required to hold the result is returned.
|
|
|
|
szSrc supplies the input multistring.
|
|
|
|
cchLength supplies the length of the input string, in characters, with or
|
|
without trailing nulls. A -1 value implies the length should be
|
|
computed based on a double trailing null.
|
|
|
|
Return Value:
|
|
|
|
The length of the resultant string, in characters, including the trailing
|
|
nulls.
|
|
|
|
Throws:
|
|
|
|
Errors as DWORD status codes.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 2/17/1997
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MoveToUnicodeMultiString(
|
|
LPWSTR mszDst,
|
|
LPCTSTR mszSrc,
|
|
DWORD cchLength)
|
|
{
|
|
DWORD dwLen;
|
|
|
|
if ((DWORD)(-1) == cchLength)
|
|
cchLength = MStrLen(mszSrc);
|
|
dwLen = MoveToUnicodeString(mszDst, mszSrc, cchLength);
|
|
if (NULL != mszDst)
|
|
mszDst[dwLen] = 0;
|
|
dwLen += 1;
|
|
return dwLen;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
ErrorString:
|
|
|
|
This routine does it's very best to translate a given error code into a
|
|
text message. Any trailing non-printable characters are striped from the
|
|
end of the text message, such as carriage returns and line feeds.
|
|
|
|
Arguments:
|
|
|
|
dwErrorCode supplies the error code to be translated.
|
|
|
|
Return Value:
|
|
|
|
The address of a freshly allocated text string. Use FreeErrorString to
|
|
dispose of it.
|
|
|
|
Throws:
|
|
|
|
Errors are thrown as DWORD status codes.
|
|
|
|
Remarks:
|
|
|
|
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 8/27/1998
|
|
|
|
--*/
|
|
|
|
LPCTSTR
|
|
ErrorString(
|
|
DWORD dwErrorCode)
|
|
{
|
|
LPTSTR szErrorString = NULL;
|
|
|
|
try
|
|
{
|
|
DWORD dwLen;
|
|
LPTSTR szLast;
|
|
|
|
dwLen = FormatMessage(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
| FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
dwErrorCode,
|
|
LANG_NEUTRAL,
|
|
(LPTSTR)&szErrorString,
|
|
0,
|
|
NULL);
|
|
if (0 == dwLen)
|
|
{
|
|
ASSERT(NULL == szErrorString);
|
|
dwLen = FormatMessage(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
| FORMAT_MESSAGE_FROM_HMODULE,
|
|
GetModuleHandle(NULL),
|
|
dwErrorCode,
|
|
LANG_NEUTRAL,
|
|
(LPTSTR)&szErrorString,
|
|
0,
|
|
NULL);
|
|
if (0 == dwLen)
|
|
{
|
|
ASSERT(NULL == szErrorString);
|
|
szErrorString = (LPTSTR)LocalAlloc(
|
|
LMEM_FIXED,
|
|
32 * sizeof(TCHAR));
|
|
if (NULL == szErrorString)
|
|
throw (DWORD)SCARD_E_NO_MEMORY;
|
|
_stprintf(szErrorString, TEXT("0x%08x"), dwErrorCode);
|
|
}
|
|
}
|
|
|
|
ASSERT(NULL != szErrorString);
|
|
for (szLast = szErrorString + lstrlen(szErrorString) - 1;
|
|
szLast > szErrorString;
|
|
szLast -= 1)
|
|
{
|
|
if (_istgraph(*szLast))
|
|
break;
|
|
*szLast = 0;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
FreeErrorString(szErrorString);
|
|
throw;
|
|
}
|
|
|
|
return szErrorString;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
FreeErrorString:
|
|
|
|
This routine frees the Error String allocated by the ErrorString service.
|
|
|
|
Arguments:
|
|
|
|
szErrorString supplies the error string to be deallocated.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Remarks:
|
|
|
|
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 8/27/1998
|
|
|
|
--*/
|
|
|
|
void
|
|
FreeErrorString(
|
|
LPCTSTR szErrorString)
|
|
{
|
|
if (NULL != szErrorString)
|
|
LocalFree((LPVOID)szErrorString);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
SelectString:
|
|
|
|
This routine compares a given string to a list of possible strings, and
|
|
returns the index of the string that matches. The comparison is done case
|
|
insensitive, and abbreviations are allowed, as long as they're unique.
|
|
|
|
Arguments:
|
|
|
|
szSource supplies the string to be compared against all other strings.
|
|
|
|
Following strings supply a list of strings against which the source string
|
|
can be compared. The last parameter must be NULL.
|
|
|
|
Return Value:
|
|
|
|
0 - No match, or ambiguous match.
|
|
1-n - The source string matches the indexed template string.
|
|
|
|
Throws:
|
|
|
|
None
|
|
|
|
Remarks:
|
|
|
|
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 8/27/1998
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
SelectString(
|
|
LPCTSTR szSource,
|
|
...)
|
|
{
|
|
va_list vaArgs;
|
|
DWORD cchSourceLen;
|
|
DWORD dwReturn = 0;
|
|
DWORD dwIndex = 1;
|
|
LPCTSTR szTpl;
|
|
|
|
|
|
va_start(vaArgs, szSource);
|
|
|
|
|
|
//
|
|
// Step through each input parameter until we find an exact match.
|
|
//
|
|
|
|
cchSourceLen = lstrlen(szSource);
|
|
if (0 == cchSourceLen)
|
|
return 0; // Empty strings don't match anything.
|
|
szTpl = va_arg(vaArgs, LPCTSTR);
|
|
while (NULL != szTpl)
|
|
{
|
|
if (0 == _tcsncicmp(szTpl, szSource, cchSourceLen))
|
|
{
|
|
if (0 != dwReturn)
|
|
{
|
|
dwReturn = 0;
|
|
break;
|
|
}
|
|
dwReturn = dwIndex;
|
|
}
|
|
szTpl = va_arg(vaArgs, LPCTSTR);
|
|
dwIndex += 1;
|
|
}
|
|
va_end(vaArgs);
|
|
return dwReturn;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
StringFromGuid:
|
|
|
|
This routine converts a GUID into its corresponding string representation.
|
|
It's here so that it's not necessary to link all of OleBase into WinSCard.
|
|
Otherwise, we'd just use StringFromCLSID.
|
|
|
|
Arguments:
|
|
|
|
pguidSource supplies the GUID to convert.
|
|
|
|
szGuid receives the GUID as a string. This string is assumed to be at
|
|
least 39 characters long.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Throws:
|
|
|
|
Errors are thrown as DWORD status codes.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 1/20/1998
|
|
|
|
--*/
|
|
|
|
void
|
|
StringFromGuid(
|
|
IN LPCGUID pguidResult,
|
|
OUT LPTSTR szGuid)
|
|
{
|
|
|
|
//
|
|
// The following placement assumes Little Endianness.
|
|
// {1D92589A-91E4-11d1-93AA-00C04FD91402}
|
|
// 0123456789012345678901234567890123456789
|
|
// 1 2 3
|
|
//
|
|
|
|
static const WORD wPlace[sizeof(GUID)]
|
|
= { 8, 6, 4, 2, 13, 11, 18, 16, 21, 23, 26, 28, 30, 32, 34, 36 };
|
|
static const WORD wPunct[]
|
|
= { 0, 9, 14, 19, 24, 37, 38 };
|
|
static const TCHAR chPunct[]
|
|
= { TEXT('{'), TEXT('-'), TEXT('-'), TEXT('-'), TEXT('-'), TEXT('}'), TEXT('\000') };
|
|
DWORD dwI, dwJ;
|
|
TCHAR ch;
|
|
LPTSTR pch;
|
|
LPBYTE pbGuid = (LPBYTE)pguidResult;
|
|
BYTE bVal;
|
|
|
|
for (dwI = 0; dwI < sizeof(GUID); dwI += 1)
|
|
{
|
|
bVal = pbGuid[dwI];
|
|
pch = &szGuid[wPlace[dwI]];
|
|
for (dwJ = 0; dwJ < 2; dwJ += 1)
|
|
{
|
|
ch = bVal & 0x0f;
|
|
ch += TEXT('0');
|
|
if (ch > TEXT('9'))
|
|
ch += TEXT('A') - (TEXT('9') + 1);
|
|
*pch-- = ch;
|
|
bVal >>= 4;
|
|
}
|
|
}
|
|
|
|
dwI = 0;
|
|
do
|
|
{
|
|
szGuid[wPunct[dwI]] = chPunct[dwI];
|
|
} while (0 != chPunct[dwI++]);
|
|
}
|
|
|