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.
 
 
 
 
 
 

944 lines
22 KiB

//
// Util.cpp
//
#include "stdafx.h"
#include "Util.h"
#include "theapp.h"
#include <stdarg.h>
#include <shlobj.h>
LPTSTR lstrchr(LPCTSTR pszString, TCHAR ch)
{
while (*pszString != _T('\0'))
{
if (*pszString == ch)
return (LPTSTR)pszString;
pszString = CharNext(pszString);
}
return NULL;
}
LPTSTR lstrdup(LPCTSTR psz)
{
LPTSTR pszResult = (LPTSTR)malloc((lstrlen(psz)+1) * sizeof(TCHAR));
if (pszResult != NULL)
lstrcpy(pszResult, psz);
return pszResult;
}
void ReplaceString(LPTSTR& pszTarget, LPCTSTR pszSource)
{
free(pszTarget);
pszTarget = lstrdup(pszSource);
}
BOOL MyIsDigit(TCHAR ch)
{
return ((UINT)ch - (UINT)_T('0')) <= 9;
}
// A version of atoi that doesn't use the CRT
int MyAtoi(LPCTSTR psz)
{
int result = 0;
UINT digit;
TCHAR chSign = *psz;
if (*psz == _T('-') || *psz == _T('+'))
psz += 1;
while ((digit = (UINT)((int)*psz - (int)_T('0'))) <= 9)
{
result = (result * 10) + (int)digit;
psz += 1;
}
if (chSign == _T('-'))
result = -result;
return result;
}
// CountChars
//
// Returns the number of times the given character appears in the
// string.
//
// 2/03/1999 KenSh Created
//
int CountChars(LPCTSTR psz, TCHAR ch)
{
int count = 0;
while (*psz != _T('\0'))
{
if (*psz == ch)
count++;
psz = CharNext(psz);
}
return count;
}
// GetFirstToken
//
// Copies the characters up to but not including the separator char, and
// advances the source pointer to the character after the separator char.
// Returns TRUE if a token was found, FALSE if not.
//
BOOL GetFirstToken(LPCTSTR& pszList, TCHAR chSeparator, LPTSTR pszBuf, int cchBuf)
{
if (pszList == NULL || *pszList == '\0')
{
*pszBuf = '\0';
return FALSE;
}
LPTSTR pchComma = lstrchr(pszList, chSeparator);
int cchCopy;
int cchSkip;
if (pchComma == NULL)
{
cchCopy = lstrlen(pszList);
cchSkip = cchCopy;
}
else
{
cchCopy = (int)(pchComma - pszList);
cchSkip = cchCopy + 1;
}
cchCopy += 1;
if (cchCopy > cchBuf)
cchCopy = cchBuf;
lstrcpyn(pszBuf, pszList, cchCopy);
pszList += cchSkip;
return TRUE;
}
// Use this function for initializing multiple DLL procs
// pszFunction names is a series of null-separated proc names, followed by an extra null
BOOL LoadDllFunctions(LPCTSTR pszDll, LPCSTR pszFunctionNames, FARPROC* prgFunctions)
{
UINT uPrevMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
HINSTANCE hInst = LoadLibrary(pszDll);
SetErrorMode(uPrevMode);
if (hInst == NULL)
{
ASSERT(FALSE);
return FALSE;
}
while (*pszFunctionNames != '\0')
{
*prgFunctions = GetProcAddress(hInst, pszFunctionNames);
if (*prgFunctions == NULL)
{
ASSERT(FALSE);
return FALSE;
}
pszFunctionNames += (lstrlenA(pszFunctionNames) + 1);
prgFunctions += 1;
}
return TRUE;
}
int MakePath(LPTSTR pszBuf, LPCTSTR pszFolder, LPCTSTR pszFileTitle)
{
lstrcpy(pszBuf, pszFolder);
int cch = lstrlen(pszBuf);
if (pszBuf[cch-1] != _T('\\'))
pszBuf[cch++] = _T('\\');
lstrcpy(pszBuf + cch, pszFileTitle);
return lstrlen(pszBuf);
}
// pszLinkTarget - where the link will point
// pszDescription - link's description
// pszFolderPath - path to folder to create file in or fully qualified file path to create
// pszFileName - name of file to create in pszFolderPath or NULL to indicate pszFolderPath is already a file path
//
#ifndef NO_MAKELNKFILE
HRESULT MakeLnkFile(CLSID clsid, LPCTSTR pszLinkTarget, LPCTSTR pszDescription, LPCTSTR pszFolderPath, LPCTSTR pszFileName)
{
HRESULT hresCoInit = CoInitialize(NULL); // we will create a COM object
IUnknown *punk;
HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &punk));
if (SUCCEEDED(hr))
{
IShellLinkW * pslW;
hr = punk->QueryInterface(IID_PPV_ARG(IShellLinkW, &pslW));
if (SUCCEEDED(hr))
{
//WCHAR szBuffer[MAX_PATH];
//SHTCharToUnicode(pszLinkTarget, szBuffer, ARRAYSIZE(szBuffer));
pslW->SetPath(pszLinkTarget);
if (pszDescription)
{
//SHTCharToUnicode(pszDescription, szBuffer, ARRAYSIZE(szBuffer));
pslW->SetDescription(pszDescription);
}
pslW->Release();
}
else
{
IShellLinkA * pslA;
hr = punk->QueryInterface(IID_PPV_ARG(IShellLinkA, &pslA));
if (SUCCEEDED(hr))
{
char szBuffer[MAX_PATH];
SHTCharToAnsi(pszLinkTarget, szBuffer, ARRAYSIZE(szBuffer));
pslA->SetPath(szBuffer);
if (pszDescription)
{
SHTCharToAnsi(pszDescription, szBuffer, ARRAYSIZE(szBuffer));
pslA->SetDescription(szBuffer);
}
pslA->Release();
}
}
if (SUCCEEDED(hr))
{
IPersistFile *ppf;
hr = punk->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
if (SUCCEEDED(hr))
{
TCHAR szPath[MAX_PATH];
if (!pszFileName)
{
MakePath(szPath, pszFolderPath, pszFileName);
pszFolderPath = szPath;
}
//WCHAR szFolderPath[MAX_PATH];
//SHTCharToUnicode(pszFolderPath, szFolderPath, ARRAYSIZE(szFolderPath));
hr = ppf->Save(pszFolderPath, TRUE);
ppf->Release();
}
}
punk->Release();
}
if (SUCCEEDED(hresCoInit))
CoUninitialize();
return hr;
}
#endif
// FindPartialPath
//
// Returns a pointer to the file title preceded by nDepth levels of
// directory names (zero == file title only). If the path has less than
// nDepth levels, a pointer to the beginning of the string is returned.
// NULL is never returned.
//
// 10/18/1996 KenSh Created
//
LPTSTR FindPartialPath(LPCTSTR pszFullPath, int nDepth)
{
#define MAX_SLASHES (MAX_PATH / 2) // No more slashes than this in the path
LPTSTR pch;
LPTSTR rgpchSlashes[MAX_SLASHES];
int cSlashes = 0;
for (pch = (LPTSTR)pszFullPath; *pch; pch = CharNext(pch))
{
if (*pch == _T('\\') || *pch == _T('/'))
{
rgpchSlashes[cSlashes++] = pch;
}
}
if (cSlashes > nDepth)
{
return rgpchSlashes[cSlashes-nDepth-1] + 1;
}
else
{
// Not enough slashes - return the full path
return (LPTSTR)pszFullPath;
}
}
// FindFileTitle
//
// Given a full pathname or URL, returns a pointer to the file title. If
// the given does not contain path information, a pointer to the beginning
// of the string is returned. NULL is never returned.
//
// 4/19/1996 KenSh Created
//
LPTSTR FindFileTitle(LPCTSTR pszFullPath)
{
LPTSTR pch;
LPTSTR pchSlash = NULL;
for (pch = (LPTSTR)pszFullPath; *pch; pch = CharNext(pch))
{
if (*pch == _T('\\') || *pch == _T('/'))
pchSlash = pch;
}
if (pchSlash)
return pchSlash+1;
else
return (LPTSTR)pszFullPath;
}
// FindExtension
//
// Given a path, returns a pointer to its file extension (the character
// following the "."). If there is no extension, the return value is
// a pointer to the end of the string ('\0' character).
//
// 3/04/1996 KenSh Created
// 11/17/1997 KenSh Fixed case where path has "." but the filename doesn't
//
LPTSTR FindExtension(LPCTSTR pszFileName)
{
// Start with the file title
LPTSTR pch = FindFileTitle(pszFileName);
LPTSTR pchDot = NULL;
// Find the last "." in the filename
while (*pch)
{
if (*pch == _T('.'))
pchDot = pch;
pch = CharNext(pch);
}
if (pchDot)
return pchDot+1;
else
return pch; // empty string
}
// IsFullPath
//
// Returns nonzero if the given path is a fully qualified path starting
// with "X:\" or "\\"
//
// 5/19/1999 KenSh Created
//
BOOL IsFullPath(LPCTSTR pszPath)
{
if ((*pszPath == '\\' && *(pszPath+1) == '\\') ||
(*pszPath != '\0' && *(pszPath+1) == ':' && *(pszPath+2) == '\\'))
{
return TRUE;
}
else
{
return FALSE;
}
}
void ShowDlgItem(HWND hwndDlg, int nCtrlID, int nCmdShow)
{
ShowWindow(GetDlgItem(hwndDlg, nCtrlID), nCmdShow);
}
// GetDlgItemRect
//
// Retrieves the bounding rect of the dialog item relative to the top left
// corner of the dialog's client area.
//
// 10/13/1997 KenSh Created
//
HWND GetDlgItemRect(HWND hwndDlg, int nCtrlID, RECT* pRect)
{
ASSERT(IsWindow(hwndDlg));
ASSERT(pRect);
HWND hwndCtrl = GetDlgItem(hwndDlg, nCtrlID);
if (hwndCtrl != NULL)
{
POINT ptTopLeft;
ptTopLeft.x = ptTopLeft.y = 0;
ClientToScreen(hwndDlg, &ptTopLeft);
GetWindowRect(hwndCtrl, pRect);
OffsetRect(pRect, -ptTopLeft.x, -ptTopLeft.y);
}
return hwndCtrl;
}
// GetRelativeRect
//
// Retrieves the bounding rect of the window relative to the top left
// corner of its parent client area.
//
// 1/04/2000 KenSh Created
//
void GetRelativeRect(HWND hwndCtrl, RECT* pRect)
{
ASSERT(IsWindow(hwndCtrl));
ASSERT(pRect != NULL);
HWND hwndParent = GetParent(hwndCtrl);
POINT ptTopLeft = { 0, 0 };
ClientToScreen(hwndParent, &ptTopLeft);
GetWindowRect(hwndCtrl, pRect);
OffsetRect(pRect, -ptTopLeft.x, -ptTopLeft.y);
}
// SetDlgItemRect
//
// Updates the position and size of a dialog item to the given rectangle,
// in coordinates relative to the top left corner of the dialog's client area.
//
// 3/17/1999 KenSh Created
//
void SetDlgItemRect(HWND hwndDlg, int nCtrlID, CONST RECT* pRect)
{
ASSERT(IsWindow(hwndDlg));
ASSERT(GetDlgItem(hwndDlg, nCtrlID));
ASSERT(pRect);
SetWindowPos(GetDlgItem(hwndDlg, nCtrlID), NULL, pRect->left, pRect->top,
pRect->right - pRect->left, pRect->bottom - pRect->top,
SWP_NOZORDER | SWP_NOACTIVATE);
}
// FormatDlgItemText
//
// Works like wsprintf to change the text of an existing dialog control.
// If pszFormat is non-NULL, it contains the formatting string.
// If pszFormat is NULL, then the existing control text is used as the
// format string.
//
// 9/22/1999 KenSh Created
//
BOOL __cdecl FormatDlgItemText(HWND hwnd, int nCtrlID, LPCTSTR pszFormat, ...)
{
HWND hwndCtrl = GetDlgItem(hwnd, nCtrlID);
if (NULL == hwndCtrl)
return FALSE;
va_list argList;
va_start(argList, pszFormat);
FormatWindowTextV(hwndCtrl, pszFormat, argList);
return TRUE;
}
// FormatWindowTextV
//
// Combines the functionality of wvsprintf with SetWindowText, automatically
// allocating a buffer large enough to hold the expanded string, and freeing
// the buffer after setting the window text.
//
// 9/22/1999 KenSh Created
//
void FormatWindowTextV(HWND hwnd, LPCTSTR pszFormat, va_list argList)
{
LPTSTR pszWindowText = NULL;
if (pszFormat == NULL)
{
int cchWindowText = GetWindowTextLength(hwnd) + 1;
pszWindowText = (LPTSTR)malloc(cchWindowText * sizeof(TCHAR));
if (pszWindowText)
{
GetWindowText(hwnd, pszWindowText, cchWindowText);
pszFormat = pszWindowText;
}
}
if (pszFormat)
{
int cchNeeded = EstimateFormatLength(pszFormat, argList);
LPTSTR pszBuf = (LPTSTR)malloc(cchNeeded * sizeof(TCHAR));
if (pszBuf)
{
#ifdef UNICODE
wvnsprintf(pszBuf, cchNeeded, pszFormat, argList);
#else
wvsprintf(pszBuf, pszFormat, argList);
#endif
SetWindowText(hwnd, pszBuf);
free(pszBuf);
}
}
if (pszWindowText != NULL)
{
free(pszWindowText);
}
}
LPTSTR __cdecl LoadStringFormat(HINSTANCE hInstance, UINT nStringID, ...)
{
LPTSTR pszBuf = NULL;
LPTSTR pszFormat = LoadStringAlloc(hInstance, nStringID);
if (pszFormat)
{
va_list argList;
va_start(argList, nStringID);
int cchNeeded = EstimateFormatLength(pszFormat, argList);
LPTSTR pszBuf = (LPTSTR)malloc(cchNeeded * sizeof(TCHAR));
if (pszBuf)
{
#ifdef UNICODE
wvnsprintf(pszBuf, cchNeeded, pszFormat, argList);
#else
wvsprintf(pszBuf, pszFormat, argList);
#endif
}
free(pszFormat);
}
return pszBuf;
}
// EstimateFormatLength
//
// Estimates the number of characters needed to format the string,
// including the terminating NULL.
//
// 9/22/1999 KenSh Created
//
int EstimateFormatLength(LPCTSTR pszFormat, va_list argList)
{
ASSERT(pszFormat != NULL);
int cch = lstrlen(pszFormat) + 1;
for (LPCTSTR pch = pszFormat; *pch; pch = CharNext(pch))
{
if (*pch == _T('%'))
{
pch++;
if (*pch == _T('-')) // we don't care about left vs. right justification
pch++;
if (*pch == _T('#')) // prefix hex numbers with 0x
{
pch++;
cch += 2;
}
if (*pch == _T('0')) // pads with zeroes instead of spaces
pch++;
if (MyIsDigit(*pch))
{
cch += MyAtoi(pch); // this overshoots but that's ok
do
{
pch++;
} while (MyIsDigit(*pch));
}
switch (*pch)
{
case _T('s'):
cch += lstrlen(va_arg(argList, LPCTSTR)) - 2;
break;
case _T('c'):
case _T('C'):
va_arg(argList, TCHAR);
cch -= 1;
break;
case _T('d'):
va_arg(argList, int);
cch += INT_CCH_MAX - 2;
break;
case _T('h'):
pch++;
ASSERT(*pch == _T('d') || *pch == _T('u')); // other forms of 'h' not implemented!
cch += SHORT_CCH_MAX - 2;
break;
case _T('l'):
pch++;
if (*pch == _T('d') || *pch == _T('i'))
cch += LONG_CCH_MAX - 2;
else if (*pch == _T('x') || *pch == _T('X'))
cch += LONGX_CCH_MAX - 2;
else
ASSERT(FALSE); // other forms of 'l' not implemented!
break;
default:
ASSERT(FALSE); // other
break;
}
}
}
return cch;
}
// CenterWindow
//
// Centers the given window relative to its parent window. If the parent
// is NULL, the window is centered over the desktop excluding the taskbar.
//
// 9/24/1999 KenSh Created
//
void CenterWindow(HWND hwnd)
{
RECT rcWindow;
RECT rcDesktop;
GetWindowRect(hwnd, &rcWindow);
HWND hwndParent = GetParent(hwnd);
if (hwndParent == NULL)
{
SystemParametersInfo(SPI_GETWORKAREA, sizeof(RECT), &rcDesktop, FALSE);
}
else
{
GetWindowRect(hwndParent, &rcDesktop);
}
int cxWindow = rcWindow.right - rcWindow.left;
int cyWindow = rcWindow.bottom - rcWindow.top;
int x = (rcDesktop.left + rcDesktop.right - cxWindow) / 2;
int y = (rcDesktop.top + rcDesktop.bottom - cyWindow) / 2;
SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
// FindResourceString
//
// Returns a pointer to the given string resource in memory, or NULL
// if the string does not exist. Note that the string is in Unicode,
// and is not NULL-terminated.
//
// 3/17/1999 KenSh Created
//
LPCWSTR FindResourceString(HINSTANCE hInstance, UINT nStringID, int* pcchString, WORD wLangID)
{
ASSERT(pcchString != NULL);
*pcchString = 0;
HRSRC hRsrc = FindResourceEx(hInstance, RT_STRING, MAKEINTRESOURCE((nStringID/16)+1), wLangID);
if (hRsrc == NULL)
return NULL;
DWORD cbStringTable = SizeofResource(hInstance, hRsrc);
HGLOBAL hGlb = LoadResource(hInstance, hRsrc);
LPBYTE pbData = (LPBYTE)LockResource(hGlb);
LPBYTE pbEnd = pbData + cbStringTable;
// Skip strings preceding desired one
int iString = (int)nStringID % 16;
for (int i = 0; i < iString; i++)
{
int cch = (int)*((LPWORD)pbData);
pbData += sizeof(WORD) + (sizeof(WCHAR) * cch);
if (pbData >= pbEnd)
return NULL;
}
if (pbData + sizeof(WORD) >= pbEnd)
return NULL;
*pcchString = (int)*((LPWORD)pbData);
pbData += sizeof(WORD);
return (LPCWSTR)pbData;
}
// GetResourceStringLength
//
// Finds the given string in the string table, and returns its length
// in characters, not including room for the terminating NULL.
//
// History:
//
// 3/17/1999 KenSh Created
//
int GetResourceStringLength(HINSTANCE hInstance, UINT nStringID, WORD wLangID)
{
int cch;
FindResourceString(hInstance, nStringID, &cch, wLangID);
return cch;
}
// LoadStringHelper
//
// Helper function for LoadStringAllocEx.
//
// 2/23/1998 KenSh Created
// 9/27/1999 KenSh changed alloc method from new[] to malloc
// 12/21/1999 KenSh fixed unicode and DBCS bugs
//
int LoadStringHelper(HINSTANCE hInstance, UINT nID, LPTSTR* ppszBuf, int cchBuf, WORD wLangID)
{
int cch, cchCopy;
LPCWSTR pwszString = FindResourceString(hInstance, nID, &cch, wLangID);
if (pwszString == NULL)
return 0;
if (!(*ppszBuf))
{
#ifdef UNICODE
cchBuf = 1 + cch;
#else
cchBuf = 1 + WideCharToMultiByte(CP_ACP, 0, pwszString, cch, NULL, 0, NULL, NULL);
#endif
*ppszBuf = (LPTSTR)malloc(cchBuf * sizeof(TCHAR));
cchCopy = cch;
}
else
{
cchCopy = min(cchBuf-1, cch);
}
if (*ppszBuf)
{
#ifdef UNICODE
CopyMemory(*ppszBuf, pwszString, cchCopy * sizeof(WCHAR));
(*ppszBuf)[cchCopy] = _T('\0');
#else
cchCopy = WideCharToMultiByte(CP_ACP, 0, pwszString, cchCopy, *ppszBuf, cchBuf, NULL, NULL);
(*ppszBuf)[cchCopy] = _T('\0');
#endif
return cchCopy;
}
return 0;
}
// LoadStringAllocEx
//
// Finds the string resource with the given ID and language, allocates a
// buffer using malloc, and copies the string to the buffer. If the
// string is not found, NULL is returned.
//
// 2/24/1998 KenSh Created
//
LPTSTR LoadStringAllocEx(HINSTANCE hInstance, UINT nID, WORD wLangID)
{
LPTSTR psz = NULL;
LoadStringHelper(hInstance, nID, &psz, 0, wLangID);
return psz;
}
void TrimLeft(LPTSTR pszText)
{
LPTSTR pch2 = pszText; // will point to first non-space
while (*pch2 == _T(' '))
pch2++;
// If there's leading space, slide the string down
if (pch2 != pszText)
{
// Note: it's safe to skip CharNext here, since '\0' is immune to DBCS
while (_T('\0') != (*pszText++ = *pch2++))
NULL;
}
}
void TrimRight(LPTSTR pszText)
{
LPTSTR pch2 = NULL; // will point to beginning of trailing space
while (*pszText != _T('\0'))
{
if (*pszText == _T(' '))
{
if (pch2 == NULL)
pch2 = pszText;
}
else
{
// found more non-space, reset the trailing-space pointer
pch2 = NULL;
}
pszText = CharNext(pszText);
}
// Truncate the trailing spaces, if any
if (pch2 != NULL)
*pch2 = _T('\0');
}
// RegDeleteKeyAndSubKeys
//
// Does what RegDeleteKey should do. (Actually a single call to RegDeleteKey
// will do this in Win95, but not in NT, according to the docs. Should see
// if this gets fixed in NT5.)
//
// 2/24/1998 KenSh Created
//
DWORD RegDeleteKeyAndSubKeys(HKEY hkey, LPCTSTR pszSubKey)
{
#if 0 // This might be faster in Win95 than doing it manually, but bigger.
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
#endif
{
HKEY hSubKey;
LONG err = RegOpenKeyEx(hkey, pszSubKey, 0, KEY_ALL_ACCESS, &hSubKey);
if (ERROR_SUCCESS == err)
{
DWORD dwNumSubKeys;
RegQueryInfoKey(hSubKey, NULL, NULL, NULL, &dwNumSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
for (DWORD iSubKey = dwNumSubKeys; iSubKey > 0; iSubKey--)
{
TCHAR szSubKey[260];
DWORD cchSubKey = _countof(szSubKey);
if (ERROR_SUCCESS == RegEnumKeyEx(hSubKey, iSubKey-1, szSubKey, &cchSubKey, NULL, NULL, NULL, NULL))
{
RegDeleteKeyAndSubKeys(hSubKey, szSubKey);
}
}
RegCloseKey(hSubKey);
}
}
return RegDeleteKey(hkey, pszSubKey);
}
// LoadFile
//
// Loads the file and null-terminates the copy in memory. The memory
// is allocated via malloc().
//
// 4/05/1996 KenSh Created
// 8/27/1996 KenSh Improved error checking
// 4/21/1997 KenSh Tightened up a bit
// 2/01/1998 KenSh Append a null-terminating byte
// 9/29/1999 KenSh use malloc instead of new []
//
LPBYTE LoadFile(LPCTSTR pszFileName, DWORD* pdwFileSize /*=NULL*/)
{
HANDLE hFile;
LPBYTE pData = NULL;
DWORD dwFileSize = 0;
hFile = CreateFile( pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
if (hFile == INVALID_HANDLE_VALUE)
goto done;
dwFileSize = GetFileSize(hFile, NULL);
ASSERT(dwFileSize != 0xFFFFFFFF); // this shouldn't ever happen for valid hFile
pData = (LPBYTE)malloc(dwFileSize + 1);
if (!pData)
goto done;
DWORD cbRead;
if (!ReadFile(hFile, pData, dwFileSize, &cbRead, NULL))
{
free(pData);
pData = NULL;
goto done;
}
pData[dwFileSize] = 0;
done:
if (pdwFileSize)
*pdwFileSize = dwFileSize;
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
return pData;
}
// DrawHollowRect
//
// Draws a hollow rectangle in the current background color.
//
// 2/06/1998 KenSh Created
//
void DrawHollowRect(HDC hdc, const RECT* pRect, int cxLeft, int cyTop, int cxRight, int cyBottom)
{
RECT rcCopy;
RECT rcNewCoords;
int i;
CopyRect(&rcCopy, pRect);
SetRect(&rcNewCoords,
pRect->right - cxRight,
pRect->bottom - cyBottom,
pRect->left + cxLeft,
pRect->top + cyTop);
// Do each side in turn : right, bottom, left, top
for (i = 0; i < 4; i++)
{
LONG coordSave = ((LONG*)&rcCopy)[i];
((LONG*)&rcCopy)[i] = ((LONG*)&rcNewCoords)[i];
DrawFastRect(hdc, &rcCopy);
((LONG*)&rcCopy)[i] = coordSave;
}
}
void DrawFastRect(HDC hdc, const RECT* pRect)
{
COLORREF crTextSave = SetTextColor(hdc, GetBkColor(hdc));
ExtTextOut(hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, pRect, TEXT(" "), 1, NULL);
SetTextColor(hdc, crTextSave);
}
int GetFontHeight(HFONT hFont)
{
HDC hdcT = GetDC(NULL);
HFONT hFontSave = (HFONT)SelectObject(hdcT, hFont);
TEXTMETRIC tm;
GetTextMetrics(hdcT, &tm);
SelectObject(hdcT, hFontSave);
ReleaseDC(NULL, hdcT);
return tm.tmHeight;
}
HRESULT MyGetSpecialFolderPath(int nFolder, LPTSTR pszPath)
{
LPITEMIDLIST pidl;
HRESULT hr;
if (SUCCEEDED(hr = SHGetSpecialFolderLocation(NULL, nFolder, &pidl)))
{
hr = SHGetPathFromIDList(pidl, pszPath) ? S_OK : E_FAIL;
LPMALLOC pMalloc;
if (SUCCEEDED(SHGetMalloc(&pMalloc)))
{
pMalloc->Free(pidl);
pMalloc->Release();
}
}
return hr;
}