Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

766 lines
18 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
StartD.cpp : implementation file
File History:
JonY Jan-96 created
--*/
#include "stdafx.h"
#include "Mustard.h"
#include "wizlist.h"
#include "StartD.h"
#include "Listdata.h"
#include <winreg.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
LPITEMIDLIST CreatePIDL(UINT cbSize);
LPITEMIDLIST ConcatPIDLs(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
LPITEMIDLIST Next(LPCITEMIDLIST pidl);
UINT GetSize(LPCITEMIDLIST pidl);
/////////////////////////////////////////////////////////////////////////////
// CStartD dialog
CStartD::CStartD(CWnd* pParent /*=NULL*/)
: CDialog(CStartD::IDD, pParent), m_bActive(TRUE),
m_hAccelTable(NULL)
{
//{{AFX_DATA_INIT(CStartD)
m_bStartup = TRUE;
//}}AFX_DATA_INIT
m_pFont1 = NULL;
m_pFont2 = NULL;
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
// Load the accelerator tables.
m_hAccelTable = LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ACCELERATOR1));
}
CStartD::~CStartD()
{
if (m_pFont1 != NULL) delete m_pFont1;
// if (m_pFont2 != NULL) delete m_pFont2;
}
void CStartD::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CStartD)
DDX_Control(pDX, IDC_WIZ_LIST2, m_lbWizList2);
DDX_Control(pDX, IDC_WIZ_LIST, m_lbWizList);
DDX_Check(pDX, IDC_STARTUP, m_bStartup);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CStartD, CDialog)
//{{AFX_MSG_MAP(CStartD)
ON_WM_PAINT()
ON_BN_CLICKED(IDC_QUIT_BUTTON, OnQuitButton)
ON_WM_ACTIVATEAPP()
ON_WM_QUERYDRAGICON()
ON_WM_MOUSEMOVE()
//}}AFX_MSG_MAP
ON_COMMAND(ID_ACCEL_GROUP, OnAccelGroup)
ON_COMMAND(ID_ACCEL_MODEM, OnAccelModem)
ON_COMMAND(ID_ACCEL_NETWORK, OnAccelNetwork)
ON_COMMAND(ID_ACCEL_PRINT, OnAccelPrint)
ON_COMMAND(ID_ACCEL_SHARE, OnAccelShare)
ON_COMMAND(ID_ACCEL_USER, OnAccelUser)
ON_COMMAND(ID_ACCEL_PROGRAMS, OnAccelPrograms)
ON_COMMAND(ID_ACCEL_LICENSE, OnAccelLicense)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CStartD message handlers
BOOL CStartD::OnInitDialog()
{
CDialog::OnInitDialog();
// tell the two sides about each other
m_lbWizList2.m_pOtherGuy = &m_lbWizList;
m_lbWizList.m_pOtherGuy = &m_lbWizList2;
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// font information for the headings
m_pFont1 = new CFont;
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT)); // Clear out structure.
CString fontname;
fontname.LoadString(IDS_FONT_NAME);
_tcscpy(lf.lfFaceName, fontname);
CString fontheight;
fontheight.LoadString(IDS_BIGFONT_SIZE);
lf.lfHeight = (LONG)_tcstoul(fontheight, NULL, 10);
lf.lfWeight = 700;
m_pFont1->CreateFontIndirect(&lf); // Create the font.
CString cs;
cs.LoadString(IDS_CAPTION);
CWnd* pWnd = GetDlgItem(IDC_STATIC1);
pWnd->SetWindowText(cs);
pWnd->SetFont(m_pFont1);
CMustardApp* pApp = (CMustardApp*)AfxGetApp();
// get install dir
TCHAR pSysDir[256];
UINT nLen = GetWindowsDirectory(pSysDir, 256);
if (*(pSysDir + nLen) != '\\') _tcscat(pSysDir, L"\\");
_tcscat(pSysDir, L"SYSTEM32\\");
// Add User
CItemData* pData = new CItemData;
pData->csName.LoadString(IDS_USER_NAME);// = L"Add User";
pData->csDesc.LoadString(IDS_USER_DESC);// = L"Add User";
pData->csAppStart1 = pSysDir + CString(L"addusrw.exe");
pData->csAppStart2 = L"";
GetIconIndices(pData->csAppStart1, &pData->hIcon, &pData->hSelIcon);
m_lbWizList.InsertString(0, _T(""));
m_lbWizList.SetItemData(0, (LONG_PTR)pData);
// Group Creation
pData = new CItemData;
pData->csName.LoadString(IDS_GROUP_NAME);// = L"Group Creation";
pData->csDesc.LoadString(IDS_GROUP_DESC);// = L"Group Creation";
pData->csAppStart1 = pSysDir + CString(L"addgrpw.exe");
pData->csAppStart2 = L"";
GetIconIndices(pData->csAppStart1, &pData->hIcon, &pData->hSelIcon);
m_lbWizList.InsertString(1, _T(""));
m_lbWizList.SetItemData(1, (LONG_PTR)pData);
// Share Publishing / folder security
pData = new CItemData;
pData->csName.LoadString(IDS_SHRPUB_NAME);// = L"Share Publishing";
pData->csDesc.LoadString(IDS_SHRPUB_DESC);// = L"Share Publishing ";
pData->csAppStart1 = pSysDir + CString(L"shrpubw.exe");
pData->csAppStart2 = L"";
GetIconIndices(pData->csAppStart1, &pData->hIcon, &pData->hSelIcon);
m_lbWizList.InsertString(2, _T(""));
m_lbWizList.SetItemData(2, (LONG_PTR)pData);
// printer
pData = new CItemData;
pData->csName.LoadString(IDS_PRINT_NAME);// = L"Add Printer";
pData->csDesc.LoadString(IDS_PRINT_DESC);// = L"Print manager";
pData->csAppStart1 = L"";
pData->csAppStart2 = L"";
GetCPLIcon(IDS_ADD_PRINTER, &pData->hIcon, &pData->hSelIcon, TRUE);
m_lbWizList.InsertString(3, _T(""));
m_lbWizList.SetItemData(3, (LONG_PTR)pData);
// Add/remove software
pData = new CItemData;
pData->csName.LoadString(IDS_ARS_NAME);// = L"Add/Remove software";
pData->csDesc.LoadString(IDS_ARS_DESC);// = L"Add/Remove software";
pData->csAppStart1 = L"control.exe";
pData->csAppStart2 = L"appwiz.cpl";
GetCPLIcon(IDS_APPWIZ, &pData->hIcon, &pData->hSelIcon);
m_lbWizList2.InsertString(0, _T(""));
m_lbWizList2.SetItemData(0, (LONG_PTR)pData);
// modem
pData = new CItemData;
pData->csName.LoadString(IDS_MODEM_NAME);// = L"Add Modem";
pData->csDesc.LoadString(IDS_MODEM_DESC);// = L"Modems, ports, etc";
pData->csAppStart1 = L"control.exe";
pData->csAppStart2 = L"modem.cpl";
GetCPLIcon(IDS_MODEM, &pData->hIcon, &pData->hSelIcon);
m_lbWizList2.InsertString(1, _T(""));
m_lbWizList2.SetItemData(1, (LONG_PTR)pData);
// Workstation installation
pData = new CItemData;
pData->csName.LoadString(IDS_WKSSETUP_NAME);// = L"Workstation Installation";
pData->csDesc.LoadString(IDS_WKSSETUP_DESC);// = L"Workstation Installation";
pData->csAppStart1 = pSysDir + CString(L"ncadmin.exe");
pData->csAppStart2 = L"";
GetIconIndices(pData->csAppStart1, &pData->hIcon, &pData->hSelIcon);
m_lbWizList2.InsertString(2, _T(""));
m_lbWizList2.SetItemData(2, (LONG_PTR)pData);
// license compliancy
pData = new CItemData;
pData->csName.LoadString(IDS_FDS_NAME);
pData->csDesc.LoadString(IDS_FDS_DESC);
pData->csAppStart1 = pSysDir + CString(L"lcwiz.exe");
pData->csAppStart2 = L"";
GetIconIndices(pData->csAppStart1, &pData->hIcon, &pData->hSelIcon);
m_lbWizList2.InsertString(3, _T(""));
m_lbWizList2.SetItemData(3, (LONG_PTR)pData);
// check the registry to see if we are loaded automatically at startup
DWORD dwRet;
HKEY hKey;
DWORD cbProv = 0;
TCHAR* lpProv = NULL;
dwRet = RegOpenKey(HKEY_CURRENT_USER,
TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows"), &hKey );
TCHAR* lpStartup = NULL;
if ((dwRet = RegQueryValueEx( hKey, TEXT("run"), NULL, NULL, NULL, &cbProv )) == ERROR_SUCCESS)
{
lpStartup = (TCHAR*)malloc(cbProv);
if (lpStartup == NULL)
{
AfxMessageBox(IDS_GENERIC_NO_HEAP, MB_ICONEXCLAMATION);
ExitProcess(1);
}
dwRet = RegQueryValueEx( hKey, TEXT("run"), NULL, NULL, (LPBYTE) lpStartup, &cbProv );
// see if we are included
if (_tcsstr(lpStartup, L"wizmgr.exe") != NULL)
m_bStartup = TRUE;
else m_bStartup = FALSE;
UpdateData(FALSE);
}
free(lpStartup);
RegCloseKey(hKey);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CStartD::OnPaint()
{
CPaintDC dc(this); // device context for painting
if (IsIconic())
{
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CPen pBlackPen(PS_SOLID, 1, RGB(0,0,0));
CPen* pOriginalPen = (CPen*)dc.SelectObject(pBlackPen);
dc.MoveTo(596, 77);
dc.LineTo(2, 77);
dc.LineTo(2, 362);
CPen pLtGreyPen(PS_SOLID, 1, RGB(192, 192, 192));
dc.SelectObject(pLtGreyPen);
dc.LineTo(596, 362);
dc.LineTo(596, 77);
CPen pDkGreyPen(PS_SOLID, 1, RGB(128, 128, 128));
dc.SelectObject(pDkGreyPen);
dc.MoveTo(597, 76);
dc.LineTo(1, 76);
dc.LineTo(1, 363);
CPen pWhitePen(PS_SOLID, 1, RGB(255, 255, 255));
dc.SelectObject(pWhitePen);
dc.MoveTo(2, 363);
dc.LineTo(598, 363);
dc.LineTo(598, 76);
}
// Do not call CDialog::OnPaint() for painting messages
}
HCURSOR CStartD::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CStartD::OnQuitButton()
{
CItemData* pData;
ULONG_PTR lData;
USHORT us = 0;
while((lData = m_lbWizList.GetItemData(us)) != LB_ERR)
{
pData = (CItemData*)lData;
delete pData;
m_lbWizList.DeleteString(us);
}
while((lData = m_lbWizList2.GetItemData(us)) != LB_ERR)
{
pData = (CItemData*)lData;
delete pData;
m_lbWizList2.DeleteString(us);
}
// set the reg value for startup
UpdateData(TRUE);
DWORD dwRet;
HKEY hKey;
DWORD cbProv = 0;
TCHAR* lpProv = NULL;
dwRet = RegOpenKey(HKEY_CURRENT_USER,
TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows"), &hKey );
TCHAR* lpStartup = NULL;
if ((dwRet = RegQueryValueEx( hKey, TEXT("run"), NULL, NULL, NULL, &cbProv )) == ERROR_SUCCESS)
{
lpStartup = (TCHAR*)malloc(cbProv);
if (lpStartup == NULL)
{
AfxMessageBox(IDS_GENERIC_NO_HEAP, MB_ICONEXCLAMATION);
ExitProcess(1);
}
dwRet = RegQueryValueEx( hKey, TEXT("run"), NULL, NULL, (LPBYTE) lpStartup, &cbProv );
// see if we are included
TCHAR* pPos;
if ((pPos = _tcsstr(lpStartup, L"wizmgr.exe")) != NULL) // included
{
if (!m_bStartup) // don't want to be
{
UINT nPos = (UINT)(pPos - lpStartup);
CString csCurrentValue = lpStartup;
CString csNewVal = csCurrentValue.Left(nPos);
csNewVal += csCurrentValue.Right(csCurrentValue.GetLength() - nPos - _tcslen(L"wizmgr.exe"));
RegSetValueEx(hKey,
TEXT("run"),
0,
REG_SZ,
(BYTE *)csNewVal.GetBuffer(csNewVal.GetLength()),
csNewVal.GetLength() * sizeof(TCHAR));
}
}
else // not included
{
if (m_bStartup) // want to be
{
CString csNewVal = lpStartup;
csNewVal += L" wizmgr.exe";
RegSetValueEx(hKey,
TEXT("run"),
0,
REG_SZ,
(BYTE *)csNewVal.GetBuffer(csNewVal.GetLength()),
csNewVal.GetLength() * sizeof(TCHAR));
}
}
UpdateData(FALSE);
}
free(lpStartup);
RegCloseKey(hKey);
EndDialog(1);
}
void CStartD::OnActivateApp(BOOL bActive, HTASK hTask)
{
CDialog::OnActivateApp(bActive, hTask);
m_bActive = bActive;
// Remove the selections from the list boxes if
// the application is being deactivated.
if (!m_bActive)
{
m_lbWizList.SetCurSel(-1);
m_lbWizList2.SetCurSel(-1);
}
}
void CStartD::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bActive)
{
// Remove the selection from the list boxes if
// the mouse has moved outside of them.
m_lbWizList.SetCurSel(-1);
m_lbWizList2.SetCurSel(-1);
}
CDialog::OnMouseMove(nFlags, point);
}
BOOL CStartD::PreTranslateMessage(MSG* pMsg)
{
if (m_hAccelTable != NULL)
{
if (!TranslateAccelerator(GetSafeHwnd(), m_hAccelTable, pMsg) )
return CDialog::PreTranslateMessage(pMsg);
else
return TRUE;
}
return CDialog::PreTranslateMessage(pMsg);
}
void CStartD::HandleAccelerator1(USHORT nIndex)
{
// Change the selection in the listbox and
// make it think it's been clicked.
// Remove the selection from the other listbox, too.
m_lbWizList.SetCurSel(nIndex);
m_lbWizList2.SetCurSel(-1);
m_lbWizList.PostMessage(WM_LBUTTONDOWN);
}
void CStartD::HandleAccelerator2(USHORT nIndex)
{
m_lbWizList2.SetCurSel(nIndex);
m_lbWizList.SetCurSel(-1);
m_lbWizList2.PostMessage(WM_LBUTTONDOWN);
}
void CStartD::OnAccelUser()
{
HandleAccelerator1(ACCEL_USER);
}
void CStartD::OnAccelGroup()
{
HandleAccelerator1(ACCEL_GROUP);
}
void CStartD::OnAccelShare()
{
HandleAccelerator1(ACCEL_SHARE);
}
void CStartD::OnAccelPrint()
{
HandleAccelerator1(ACCEL_PRINT);
}
void CStartD::OnAccelPrograms()
{
HandleAccelerator2(ACCEL_PROGRAMS);
}
void CStartD::OnAccelModem()
{
HandleAccelerator2(ACCEL_MODEM);
}
void CStartD::OnAccelNetwork()
{
HandleAccelerator2(ACCEL_NETWORK);
}
void CStartD::OnAccelLicense()
{
HandleAccelerator2(ACCEL_LICENSE);
}
void CStartD::GetIconIndices(LPCTSTR pszPathName, HICON* hiNormal, HICON* hiSelected)
{
SHFILEINFO sfi;
// Get the index for the normal icon.
SHGetFileInfo(pszPathName, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON);
*hiNormal = sfi.hIcon;
// Get the index for the selected icon.
SHGetFileInfo(pszPathName, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SELECTED);
*hiSelected = sfi.hIcon;
}
void CStartD::GetCPLIcon(UINT pszCplItem, HICON* hiNormal, HICON* hiSelected, BOOL bPrinters)
{
LPSHELLFOLDER pshf, pshfCPLItem;
LPITEMIDLIST pidlCPLFolder, pidlCPLObj;
LPMALLOC pMalloc;
HRESULT hr;
// Get a pointer to the shell's IMalloc interface.
hr = SHGetMalloc(&pMalloc);
if (SUCCEEDED(hr) && pMalloc) // JonN 01/26/00: PREFIX bug 445827
{
// Get a pointer to the shell's IShellFolder interface.
hr = SHGetDesktopFolder(&pshf);
if (SUCCEEDED(hr))
{
// Get a PIDL for the specified folder.
if (!bPrinters)
hr = SHGetSpecialFolderLocation(GetSafeHwnd(), CSIDL_CONTROLS, &pidlCPLFolder);
else
hr = SHGetSpecialFolderLocation(GetSafeHwnd(), CSIDL_PRINTERS, &pidlCPLFolder);
if (SUCCEEDED(hr))
{
hr = pshf->BindToObject(pidlCPLFolder, NULL, IID_IShellFolder, (LPVOID*)&pshfCPLItem);
if (SUCCEEDED(hr))
GetCPLObject(pszCplItem, pshfCPLItem, &pidlCPLObj, hiNormal, hiSelected, pidlCPLFolder);
}
pMalloc->Free(pidlCPLFolder);
}
pMalloc->Release();
}
}
HRESULT CStartD::GetCPLObject(UINT pszCplItem, LPSHELLFOLDER pshf, LPITEMIDLIST* ppidl, HICON* hiNormal, HICON* hiSelected, LPITEMIDLIST pParentPIDL)
{
HRESULT hr;
LPENUMIDLIST pEnum;
LPMALLOC pMalloc;
// Get a pointer to the shell's IMalloc interface.
hr = SHGetMalloc(&pMalloc);
if (SUCCEEDED(hr))
{
// Get a pointer to the folder's IEnumIDList interface.
hr = pshf->EnumObjects(GetSafeHwnd(), SHCONTF_NONFOLDERS, &pEnum);
if (SUCCEEDED(hr))
{
STRRET str;
CString strCPLObj, strEnumObj;
// Load the display name for the control panel object.
strCPLObj.LoadString(pszCplItem);
// Enumerate the objects in the control panel folder.
while (pEnum->Next(1, ppidl, NULL) == NOERROR)
{
// Get the display name for the object.
hr = pshf->GetDisplayNameOf((LPCITEMIDLIST)*ppidl, SHGDN_INFOLDER, &str);
if (SUCCEEDED(hr))
{
// Copy the display name to the strEnumObj string.
switch (str.uType)
{
case STRRET_CSTR:
strEnumObj = str.cStr;
break;
case STRRET_OFFSET:
char pStr[255];
strcpy(pStr, (LPCSTR)(((UINT_PTR)*ppidl) + str.uOffset));
TCHAR wStr[255];
mbstowcs(wStr, pStr, 255);
strEnumObj = wStr;
break;
case STRRET_WSTR:
strEnumObj = str.pOleStr;
break;
case 0x04:
strEnumObj = (LPCTSTR)(((UINT_PTR)*ppidl) + str.uOffset);
break;
case 0x05:
{
TCHAR pStr[255];
memcpy(pStr, str.cStr, 255);
strEnumObj = pStr;
break;
}
default:
strEnumObj.Empty();
}
// If we found the correct object, exit the loop.
if (strCPLObj == strEnumObj)
{
LPITEMIDLIST pBigPIDL = ConcatPIDLs(pParentPIDL, *ppidl);
SHFILEINFO sfi;
// Get the index for the normal icon.
SHGetFileInfo((LPCTSTR)pBigPIDL, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_PIDL);
*hiNormal = sfi.hIcon;
// Get the index for the selected icon.
SHGetFileInfo((LPCTSTR)pBigPIDL, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SELECTED | SHGFI_PIDL);
*hiSelected = sfi.hIcon;
}
// Free the PIDL returned by IEnumIDList::Next().
pMalloc->Free(*ppidl);
if (FAILED(hr))
{
TRACE(_T("IMalloc::Free failed.\n"));
break;
}
}
else
{
TRACE(_T("IShellFolder::GetDisplayNameOf failed.\n"));
}
}
// Release the IEnumIDList pointer.
pEnum->Release();
}
else
{
TRACE(_T("IShellFolder::EnumObjects failed.\n"));
}
// Release the IMalloc pointer.
pMalloc->Release();
}
return hr;
}
/****************************************************************************
*
* FUNCTION: Create(UINT cbSize)
*
* PURPOSE: Allocates a PIDL
*
****************************************************************************/
LPITEMIDLIST CreatePIDL(UINT cbSize)
{
LPMALLOC lpMalloc;
HRESULT hr;
LPITEMIDLIST pidl=NULL;
hr=SHGetMalloc(&lpMalloc);
if (FAILED(hr))
return 0;
pidl=(LPITEMIDLIST)lpMalloc->Alloc(cbSize);
if (pidl)
memset(pidl, 0, cbSize); // zero-init for external task alloc
if (lpMalloc) lpMalloc->Release();
return pidl;
}
/****************************************************************************
*
* FUNCTION: ConcatPidls(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
*
* PURPOSE: Concatenates two PIDLs
*
****************************************************************************/
LPITEMIDLIST ConcatPIDLs(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
LPITEMIDLIST pidlNew;
UINT cb1;
UINT cb2;
if (pidl1) //May be NULL
cb1 = GetSize(pidl1) - sizeof(pidl1->mkid.cb);
else
cb1 = 0;
cb2 = GetSize(pidl2);
pidlNew = CreatePIDL(cb1 + cb2);
if (pidlNew)
{
if (pidl1)
memcpy(pidlNew, pidl1, cb1);
memcpy(((LPSTR)pidlNew) + cb1, pidl2, cb2);
}
return pidlNew;
}
/****************************************************************************
*
* FUNCTION: Next(LPCITEMIDLIST pidl)
*
* PURPOSE: Gets the next PIDL in the list
*
****************************************************************************/
LPITEMIDLIST Next(LPCITEMIDLIST pidl)
{
LPSTR lpMem=(LPSTR)pidl;
lpMem+=pidl->mkid.cb;
return (LPITEMIDLIST)lpMem;
}
/****************************************************************************
*
* FUNCTION: GetSize(LPCITEMIDLIST pidl)
*
* PURPOSE: Gets the size of the PIDL
*
****************************************************************************/
UINT GetSize(LPCITEMIDLIST pidl)
{
UINT cbTotal = 0;
if (pidl)
{
cbTotal += sizeof(pidl->mkid.cb); // Null terminator
while (pidl->mkid.cb)
{
cbTotal += pidl->mkid.cb;
pidl = Next(pidl);
}
}
return cbTotal;
}