1040 lines
27 KiB
C
1040 lines
27 KiB
C
|
/*
|
|||
|
* UTILITY.C
|
|||
|
*
|
|||
|
* Utility routines for functions inside OLE2UI.DLL
|
|||
|
*
|
|||
|
* General:
|
|||
|
* ----------------------
|
|||
|
* HourGlassOn Displays the hourglass
|
|||
|
* HourGlassOff Hides the hourglass
|
|||
|
*
|
|||
|
* Misc Tools:
|
|||
|
* ----------------------
|
|||
|
* Browse Displays the "File..." or "Browse..." dialog.
|
|||
|
* ReplaceCharWithNull Used to form filter strings for Browse.
|
|||
|
* ErrorWithFile Creates an error message with embedded filename
|
|||
|
* OpenFileError Give error message for OpenFile error return
|
|||
|
* ChopText Chop a file path to fit within a specified width
|
|||
|
* DoesFileExist Checks if file is valid
|
|||
|
*
|
|||
|
* Registration Database:
|
|||
|
* ----------------------
|
|||
|
* HIconFromClass Extracts the first icon in a class's server path
|
|||
|
* FServerFromClass Retrieves the server path for a class name (fast)
|
|||
|
* UClassFromDescription Finds the classname given a description (slow)
|
|||
|
* UDescriptionFromClass Retrieves the description for a class name (fast)
|
|||
|
* FGetVerb Retrieves a specific verb for a class (fast)
|
|||
|
*
|
|||
|
*
|
|||
|
* Copyright (c)1992 Microsoft Corporation, All Right Reserved
|
|||
|
*/
|
|||
|
|
|||
|
#define STRICT 1
|
|||
|
#include "ole2ui.h"
|
|||
|
#include <stdlib.h>
|
|||
|
#include <commdlg.h>
|
|||
|
#include <memory.h>
|
|||
|
#include <cderr.h>
|
|||
|
#include "common.h"
|
|||
|
#include "utility.h"
|
|||
|
#include "geticon.h"
|
|||
|
|
|||
|
OLEDBGDATA
|
|||
|
|
|||
|
/*
|
|||
|
* HourGlassOn
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Shows the hourglass cursor returning the last cursor in use.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* None
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* HCURSOR Cursor in use prior to showing the hourglass.
|
|||
|
*/
|
|||
|
|
|||
|
HCURSOR WINAPI HourGlassOn(void)
|
|||
|
{
|
|||
|
HCURSOR hCur;
|
|||
|
|
|||
|
hCur=SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|||
|
ShowCursor(TRUE);
|
|||
|
|
|||
|
return hCur;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* HourGlassOff
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Turns off the hourglass restoring it to a previous cursor.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* hCur HCURSOR as returned from HourGlassOn
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* None
|
|||
|
*/
|
|||
|
|
|||
|
void WINAPI HourGlassOff(HCURSOR hCur)
|
|||
|
{
|
|||
|
ShowCursor(FALSE);
|
|||
|
SetCursor(hCur);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Browse
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Displays the standard GetOpenFileName dialog with the title of
|
|||
|
* "Browse." The types listed in this dialog are controlled through
|
|||
|
* iFilterString. If it's zero, then the types are filled with "*.*"
|
|||
|
* Otherwise that string is loaded from resources and used.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* hWndOwner HWND owning the dialog
|
|||
|
* lpszFile LPSTR specifying the initial file and the buffer in
|
|||
|
* which to return the selected file. If there is no
|
|||
|
* initial file the first character of this string should
|
|||
|
* be NULL.
|
|||
|
* lpszInitialDir LPSTR specifying the initial directory. If none is to
|
|||
|
* set (ie, the cwd should be used), then this parameter
|
|||
|
* should be NULL.
|
|||
|
* cchFile UINT length of pszFile
|
|||
|
* iFilterString UINT index into the stringtable for the filter string.
|
|||
|
* dwOfnFlags DWORD flags to OR with OFN_HIDEREADONLY
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* BOOL TRUE if the user selected a file and pressed OK.
|
|||
|
* FALSE otherwise, such as on pressing Cancel.
|
|||
|
*/
|
|||
|
|
|||
|
BOOL WINAPI Browse(HWND hWndOwner, LPTSTR lpszFile, LPTSTR lpszInitialDir, UINT cchFile, UINT iFilterString, DWORD dwOfnFlags)
|
|||
|
{
|
|||
|
UINT cch;
|
|||
|
TCHAR szFilters[256];
|
|||
|
OPENFILENAME ofn;
|
|||
|
BOOL fStatus;
|
|||
|
DWORD dwError;
|
|||
|
TCHAR szDlgTitle[128]; // that should be big enough
|
|||
|
|
|||
|
if (NULL==lpszFile || 0==cchFile)
|
|||
|
return FALSE;
|
|||
|
|
|||
|
/*
|
|||
|
* REVIEW: Exact contents of the filter combobox is TBD. One idea
|
|||
|
* is to take all the extensions in the RegDB and place them in here
|
|||
|
* with the descriptive class name associate with them. This has the
|
|||
|
* extra step of finding all extensions of the same class handler and
|
|||
|
* building one extension string for all of them. Can get messy quick.
|
|||
|
* UI demo has only *.* which we do for now.
|
|||
|
*/
|
|||
|
|
|||
|
if (0!=iFilterString)
|
|||
|
cch=LoadString(ghInst, iFilterString, (LPTSTR)szFilters, sizeof(szFilters)/sizeof(TCHAR));
|
|||
|
else
|
|||
|
{
|
|||
|
szFilters[0]=0;
|
|||
|
cch=1;
|
|||
|
}
|
|||
|
|
|||
|
if (0==cch)
|
|||
|
return FALSE;
|
|||
|
|
|||
|
ReplaceCharWithNull(szFilters, szFilters[cch-1]);
|
|||
|
|
|||
|
//Prior string must also be initialized, if there is one.
|
|||
|
_fmemset((LPOPENFILENAME)&ofn, 0, sizeof(ofn));
|
|||
|
ofn.lStructSize =sizeof(ofn);
|
|||
|
ofn.hwndOwner =hWndOwner;
|
|||
|
ofn.lpstrFile =lpszFile;
|
|||
|
ofn.nMaxFile =cchFile;
|
|||
|
ofn.lpstrFilter =(LPTSTR)szFilters;
|
|||
|
ofn.nFilterIndex=1;
|
|||
|
if (LoadString(ghInst, IDS_BROWSE, (LPTSTR)szDlgTitle, sizeof(szDlgTitle)/sizeof(TCHAR)))
|
|||
|
ofn.lpstrTitle =(LPTSTR)szDlgTitle;
|
|||
|
ofn.hInstance = ghInst;
|
|||
|
ofn.lpTemplateName = MAKEINTRESOURCE(IDD_FILEOPEN);
|
|||
|
if (NULL != lpszInitialDir)
|
|||
|
ofn.lpstrInitialDir = lpszInitialDir;
|
|||
|
|
|||
|
ofn.Flags= OFN_HIDEREADONLY | OFN_ENABLETEMPLATE | (dwOfnFlags) ;
|
|||
|
|
|||
|
//On success, copy the chosen filename to the static display
|
|||
|
fStatus = GetOpenFileName((LPOPENFILENAME)&ofn);
|
|||
|
dwError = CommDlgExtendedError();
|
|||
|
return fStatus;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ReplaceCharWithNull
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Walks a null-terminated string and replaces a given character
|
|||
|
* with a zero. Used to turn a single string for file open/save
|
|||
|
* filters into the appropriate filter string as required by the
|
|||
|
* common dialog API.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* psz LPTSTR to the string to process.
|
|||
|
* ch int character to replace.
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* int Number of characters replaced. -1 if psz is NULL.
|
|||
|
*/
|
|||
|
|
|||
|
int WINAPI ReplaceCharWithNull(LPTSTR psz, int ch)
|
|||
|
{
|
|||
|
int cChanged=-1;
|
|||
|
|
|||
|
if (NULL!=psz)
|
|||
|
{
|
|||
|
while (0!=*psz)
|
|||
|
{
|
|||
|
if (ch==*psz)
|
|||
|
{
|
|||
|
*psz=TEXT('\0');
|
|||
|
cChanged++;
|
|||
|
}
|
|||
|
psz++;
|
|||
|
}
|
|||
|
}
|
|||
|
return cChanged;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ErrorWithFile
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Displays a message box built from a stringtable string containing
|
|||
|
* one %s as a placeholder for a filename and from a string of the
|
|||
|
* filename to place there.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* hWnd HWND owning the message box. The caption of this
|
|||
|
* window is the caption of the message box.
|
|||
|
* hInst HINSTANCE from which to draw the idsErr string.
|
|||
|
* idsErr UINT identifier of a stringtable string containing
|
|||
|
* the error message with a %s.
|
|||
|
* lpszFile LPSTR to the filename to include in the message.
|
|||
|
* uFlags UINT flags to pass to MessageBox, like MB_OK.
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* int Return value from MessageBox.
|
|||
|
*/
|
|||
|
|
|||
|
int WINAPI ErrorWithFile(HWND hWnd, HINSTANCE hInst, UINT idsErr
|
|||
|
, LPTSTR pszFile, UINT uFlags)
|
|||
|
{
|
|||
|
int iRet=0;
|
|||
|
HANDLE hMem;
|
|||
|
const UINT cb=(2*OLEUI_CCHPATHMAX_SIZE);
|
|||
|
LPTSTR psz1, psz2, psz3;
|
|||
|
|
|||
|
if (NULL==hInst || NULL==pszFile)
|
|||
|
return iRet;
|
|||
|
|
|||
|
//Allocate three 2*OLEUI_CCHPATHMAX byte work buffers
|
|||
|
hMem=GlobalAlloc(GHND, (DWORD)(3*cb));
|
|||
|
|
|||
|
if (NULL==hMem)
|
|||
|
return iRet;
|
|||
|
|
|||
|
psz1=GlobalLock(hMem);
|
|||
|
psz2=psz1+cb;
|
|||
|
psz3=psz2+cb;
|
|||
|
|
|||
|
if (0!=LoadString(hInst, idsErr, psz1, cb))
|
|||
|
{
|
|||
|
wsprintf(psz2, psz1, pszFile);
|
|||
|
|
|||
|
//Steal the caption of the dialog
|
|||
|
GetWindowText(hWnd, psz3, cb);
|
|||
|
iRet=MessageBox(hWnd, psz2, psz3, uFlags);
|
|||
|
}
|
|||
|
|
|||
|
GlobalUnlock(hMem);
|
|||
|
GlobalFree(hMem);
|
|||
|
return iRet;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* HIconFromClass
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Given an object class name, finds an associated executable in the
|
|||
|
* registration database and extracts the first icon from that
|
|||
|
* executable. If none is available or the class has no associated
|
|||
|
* executable, this function returns NULL.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* pszClass LPSTR giving the object class to look up.
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* HICON Handle to the extracted icon if there is a module
|
|||
|
* associated to pszClass. NULL on failure to either
|
|||
|
* find the executable or extract and icon.
|
|||
|
*/
|
|||
|
|
|||
|
HICON WINAPI HIconFromClass(LPTSTR pszClass)
|
|||
|
{
|
|||
|
HICON hIcon;
|
|||
|
TCHAR szEXE[OLEUI_CCHPATHMAX];
|
|||
|
UINT Index;
|
|||
|
CLSID clsid;
|
|||
|
|
|||
|
if (NULL==pszClass)
|
|||
|
return NULL;
|
|||
|
|
|||
|
CLSIDFromStringA(pszClass, &clsid);
|
|||
|
|
|||
|
if (!FIconFileFromClass((REFCLSID)&clsid, szEXE, OLEUI_CCHPATHMAX_SIZE, &Index))
|
|||
|
return NULL;
|
|||
|
|
|||
|
hIcon=ExtractIcon(ghInst, szEXE, Index);
|
|||
|
|
|||
|
if ((HICON)32 > hIcon)
|
|||
|
hIcon=NULL;
|
|||
|
|
|||
|
return hIcon;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* FServerFromClass
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Looks up the classname in the registration database and retrieves
|
|||
|
* the name undet protocol\StdFileEditing\server.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* pszClass LPSTR to the classname to look up.
|
|||
|
* pszEXE LPSTR at which to store the server name
|
|||
|
* cch UINT size of pszEXE
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* BOOL TRUE if one or more characters were loaded into pszEXE.
|
|||
|
* FALSE otherwise.
|
|||
|
*/
|
|||
|
|
|||
|
BOOL WINAPI FServerFromClass(LPTSTR pszClass, LPTSTR pszEXE, UINT cch)
|
|||
|
{
|
|||
|
|
|||
|
DWORD dw;
|
|||
|
LONG lRet;
|
|||
|
HKEY hKey;
|
|||
|
|
|||
|
if (NULL==pszClass || NULL==pszEXE || 0==cch)
|
|||
|
return FALSE;
|
|||
|
|
|||
|
/*
|
|||
|
* We have to go walking in the registration database under the
|
|||
|
* classname, so we first open the classname key and then check
|
|||
|
* under "\\LocalServer" to get the .EXE.
|
|||
|
*/
|
|||
|
|
|||
|
//Open up the class key
|
|||
|
lRet=RegOpenKey(HKEY_CLASSES_ROOT, pszClass, &hKey);
|
|||
|
|
|||
|
if ((LONG)ERROR_SUCCESS!=lRet)
|
|||
|
return FALSE;
|
|||
|
|
|||
|
//Get the executable path.
|
|||
|
dw=(DWORD)cch;
|
|||
|
lRet=RegQueryValue(hKey, TEXT("LocalServer"), pszEXE, &dw);
|
|||
|
|
|||
|
RegCloseKey(hKey);
|
|||
|
|
|||
|
return ((ERROR_SUCCESS == lRet) && (dw > 0));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* UClassFromDescription
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Looks up the actual OLE class name in the registration database
|
|||
|
* for the given descriptive name chosen from a listbox.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* psz LPSTR to the descriptive name.
|
|||
|
* pszClass LPSTR in which to store the class name.
|
|||
|
* cb UINT maximum length of pszClass.
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* UINT Number of characters copied to pszClass. 0 on failure.
|
|||
|
*/
|
|||
|
|
|||
|
UINT WINAPI UClassFromDescription(LPTSTR psz, LPTSTR pszClass, UINT cb)
|
|||
|
{
|
|||
|
DWORD dw;
|
|||
|
HKEY hKey;
|
|||
|
TCHAR szClass[OLEUI_CCHKEYMAX];
|
|||
|
LONG lRet;
|
|||
|
UINT i;
|
|||
|
|
|||
|
//Open up the root key.
|
|||
|
lRet=RegOpenKey(HKEY_CLASSES_ROOT, NULL, &hKey);
|
|||
|
|
|||
|
if ((LONG)ERROR_SUCCESS!=lRet)
|
|||
|
return 0;
|
|||
|
|
|||
|
i=0;
|
|||
|
lRet=RegEnumKey(hKey, i++, szClass, OLEUI_CCHKEYMAX_SIZE);
|
|||
|
|
|||
|
//Walk the available keys
|
|||
|
while ((LONG)ERROR_SUCCESS==lRet)
|
|||
|
{
|
|||
|
dw=(DWORD)cb;
|
|||
|
lRet=RegQueryValue(hKey, szClass, pszClass, &dw);
|
|||
|
|
|||
|
//Check if the description matches the one just enumerated
|
|||
|
if ((LONG)ERROR_SUCCESS==lRet)
|
|||
|
{
|
|||
|
if (!lstrcmp(pszClass, psz))
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//Continue with the next key.
|
|||
|
lRet=RegEnumKey(hKey, i++, szClass, OLEUI_CCHKEYMAX_SIZE);
|
|||
|
}
|
|||
|
|
|||
|
//If we found it, copy to the return buffer
|
|||
|
if ((LONG)ERROR_SUCCESS==lRet)
|
|||
|
lstrcpy(pszClass, szClass);
|
|||
|
else
|
|||
|
dw=0L;
|
|||
|
|
|||
|
RegCloseKey(hKey);
|
|||
|
return (UINT)dw;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* UDescriptionFromClass
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Looks up the actual OLE descriptive name name in the registration
|
|||
|
* database for the given class name.
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* pszClass LPSTR to the class name.
|
|||
|
* psz LPSTR in which to store the descriptive name.
|
|||
|
* cb UINT maximum length of psz.
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* UINT Number of characters copied to pszClass. 0 on failure.
|
|||
|
*/
|
|||
|
|
|||
|
UINT WINAPI UDescriptionFromClass(LPTSTR pszClass, LPTSTR psz, UINT cb)
|
|||
|
{
|
|||
|
DWORD dw;
|
|||
|
HKEY hKey;
|
|||
|
LONG lRet;
|
|||
|
|
|||
|
if (NULL==pszClass || NULL==psz)
|
|||
|
return 0;
|
|||
|
|
|||
|
//Open up the root key.
|
|||
|
lRet=RegOpenKey(HKEY_CLASSES_ROOT, NULL, &hKey);
|
|||
|
|
|||
|
if ((LONG)ERROR_SUCCESS!=lRet)
|
|||
|
return 0;
|
|||
|
|
|||
|
//Get the descriptive name using the class name.
|
|||
|
dw=(DWORD)cb;
|
|||
|
lRet=RegQueryValue(hKey, pszClass, psz, &dw);
|
|||
|
|
|||
|
RegCloseKey(hKey);
|
|||
|
|
|||
|
psz+=lstrlen(psz)+1;
|
|||
|
*psz=0;
|
|||
|
|
|||
|
if ((LONG)ERROR_SUCCESS!=lRet)
|
|||
|
return 0;
|
|||
|
|
|||
|
return (UINT)dw;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// returns width of line of text. this is a support routine for ChopText
|
|||
|
static LONG GetTextWSize(HDC hDC, LPTSTR lpsz)
|
|||
|
{
|
|||
|
SIZE size;
|
|||
|
|
|||
|
if (GetTextExtentPoint(hDC, lpsz, lstrlen(lpsz), (LPSIZE)&size))
|
|||
|
return size.cx;
|
|||
|
else {
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* ChopText
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Parse a string (pathname) and convert it to be within a specified
|
|||
|
* length by chopping the least significant part
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* hWnd window handle in which the string resides
|
|||
|
* nWidth max width of string in pixels
|
|||
|
* use width of hWnd if zero
|
|||
|
* lpch pointer to beginning of the string
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* pointer to the modified string
|
|||
|
*/
|
|||
|
LPTSTR WINAPI ChopText(HWND hWnd, int nWidth, LPTSTR lpch)
|
|||
|
{
|
|||
|
#define PREFIX_SIZE 7 + 1
|
|||
|
#define PREFIX_FORMAT TEXT("%c%c%c...\\")
|
|||
|
|
|||
|
TCHAR szPrefix[PREFIX_SIZE];
|
|||
|
BOOL fDone = FALSE;
|
|||
|
int i;
|
|||
|
RECT rc;
|
|||
|
HDC hdc;
|
|||
|
HFONT hfont;
|
|||
|
HFONT hfontOld = NULL;
|
|||
|
|
|||
|
if (!hWnd || !lpch)
|
|||
|
return NULL;
|
|||
|
|
|||
|
/* Get length of static field. */
|
|||
|
if (!nWidth) {
|
|||
|
GetClientRect(hWnd, (LPRECT)&rc);
|
|||
|
nWidth = rc.right - rc.left;
|
|||
|
}
|
|||
|
|
|||
|
/* Set up DC appropriately for the static control */
|
|||
|
hdc = GetDC(hWnd);
|
|||
|
hfont = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0L);
|
|||
|
|
|||
|
if (NULL != hfont) // WM_GETFONT returns NULL if window uses system font
|
|||
|
hfontOld = SelectObject(hdc, hfont);
|
|||
|
|
|||
|
/* check horizontal extent of string */
|
|||
|
if (GetTextWSize(hdc, lpch) > nWidth) {
|
|||
|
|
|||
|
/* string is too long to fit in static control; chop it */
|
|||
|
/* set up new prefix & determine remaining space in control */
|
|||
|
wsprintf((LPTSTR) szPrefix, PREFIX_FORMAT, lpch[0], lpch[1], lpch[2]);
|
|||
|
nWidth -= (int)GetTextWSize(hdc, (LPTSTR) szPrefix);
|
|||
|
|
|||
|
/*
|
|||
|
** advance a directory at a time until the remainder of the
|
|||
|
** string fits into the static control after the "x:\...\" prefix
|
|||
|
*/
|
|||
|
while (!fDone) {
|
|||
|
|
|||
|
#ifdef DBCS
|
|||
|
while (*lpch && (*lpch != TEXT('\\')))
|
|||
|
#ifdef WIN32
|
|||
|
lpch = CharNext(lpch);
|
|||
|
#else
|
|||
|
lpch = AnsiNext(lpch);
|
|||
|
#endif
|
|||
|
if (*lpch)
|
|||
|
#ifdef WIN32
|
|||
|
lpch = CharNext(lpch);
|
|||
|
#else
|
|||
|
lpch = AnsiNext(lpch);
|
|||
|
#endif
|
|||
|
#else
|
|||
|
while (*lpch && (*lpch++ != TEXT('\\')));
|
|||
|
#endif
|
|||
|
|
|||
|
if (!*lpch || GetTextWSize(hdc, lpch) <= nWidth) {
|
|||
|
if (!*lpch)
|
|||
|
/*
|
|||
|
** Nothing could fit after the prefix; remove the
|
|||
|
** final "\" from the prefix
|
|||
|
*/
|
|||
|
szPrefix[lstrlen((LPTSTR) szPrefix) - 1] = 0;
|
|||
|
|
|||
|
/* rest or string fits -- stick prefix on front */
|
|||
|
for (i = lstrlen((LPTSTR) szPrefix) - 1; i >= 0; --i)
|
|||
|
*--lpch = szPrefix[i];
|
|||
|
fDone = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (NULL != hfont)
|
|||
|
SelectObject(hdc, hfontOld);
|
|||
|
ReleaseDC(hWnd, hdc);
|
|||
|
|
|||
|
return(lpch);
|
|||
|
|
|||
|
#undef PREFIX_SIZE
|
|||
|
#undef PREFIX_FORMAT
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* OpenFileError
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* display message for error returned from OpenFile
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* hDlg HWND of the dialog.
|
|||
|
* nErrCode UINT error code returned in OFSTRUCT passed to OpenFile
|
|||
|
* lpszFile LPSTR file name passed to OpenFile
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* None
|
|||
|
*/
|
|||
|
void WINAPI OpenFileError(HWND hDlg, UINT nErrCode, LPTSTR lpszFile)
|
|||
|
{
|
|||
|
switch (nErrCode) {
|
|||
|
case 0x0005: // Access denied
|
|||
|
ErrorWithFile(hDlg, ghInst, IDS_CIFILEACCESS, lpszFile, MB_OK);
|
|||
|
break;
|
|||
|
|
|||
|
case 0x0020: // Sharing violation
|
|||
|
ErrorWithFile(hDlg, ghInst, IDS_CIFILESHARE, lpszFile, MB_OK);
|
|||
|
break;
|
|||
|
|
|||
|
case 0x0002: // File not found
|
|||
|
case 0x0003: // Path not found
|
|||
|
ErrorWithFile(hDlg, ghInst, IDS_CIINVALIDFILE, lpszFile, MB_OK);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
ErrorWithFile(hDlg, ghInst, IDS_CIFILEOPENFAIL, lpszFile, MB_OK);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#define chSpace TEXT(' ')
|
|||
|
#define chPeriod TEXT('.')
|
|||
|
#define PARSE_EMPTYSTRING -1
|
|||
|
#define PARSE_INVALIDDRIVE -2
|
|||
|
#define PARSE_INVALIDPERIOD -3
|
|||
|
#define PARSE_INVALIDDIRCHAR -4
|
|||
|
#define PARSE_INVALIDCHAR -5
|
|||
|
#define PARSE_WILDCARDINDIR -6
|
|||
|
#define PARSE_INVALIDNETPATH -7
|
|||
|
#define PARSE_INVALIDSPACE -8
|
|||
|
#define PARSE_EXTENTIONTOOLONG -9
|
|||
|
#define PARSE_DIRECTORYNAME -10
|
|||
|
#define PARSE_FILETOOLONG -11
|
|||
|
|
|||
|
/*---------------------------------------------------------------------------
|
|||
|
* ParseFile
|
|||
|
* Purpose: Determine if the filename is a legal DOS name
|
|||
|
* Input: Long pointer to a SINGLE file name
|
|||
|
* Circumstance checked:
|
|||
|
* 1) Valid as directory name, but not as file name
|
|||
|
* 2) Empty String
|
|||
|
* 3) Illegal Drive label
|
|||
|
* 4) Period in invalid location (in extention, 1st in file name)
|
|||
|
* 5) Missing directory character
|
|||
|
* 6) Illegal character
|
|||
|
* 7) Wildcard in directory name
|
|||
|
* 8) Double slash beyond 1st 2 characters
|
|||
|
* 9) Space character in the middle of the name (trailing spaces OK)
|
|||
|
* 10) Filename greater than 8 characters
|
|||
|
* 11) Extention greater than 3 characters
|
|||
|
* Notes:
|
|||
|
* Filename length is NOT checked.
|
|||
|
* Valid filenames will have leading spaces, trailing spaces and
|
|||
|
* terminating period stripped in place.
|
|||
|
*
|
|||
|
* Returns: If valid, LOWORD is byte offset to filename
|
|||
|
* HIWORD is byte offset to extention
|
|||
|
* if string ends with period, 0
|
|||
|
* if no extention is given, string length
|
|||
|
* If invalid, LOWORD is error code suggesting problem (< 0)
|
|||
|
* HIWORD is approximate offset where problem found
|
|||
|
* Note that this may be beyond the offending character
|
|||
|
*--------------------------------------------------------------------------*/
|
|||
|
|
|||
|
static long ParseFile(LPTSTR lpstrFileName)
|
|||
|
{
|
|||
|
short nFile, nExt, nFileOffset, nExtOffset;
|
|||
|
BOOL bExt;
|
|||
|
BOOL bWildcard;
|
|||
|
short nNetwork = 0;
|
|||
|
BOOL bUNCPath = FALSE;
|
|||
|
LPTSTR lpstr = lpstrFileName;
|
|||
|
|
|||
|
/* Strip off initial white space. Note that TAB is not checked */
|
|||
|
/* because it cannot be received out of a standard edit control */
|
|||
|
/* 30 January 1991 clarkc */
|
|||
|
while (*lpstr == chSpace)
|
|||
|
lpstr++;
|
|||
|
|
|||
|
if (!*lpstr)
|
|||
|
{
|
|||
|
nFileOffset = PARSE_EMPTYSTRING;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
if (lpstr != lpstrFileName)
|
|||
|
{
|
|||
|
lstrcpy(lpstrFileName, lpstr);
|
|||
|
lpstr = lpstrFileName;
|
|||
|
}
|
|||
|
|
|||
|
if (
|
|||
|
|
|||
|
#ifdef WIN32
|
|||
|
*CharNext(lpstr)
|
|||
|
#else
|
|||
|
*AnsiNext(lpstr)
|
|||
|
#endif
|
|||
|
== TEXT(':')
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
TCHAR cDrive = (*lpstr | (BYTE) 0x20); /* make lowercase */
|
|||
|
|
|||
|
/* This does not test if the drive exists, only if it's legal */
|
|||
|
if ((cDrive < TEXT('a')) || (cDrive > TEXT('z')))
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDDRIVE;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
#ifdef WIN32
|
|||
|
lpstr = CharNext(CharNext(lpstr));
|
|||
|
#else
|
|||
|
lpstr = AnsiNext(AnsiNext(lpstr));
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
if ((*lpstr == TEXT('\\')) || (*lpstr == TEXT('/')))
|
|||
|
{
|
|||
|
if (*++lpstr == chPeriod) /* cannot have c:\. */
|
|||
|
{
|
|||
|
if ((*++lpstr != TEXT('\\')) && (*lpstr != TEXT('/')))
|
|||
|
{
|
|||
|
if (!*lpstr) /* it's the root directory */
|
|||
|
goto MustBeDir;
|
|||
|
|
|||
|
nFileOffset = PARSE_INVALIDPERIOD;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
else
|
|||
|
++lpstr; /* it's saying top directory (again), thus allowed */
|
|||
|
}
|
|||
|
else if ((*lpstr == TEXT('\\')) && (*(lpstr-1) == TEXT('\\')))
|
|||
|
{
|
|||
|
/* It seems that for a full network path, whether a drive is declared or
|
|||
|
* not is insignificant, though if a drive is given, it must be valid
|
|||
|
* (hence the code above should remain there).
|
|||
|
* 13 February 1991 clarkc
|
|||
|
*/
|
|||
|
++lpstr; /* ...since it's the first slash, 2 are allowed */
|
|||
|
nNetwork = -1; /* Must receive server and share to be real */
|
|||
|
bUNCPath = TRUE; /* No wildcards allowed if UNC name */
|
|||
|
}
|
|||
|
else if (*lpstr == TEXT('/'))
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDDIRCHAR;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (*lpstr == chPeriod)
|
|||
|
{
|
|||
|
if (*++lpstr == chPeriod) /* Is this up one directory? */
|
|||
|
++lpstr;
|
|||
|
if (!*lpstr)
|
|||
|
goto MustBeDir;
|
|||
|
if ((*lpstr != TEXT('\\')) && (*lpstr != TEXT('/')))
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDPERIOD;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
else
|
|||
|
++lpstr; /* it's saying directory, thus allowed */
|
|||
|
}
|
|||
|
|
|||
|
if (!*lpstr)
|
|||
|
{
|
|||
|
goto MustBeDir;
|
|||
|
}
|
|||
|
|
|||
|
/* Should point to first char in 8.3 filename by now */
|
|||
|
nFileOffset = nExtOffset = nFile = nExt = 0;
|
|||
|
bWildcard = bExt = FALSE;
|
|||
|
while (*lpstr)
|
|||
|
{
|
|||
|
/*
|
|||
|
* The next comparison MUST be unsigned to allow for extended characters!
|
|||
|
* 21 Feb 1991 clarkc
|
|||
|
*/
|
|||
|
if (*lpstr < chSpace)
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDCHAR;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
switch (*lpstr)
|
|||
|
{
|
|||
|
case TEXT('"'): /* All invalid */
|
|||
|
case TEXT('+'):
|
|||
|
case TEXT(','):
|
|||
|
case TEXT(':'):
|
|||
|
case TEXT(';'):
|
|||
|
case TEXT('<'):
|
|||
|
case TEXT('='):
|
|||
|
case TEXT('>'):
|
|||
|
case TEXT('['):
|
|||
|
case TEXT(']'):
|
|||
|
case TEXT('|'):
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDCHAR;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
case TEXT('\\'): /* Subdirectory indicators */
|
|||
|
case TEXT('/'):
|
|||
|
nNetwork++;
|
|||
|
if (bWildcard)
|
|||
|
{
|
|||
|
nFileOffset = PARSE_WILDCARDINDIR;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
else if (nFile == 0) /* can't have 2 in a row */
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDDIRCHAR;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
else
|
|||
|
{ /* reset flags */
|
|||
|
++lpstr;
|
|||
|
if (!nNetwork && !*lpstr)
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDNETPATH;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
nFile = nExt = 0;
|
|||
|
bExt = FALSE;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case chSpace:
|
|||
|
{
|
|||
|
LPTSTR lpSpace = lpstr;
|
|||
|
|
|||
|
*lpSpace = TEXT('\0');
|
|||
|
while (*++lpSpace)
|
|||
|
{
|
|||
|
if (*lpSpace != chSpace)
|
|||
|
{
|
|||
|
*lpstr = chSpace; /* Reset string, abandon ship */
|
|||
|
nFileOffset = PARSE_INVALIDSPACE;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case chPeriod:
|
|||
|
if (nFile == 0)
|
|||
|
{
|
|||
|
if (*++lpstr == chPeriod)
|
|||
|
++lpstr;
|
|||
|
if (!*lpstr)
|
|||
|
goto MustBeDir;
|
|||
|
|
|||
|
if ((*lpstr != TEXT('\\')) && (*lpstr != TEXT('/')))
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDPERIOD;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
++lpstr; /* Flags are already set */
|
|||
|
}
|
|||
|
else if (bExt)
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDPERIOD; /* can't have one in ext */
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
nExtOffset = 0;
|
|||
|
++lpstr;
|
|||
|
bExt = TRUE;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case TEXT('*'):
|
|||
|
case TEXT('?'):
|
|||
|
if (bUNCPath)
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDNETPATH;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
bWildcard = TRUE;
|
|||
|
/* Fall through to normal character processing */
|
|||
|
|
|||
|
default:
|
|||
|
if (bExt)
|
|||
|
{
|
|||
|
if (++nExt == 1)
|
|||
|
nExtOffset = lpstr - lpstrFileName;
|
|||
|
else if (nExt > 3)
|
|||
|
{
|
|||
|
nFileOffset = PARSE_EXTENTIONTOOLONG;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
if ((nNetwork == -1) && (nFile + nExt > 11))
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDNETPATH;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (++nFile == 1)
|
|||
|
nFileOffset = lpstr - lpstrFileName;
|
|||
|
else if (nFile > 8)
|
|||
|
{
|
|||
|
/* If it's a server name, it can have 11 characters */
|
|||
|
if (nNetwork != -1)
|
|||
|
{
|
|||
|
nFileOffset = PARSE_FILETOOLONG;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
else if (nFile > 11)
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDNETPATH;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#ifdef WIN32
|
|||
|
lpstr = CharNext(lpstr);
|
|||
|
#else
|
|||
|
lpstr = AnsiNext(lpstr);
|
|||
|
#endif
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Did we start with a double backslash but not have any more slashes? */
|
|||
|
if (nNetwork == -1)
|
|||
|
{
|
|||
|
nFileOffset = PARSE_INVALIDNETPATH;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
if (!nFile)
|
|||
|
{
|
|||
|
MustBeDir:
|
|||
|
nFileOffset = PARSE_DIRECTORYNAME;
|
|||
|
goto FAILURE;
|
|||
|
}
|
|||
|
|
|||
|
if ((*(lpstr - 1) == chPeriod) && /* if true, no extention wanted */
|
|||
|
(
|
|||
|
#ifdef WIN32
|
|||
|
*CharNext(lpstr-2)
|
|||
|
#else
|
|||
|
*AnsiNext(lpstr-2)
|
|||
|
#endif
|
|||
|
== chPeriod
|
|||
|
))
|
|||
|
*(lpstr - 1) = TEXT('\0'); /* Remove terminating period */
|
|||
|
else if (!nExt)
|
|||
|
FAILURE:
|
|||
|
nExtOffset = lpstr - lpstrFileName;
|
|||
|
|
|||
|
return(MAKELONG(nFileOffset, nExtOffset));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* DoesFileExist
|
|||
|
*
|
|||
|
* Purpose:
|
|||
|
* Determines if a file path exists
|
|||
|
*
|
|||
|
* Parameters:
|
|||
|
* lpszFile LPTSTR - file name
|
|||
|
* lpOpenBuf OFSTRUCT FAR* - points to the OFSTRUCT structure that
|
|||
|
* will receive information about the file when the
|
|||
|
* file is first opened. this field is filled by the
|
|||
|
* Windows OpenFile API.
|
|||
|
*
|
|||
|
* Return Value:
|
|||
|
* HFILE HFILE_ERROR - file does NOT exist
|
|||
|
* file handle (as returned from OpenFile) - file exists
|
|||
|
*/
|
|||
|
HFILE WINAPI DoesFileExist(LPTSTR lpszFile, OFSTRUCT FAR* lpOpenBuf)
|
|||
|
{
|
|||
|
long nRet;
|
|||
|
int i;
|
|||
|
static TCHAR *arrIllegalNames[] = {
|
|||
|
TEXT("LPT1"),
|
|||
|
TEXT("LPT2"),
|
|||
|
TEXT("LPT3"),
|
|||
|
TEXT("COM1"),
|
|||
|
TEXT("COM2"),
|
|||
|
TEXT("COM3"),
|
|||
|
TEXT("COM4"),
|
|||
|
TEXT("CON"),
|
|||
|
TEXT("AUX"),
|
|||
|
TEXT("PRN")
|
|||
|
};
|
|||
|
|
|||
|
// Check if file name is syntactically correct.
|
|||
|
// (OpenFile sometimes crashes if path is not syntactically correct)
|
|||
|
nRet = ParseFile(lpszFile);
|
|||
|
if (LOWORD(nRet) < 0)
|
|||
|
goto error;
|
|||
|
|
|||
|
// Check is the name is an illegal name (eg. the name of a device)
|
|||
|
for (i=0; i < (sizeof(arrIllegalNames)/sizeof(arrIllegalNames[0])); i++) {
|
|||
|
if (lstrcmpi(lpszFile, arrIllegalNames[i])==0)
|
|||
|
goto error; // illegal name FOUND
|
|||
|
}
|
|||
|
|
|||
|
return OpenFile(lpszFile, lpOpenBuf, OF_EXIST);
|
|||
|
|
|||
|
error:
|
|||
|
_fmemset(lpOpenBuf, 0, sizeof(OFSTRUCT));
|
|||
|
lpOpenBuf->nErrCode = 0x0002; // File not found
|
|||
|
return HFILE_ERROR;
|
|||
|
}
|
|||
|
|