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
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;
|
|
}
|
|
|