windows-nt/Source/XPSP1/NT/shell/applets/grpconv/util.c
2020-09-26 16:20:57 +08:00

646 lines
19 KiB
C

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
#include "grpconv.h"
#include "util.h"
#include "rcids.h"
//---------------------------------------------------------------------------
// Global to this file only.
const TCHAR g_szDot[] = TEXT(".");
const TCHAR g_szShellOpenCommand[] = TEXT("\\Shell\\Open\\Command");
const TCHAR c_szElipses[] = TEXT("...");
const TCHAR c_szSpace[] = TEXT(" ");
const TCHAR c_szUS[] = TEXT("_");
static BOOL g_fShowProgressDlg = FALSE;
HWND g_hwndProgress = NULL; // Progress dialog.
//---------------------------------------------------------------------------
LRESULT CALLBACK ProgressWndProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_INITDIALOG:
SetDlgItemText(hdlg, IDC_GROUPNAME, (LPTSTR)lparam);
EnableMenuItem(GetSystemMenu(hdlg, FALSE), SC_CLOSE, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
return TRUE;
}
return 0;
}
//---------------------------------------------------------------------------
void ShowProgressDlg(void)
{
// Has someone tried to create the dialog but it isn't up yet?
if (g_fShowUI && g_fShowProgressDlg && !g_hwndProgress)
{
// Yep.
// NB We can handle this failing, we just try to carry on without
// the dialog.
g_hwndProgress = CreateDialog(g_hinst, MAKEINTRESOURCE(DLG_PROGRESS), NULL, ProgressWndProc);
}
}
//---------------------------------------------------------------------------
void Group_CreateProgressDlg(void)
{
// NB We just set a flag here, the first guy to try to set the
// current progress actually puts up the dialag.
g_fShowProgressDlg = TRUE;
}
//---------------------------------------------------------------------------
void Group_DestroyProgressDlg(void)
{
if (g_hwndProgress)
{
DestroyWindow(g_hwndProgress);
g_hwndProgress = NULL;
}
g_fShowProgressDlg = FALSE;
}
//---------------------------------------------------------------------------
// If the text is too long, lop off the end and stick on some elipses.
void Text_TruncateAndAddElipses(HWND hwnd, LPTSTR lpszText)
{
RECT rcClient;
SIZE sizeText;
SIZE sizeElipses;
HDC hdc;
UINT cch;
Assert(hwnd);
Assert(lpszText);
hdc = GetDC(hwnd);
if (hdc)
{
GetClientRect(hwnd, &rcClient);
GetTextExtentPoint(hdc, lpszText, lstrlen(lpszText), &sizeText);
// Is the text too long?
if (sizeText.cx > rcClient.right)
{
// Yes, it is, clip it.
GetTextExtentPoint(hdc, c_szElipses, 3, &sizeElipses);
GetTextExtentExPoint(hdc, lpszText, lstrlen(lpszText), rcClient.right - sizeElipses.cx,
&cch, NULL, &sizeText);
lstrcpy(lpszText+cch, c_szElipses);
}
ReleaseDC(hwnd, hdc);
}
}
//---------------------------------------------------------------------------
void Group_SetProgressDesc(UINT nID)
{
TCHAR sz[MAX_PATH];
ShowProgressDlg();
if (g_hwndProgress)
{
LoadString(g_hinst, nID, sz, ARRAYSIZE(sz));
SendDlgItemMessage(g_hwndProgress, IDC_STATIC, WM_SETTEXT, 0, (LPARAM)sz);
}
}
//---------------------------------------------------------------------------
void Group_SetProgressNameAndRange(LPCTSTR lpszGroup, int iMax)
{
TCHAR sz[MAX_PATH];
TCHAR szNew[MAX_PATH];
LPTSTR lpszName;
MSG msg;
static int cGen = 1;
ShowProgressDlg();
if (g_hwndProgress)
{
// DebugMsg(DM_TRACE, "gc.gspnar: Range 0 to %d", iMax);
SendDlgItemMessage(g_hwndProgress, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, iMax));
if (lpszGroup == (LPTSTR)-1)
{
// Use some sensible name - Programs (x)
// where x = 1 to n, incremented each time this is
// called.
LoadString(g_hinst, IDS_GROUP, sz, ARRAYSIZE(sz));
wsprintf(szNew, TEXT("%s (%d)"), sz, cGen++);
SetDlgItemText(g_hwndProgress, IDC_GROUPNAME, szNew);
}
else if (lpszGroup && *lpszGroup)
{
lpszName = PathFindFileName(lpszGroup);
lstrcpy(sz, lpszName);
Text_TruncateAndAddElipses(GetDlgItem(g_hwndProgress, IDC_GROUPNAME), sz);
SetDlgItemText(g_hwndProgress, IDC_GROUPNAME, sz);
}
else
{
// Use some sensible name.
LoadString(g_hinst, IDS_PROGRAMS, sz, ARRAYSIZE(sz));
SetDlgItemText(g_hwndProgress, IDC_GROUPNAME, sz);
}
// Let paints come in.
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
}
}
}
//---------------------------------------------------------------------------
void Group_SetProgress(int i)
{
MSG msg;
ShowProgressDlg();
if (g_hwndProgress)
{
// DebugMsg(DM_TRACE, "gc.gsp: Progress %d", i);
// Progman keeps trying to steal the focus...
SetForegroundWindow(g_hwndProgress);
SendDlgItemMessage(g_hwndProgress, IDC_PROGRESS, PBM_SETPOS, i, 0);
}
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
}
}
#if 0
//---------------------------------------------------------------------------
BOOL WritePrivateProfileInt(LPCTSTR lpszSection, LPCTSTR lpszValue, int i, LPCTSTR lpszIniFile)
{
TCHAR szBuf[CCHSZSHORT];
wsprintf(szBuf, TEXT("%d"), i);
return WritePrivateProfileString(lpszSection, lpszValue, szBuf, lpszIniFile);
}
#endif
//---------------------------------------------------------------------------
// Register an app as being able to handle a particular extension with the
// given internal type, human readble type and command.
// NB lpszExt doesn't need a dot.
// By default this won't overide something in the registration DB.
// Setting fOveride to TRUE will cause existing entries in the DB
// to be over written.
void ShellRegisterApp(LPCTSTR lpszExt, LPCTSTR lpszTypeKey,
LPCTSTR lpszTypeValue, LPCTSTR lpszCommand, BOOL fOveride)
{
TCHAR szKey[CCHSZNORMAL];
TCHAR szValue[CCHSZSHORT];
LONG lcb;
LONG lStatus;
// Deal with the mapping from extension to TypeKey.
lstrcpy(szKey, g_szDot);
lstrcat(szKey, lpszExt);
lcb = SIZEOF(szValue);
lStatus = RegQueryValue(HKEY_CLASSES_ROOT, szKey, szValue, &lcb);
// Is the extension not registered or do we even care?
if (lStatus != ERROR_SUCCESS || fOveride)
{
// No, so register it.
lstrcpy(szValue, lpszTypeKey);
if (RegSetValue(HKEY_CLASSES_ROOT, szKey, REG_SZ, lpszTypeKey, 0) == ERROR_SUCCESS)
{
// DebugMsg(DM_TRACE, "gc.sra: Extension registered.");
}
else
{
DebugMsg(DM_ERROR, TEXT("gc.sra: Error registering extension."));
}
}
// Deal with the mapping from TypeKey to TypeValue
lcb = SIZEOF(szValue);
lStatus = RegQueryValue(HKEY_CLASSES_ROOT, lpszTypeKey, szValue, &lcb);
// Is the type not registered or do we even care?
if (lStatus != ERROR_SUCCESS || fOveride)
{
// No, so register it.
if (RegSetValue(HKEY_CLASSES_ROOT, lpszTypeKey, REG_SZ, lpszTypeValue, 0) == ERROR_SUCCESS)
{
// DebugMsg(DM_TRACE, "gc.sra: Type registered.");
}
else
{
DebugMsg(DM_ERROR, TEXT("gc.sra: Error registering type."));
}
}
// Deal with adding the open command.
lstrcpy(szKey, lpszTypeKey);
lstrcat(szKey, g_szShellOpenCommand);
lcb = SIZEOF(szValue);
lStatus = RegQueryValue(HKEY_CLASSES_ROOT, szKey, szValue, &lcb);
// Is the command not registered or do we even care?
if (lStatus != ERROR_SUCCESS || fOveride)
{
// No, so register it.
if (RegSetValue(HKEY_CLASSES_ROOT, szKey, REG_SZ, lpszCommand, 0) == ERROR_SUCCESS)
{
// DebugMsg(DM_TRACE, "gc.sra: Command registered.");
}
else
{
DebugMsg(DM_ERROR, TEXT("gc.sra: Error registering command."));
}
}
}
#if 0
//-------------------------------------------------------------------------
// Do a unix(ish) gets(). This assumes bufferd i/o.
// Reads cb-1 characters (the last one will be a NULL) or up to and including
// the first NULL.
LPTSTR fgets(LPTSTR sz, WORD cb, int fh)
{
UINT i;
// Leave room for the NULL.
cb--;
for (i=0; i<cb; i++)
{
_lread(fh, &sz[i], 1);
// Check for a null.
if (sz[i] == TEXT('\0'))
return sz;
}
// Ran out of room.
// NULL Terminate.
sz[cb-1] = TEXT('\0');
return sz;
}
#else
//-------------------------------------------------------------------------
// Do a unix(ish) gets(). This assumes bufferd i/o.
// Reads cb-1 characters (the last one will be a NULL) or up to and including
// the first NULL.
#ifdef UNICODE
LPTSTR fgets(LPTSTR sz, DWORD count, HANDLE fh)
{
DWORD cch;
DWORD dwFilePointer, dwBytesRead;
CHAR *AnsiString = NULL, *AnsiStringPointer, ch;
LPTSTR retval = NULL;
//
// Allocate memory for the reading the ansi string from the stream
//
if ((AnsiString = (CHAR *)LocalAlloc(LPTR, count * SIZEOF(CHAR))) == NULL) {
return(retval);
}
AnsiStringPointer = AnsiString;
// Where are we?
dwFilePointer = SetFilePointer(fh, 0, NULL, FILE_CURRENT);
// Fill the buffer.
ReadFile(fh, AnsiString, count, &dwBytesRead, NULL);
// Always null the buffer.
AnsiString[count-1] = '\0';
// Convert the Ansi String to Unicode
if (MultiByteToWideChar(
CP_ACP,
MB_PRECOMPOSED,
AnsiString,
-1,
sz,
count
) != 0) {
retval = sz;
}
// If there was an earlied null we need to puke the rest
// back in to the stream?
cch = lstrlenA(AnsiString);
if (cch != count-1)
SetFilePointer(fh, dwFilePointer+cch+1, NULL, FILE_BEGIN);
// Do Cleanup
if (AnsiString != NULL) {
LocalFree(AnsiString);
}
return retval;
}
#else
LPTSTR fgets(LPTSTR sz, WORD cb, int fh)
{
int cch;
LONG lpos;
// Where are we?
lpos = _llseek(fh, 0, 1);
// Fill the buffer.
_lread(fh, sz, cb);
// Always null the buffer.
sz[cb-1] = TEXT('\0');
// If there was an earlied null we need to puke the rest
// back in to the stream?
cch = lstrlen(sz);
if (cch != cb-1)
_llseek(fh, lpos+cch+1, 0);
return sz;
}
#endif
#endif
//---------------------------------------------------------------------------
// Put up a message box wsprintf style.
int MyMessageBox(HWND hwnd, UINT idTitle, UINT idMessage, LPCTSTR lpsz, UINT nStyle)
{
TCHAR szTempField[CCHSZNORMAL];
TCHAR szTitle[CCHSZNORMAL];
TCHAR szMessage[CCHSZNORMAL];
int iMsgResult;
if (LoadString(g_hinst, idTitle, szTitle, ARRAYSIZE(szTitle)))
{
if (LoadString(g_hinst, idMessage, szTempField, ARRAYSIZE(szTempField)))
{
if (lpsz)
wsprintf(szMessage, szTempField, (LPTSTR)lpsz);
else
lstrcpy(szMessage, szTempField);
if (hwnd)
hwnd = GetLastActivePopup(hwnd);
iMsgResult = MessageBox(hwnd, szMessage, szTitle, nStyle);
if (iMsgResult != -1)
return iMsgResult;
}
}
// Out of memory...
DebugMsg(DM_ERROR, TEXT("MMB: Out of memory.\n\r"));
return -1;
}
//-------------------------------------------------------------------------
// Replace hash characters in a string with NULLS.
void ConvertHashesToNulls(LPTSTR p)
{
while (*p)
{
if (*p == TEXT('#'))
{
*p = TEXT('\0');
// You can't do an AnsiNext on a NULL.
// NB - we know this is a single byte.
p++;
}
else
p = CharNext(p);
}
}
//-------------------------------------------------------------------------
// Copy the directory component of a path into the given buffer.
// i.e. everything after the last slash and the slash itself for everything
// but the root.
// lpszDir is assumed to be as big as lpszPath.
void Path_GetDirectory(LPCTSTR lpszPath, LPTSTR lpszDir)
{
LPTSTR lpszFileName;
UINT cb;
// The default is a null.
lpszDir[0] = TEXT('\0');
// Copy over everything but the filename.
lpszFileName = PathFindFileName(lpszPath);
cb = (UINT)(lpszFileName-lpszPath);
if (cb)
{
// REVIEW lstrcpyn seems to have a problem with a cb of 0;
lstrcpyn(lpszDir, lpszPath, cb+1);
// Remove the trailing slash if needed.
if (!PathIsRoot(lpszDir))
lpszDir[cb-1] = TEXT('\0');
}
}
//-------------------------------------------------------------------------
//
// internal CoCreateInstance.
//
// bind straight to shell232 DllGetClassObject()
// this is meant to skip all the CoCreateInstance stuff when we
// know the thing we are looking for is in shell232.dll. this also
// makes things work if the registry is messed up
//
HRESULT ICoCreateInstance(REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv)
{
LPCLASSFACTORY pcf;
HRESULT hres = SHDllGetClassObject(rclsid, &IID_IClassFactory, &pcf);
if (SUCCEEDED(hres))
{
hres = pcf->lpVtbl->CreateInstance(pcf, NULL, riid, ppv);
pcf->lpVtbl->Release(pcf);
}
return hres;
}
//-------------------------------------------------------------------------
LPTSTR _lstrcatn(LPTSTR lpszDest, LPCTSTR lpszSrc, UINT cbDest)
{
UINT i;
i = lstrlen(lpszDest);
lstrcpyn(lpszDest+i, lpszSrc, cbDest-i);
return lpszDest;
}
//-------------------------------------------------------------------------
// Simplified from shelldll. Keep sticking on numbers till the name is unique.
BOOL WINAPI MakeUniqueName(LPTSTR pszNewName, UINT cbNewName, LPCTSTR pszOldName,
UINT nStart, PFNISUNIQUE pfnIsUnique, UINT nUser, BOOL fLFN)
{
TCHAR szAddend[4];
int cbAddend;
int i;
// Is it already unique?
if ((*pfnIsUnique)(pszOldName, nUser))
{
lstrcpyn(pszNewName, pszOldName, cbNewName);
return TRUE;
}
else
{
// NB Max is 100 identically names things but we should never
// hit this as the max number of items in a progman group was 50.
for (i=nStart; i<100; i++)
{
// Generate the addend.
wsprintf(szAddend, TEXT("#%d"), i);
cbAddend = lstrlen(szAddend);
// Lotsa room?
if ((UINT)(lstrlen(pszOldName)+cbAddend+1) > cbNewName)
{
// Nope.
lstrcpyn(pszNewName, pszOldName, cbNewName);
lstrcpy(pszNewName+(cbNewName-cbAddend), szAddend);
}
else
{
// Yep.
lstrcpy(pszNewName, pszOldName);
if (!fLFN)
lstrcat(pszNewName, c_szSpace);
lstrcat(pszNewName, szAddend);
}
// Is it unique?
if ((*pfnIsUnique)(pszNewName, nUser))
{
// Yep.
return TRUE;
}
}
}
// Ooopsie.
lstrcpyn(pszNewName, pszOldName, cbNewName);
DebugMsg(DM_ERROR, TEXT("gp.mun: Unable to generate a unique name for %s."), pszOldName);
return FALSE;
}
//-------------------------------------------------------------------------
// Simplified from shell.dll (For LFN things only).
BOOL WINAPI YetAnotherMakeUniqueName(LPTSTR pszNewName, UINT cbNewName, LPCTSTR pszOldName,
PFNISUNIQUE pfnIsUnique, UINT n, BOOL fLFN)
{
BOOL fRet = FALSE;
TCHAR szTemp[MAX_PATH];
// Is given name already unique?
if ((*pfnIsUnique)(pszOldName, n))
{
// Yep,
lstrcpyn(pszNewName, pszOldName, cbNewName);
}
else
{
if (fLFN)
{
// Try "another".
LoadString(g_hinst, IDS_ANOTHER, szTemp, ARRAYSIZE(szTemp));
_lstrcatn(szTemp, pszOldName, cbNewName);
if (!(*pfnIsUnique)(szTemp, n))
{
// Nope, use the old technique of sticking on numbers.
return MakeUniqueName(pszNewName, cbNewName, pszOldName, 3, pfnIsUnique, n, FALSE);
}
else
{
// Yep.
lstrcpyn(pszNewName, szTemp, cbNewName);
}
}
else
{
// Just stick on numbers.
return MakeUniqueName(pszNewName, cbNewName, pszOldName, 2, pfnIsUnique, n, TRUE);
}
}
// Name is unique.
return TRUE;
}
//----------------------------------------------------------------------------
// Sort of a registry equivalent of the profile API's.
BOOL WINAPI Reg_Get(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPVOID pData, DWORD cbData)
{
HKEY hkeyNew;
BOOL fRet = FALSE;
DWORD dwType;
if (!GetSystemMetrics(SM_CLEANBOOT) && (RegOpenKey(hkey, pszSubKey, &hkeyNew) == ERROR_SUCCESS))
{
if (RegQueryValueEx(hkeyNew, (LPVOID)pszValue, 0, &dwType, pData, &cbData) == ERROR_SUCCESS)
{
fRet = TRUE;
}
RegCloseKey(hkeyNew);
}
return fRet;
}
//----------------------------------------------------------------------------
// Sort of a registry equivalent of the profile API's.
BOOL WINAPI Reg_Set(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, DWORD dwType,
LPVOID pData, DWORD cbData)
{
HKEY hkeyNew;
BOOL fRet = FALSE;
if (pszSubKey)
{
if (RegCreateKey(hkey, pszSubKey, &hkeyNew) == ERROR_SUCCESS)
{
if (RegSetValueEx(hkeyNew, pszValue, 0, dwType, pData, cbData) == ERROR_SUCCESS)
{
fRet = TRUE;
}
RegCloseKey(hkeyNew);
}
}
else
{
if (RegSetValueEx(hkey, pszValue, 0, dwType, pData, cbData) == ERROR_SUCCESS)
{
fRet = TRUE;
}
}
return fRet;
}
//----------------------------------------------------------------------------
BOOL WINAPI Reg_SetDWord(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, DWORD dw)
{
return Reg_Set(hkey, pszSubKey, pszValue, REG_DWORD, &dw, SIZEOF(dw));
}
//----------------------------------------------------------------------------
BOOL WINAPI Reg_GetDWord(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPDWORD pdw)
{
return Reg_Get(hkey, pszSubKey, pszValue, pdw, SIZEOF(*pdw));
}
//----------------------------------------------------------------------------
void __cdecl _Log(LPCTSTR pszMsg, ...)
{
TCHAR sz[2*MAX_PATH+40]; // Handles 2*largest path + slop for message
va_list vaListMarker;
va_start(vaListMarker, pszMsg);
if (g_hkeyGrpConv)
{
wvsprintf(sz, pszMsg, vaListMarker);
Reg_SetString(g_hkeyGrpConv, NULL, TEXT("Log"), sz);
}
}