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.
 
 
 
 
 
 

902 lines
24 KiB

/******************************************************************************
Source File: ICC Profile.CPP
This implements the class we use to encapsulate everything we will ever care
to know about a profile, including the classes we need to support
associations and the like.
Copyright (c) 1996, 1997 by Microsoft Corporation. All Rights Reserved.
A Pretty Penny Enterprises Production
Change History:
10-31-96 A-RobKj (Pretty Penny Enterprises) began encapsulating it
12-04-96 A-RobKj Added the CProfileArray and CAllDeviceList classes
12-13-96 A-RobKj Modified for faster operation (more lazy evaluation,
and common DLL-wide database for installation checks)
Also moved CDeviceList derived classes to the header, so
I can use them other places, as well...
01-07-97 [email protected] Fixed CProfileArray::Empty- wasn't setting Next
object pointer to NULL after deleting said object (Fixed GP fault).
01-08-97 [email protected] Modified printer enumeration routine to only
enumerate color models (uses Global utility function).
******************************************************************************/
#include "ICMUI.H"
#include <shlobj.h>
#include "shellext.h"
#include "..\mscms\sti.h"
typedef HRESULT (__stdcall *PFNSTICREATEINSTANCE)(HINSTANCE, DWORD, PSTI*, LPDWORD);
TCHAR gszStiDll[] = __TEXT("sti.dll");
char gszStiCreateInstance[] = "StiCreateInstance";
// Printer DeviceEnumeration method
void CPrinterList::Enumerate() {
#if !defined(_WIN95_) // CPrinterList::Enumetate()
// Enumerate all local printers
DWORD dwcNeeded, dwcReturned;
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &dwcNeeded,
&dwcReturned);
union {
PBYTE pBuff;
PPRINTER_INFO_4 ppi4;
};
pBuff = new BYTE[dwcNeeded];
while (pBuff && !EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, pBuff,
dwcNeeded, &dwcNeeded, &dwcReturned) &&
GetLastError() == ERROR_MORE_DATA) {
delete [] pBuff;
pBuff = new BYTE[dwcNeeded];
}
if (pBuff) {
for (unsigned u = 0; u < dwcReturned; u++)
if (CGlobals::ThisIsAColorPrinter(ppi4[u].pPrinterName)) {
m_csaDeviceNames.Add(ppi4[u].pPrinterName);
m_csaDisplayNames.Add(ppi4[u].pPrinterName);
}
delete [] pBuff;
}
// Now, enumerate all the connected printers
EnumPrinters(PRINTER_ENUM_CONNECTIONS, NULL, 4, NULL, 0, &dwcNeeded,
&dwcReturned);
pBuff = new BYTE[dwcNeeded];
while (pBuff && !EnumPrinters(PRINTER_ENUM_CONNECTIONS, NULL, 4, pBuff,
dwcNeeded, &dwcNeeded, &dwcReturned) &&
GetLastError() == ERROR_MORE_DATA) {
delete [] pBuff;
pBuff = new BYTE[dwcNeeded];
}
if (!pBuff)
return;
for (unsigned u = 0; u < dwcReturned; u++)
if (CGlobals::ThisIsAColorPrinter(ppi4[u].pPrinterName)) {
m_csaDeviceNames.Add(ppi4[u].pPrinterName);
m_csaDisplayNames.Add(ppi4[u].pPrinterName);
}
delete [] pBuff;
#else
// Enumerate all local printers
DWORD dwcNeeded, dwcReturned;
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &dwcNeeded,
&dwcReturned);
union {
PBYTE pBuff;
PPRINTER_INFO_5 ppi5;
};
pBuff = new BYTE[dwcNeeded];
while (pBuff && !EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, pBuff,
dwcNeeded, &dwcNeeded, &dwcReturned) &&
GetLastError() == ERROR_MORE_DATA) {
delete [] pBuff;
pBuff = new BYTE[dwcNeeded];
}
if (pBuff) {
for (unsigned u = 0; u < dwcReturned; u++) {
if (CGlobals::ThisIsAColorPrinter(ppi5[u].pPrinterName)) {
m_csaDeviceNames.Add(ppi5[u].pPrinterName);
m_csaDisplayNames.Add(ppi5[u].pPrinterName);
}
}
delete [] pBuff;
}
#endif
}
// Printer Name Validity Check
BOOL CPrinterList::IsValidDeviceName(LPCTSTR lpstrRef) {
if (!lpstrRef) return FALSE;
if (!Count())
Enumerate();
for (unsigned u = 0; u < Count(); u++)
if (!lstrcmpi(m_csaDeviceNames[u], lpstrRef))
break;
return u < Count();
}
// Private monitor enumeration function- note this is ANSI only...
extern "C" BOOL WINAPI EnumerateMonitors(LPBYTE pBuffer, PDWORD pdwcbNeeded,
PDWORD pdwcReturned);
// CMonitor class enumerator
void CMonitorList::Enumerate() {
ULONG ulSerialNumber = 1;
ULONG ulDeviceIndex = 0;
DISPLAY_DEVICE ddPriv;
ddPriv.cb = sizeof(ddPriv);
// Enumurate display adaptor on the system.
while (EnumDisplayDevices(NULL, ulDeviceIndex, &ddPriv, 0))
{
ULONG ulMonitorIndex = 0;
DISPLAY_DEVICE ddPrivMonitor;
ddPrivMonitor.cb = sizeof(ddPrivMonitor);
// then, enumurate monitor device, attached the display adaptor.
while (EnumDisplayDevices(ddPriv.DeviceName, ulMonitorIndex, &ddPrivMonitor, 0))
{
TCHAR DisplayNameBuf[256]; // number: devicename - 256 is good enough.
// Insert PnP id as device name.
m_csaDeviceNames.Add(ddPrivMonitor.DeviceID);
// If this is primary display device, remember it.
if (ddPriv.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
{
m_csPrimaryDeviceName = ddPrivMonitor.DeviceID;
}
// Build display name.
wsprintf(DisplayNameBuf,TEXT("%d. %s"),ulSerialNumber,ddPrivMonitor.DeviceString);
m_csaDisplayNames.Add(DisplayNameBuf);
ulMonitorIndex++;
ulSerialNumber++;
ddPrivMonitor.cb = sizeof(ddPrivMonitor);
}
ulDeviceIndex++;
ddPriv.cb = sizeof(ddPriv);
}
}
// Monitor Name Validity Check
BOOL CMonitorList::IsValidDeviceName(LPCTSTR lpstrRef) {
if (!lpstrRef) return FALSE;
if (!Count())
Enumerate();
for (unsigned u = 0; u < Count(); u++)
if (!lstrcmpi(m_csaDeviceNames[u], lpstrRef))
break;
return u < Count();
}
LPCSTR CMonitorList::DeviceNameToDisplayName(LPCTSTR lpstrRef) {
if (!lpstrRef) return NULL;
if (!Count())
Enumerate();
for (unsigned u = 0; u < Count(); u++)
if (!lstrcmpi(m_csaDeviceNames[u], lpstrRef))
return (LPCSTR)(m_csaDisplayNames[u]);
return NULL;
}
// Scanner DeviceEnumeration method
void CScannerList::Enumerate() {
PFNSTICREATEINSTANCE pStiCreateInstance;
PSTI pSti = NULL;
PSTI_DEVICE_INFORMATION pDevInfo;
PVOID pBuffer = NULL;
HINSTANCE hModule;
HRESULT hres;
DWORD i, dwItemsReturned;
#ifndef UNICODE
char szName[256];
#endif
if (!(hModule = LoadLibrary(gszStiDll)))
{
_RPTF1(_CRT_WARN, "Error loading sti.dll: %d\n",
GetLastError());
return;
}
if (!(pStiCreateInstance = (PFNSTICREATEINSTANCE)GetProcAddress(hModule, gszStiCreateInstance)))
{
_RPTF0(_CRT_WARN, "Error getting proc StiCreateInstance\n");
goto EndEnumerate;
}
hres = (*pStiCreateInstance)(GetModuleHandle(NULL), STI_VERSION, &pSti, NULL);
if (FAILED(hres))
{
_RPTF1(_CRT_WARN, "Error creating sti instance: %d\n", hres);
goto EndEnumerate;
}
hres = pSti->GetDeviceList(0, 0, &dwItemsReturned, &pBuffer);
if (FAILED(hres) || !pBuffer)
{
_RPTF0(_CRT_WARN, "Error getting scanner devices\n");
goto EndEnumerate;
}
pDevInfo = (PSTI_DEVICE_INFORMATION) pBuffer;
for (i=0; i<dwItemsReturned; i++, pDevInfo++)
{
#ifndef UNICODE
DWORD dwLen; // length of Ansi string
BOOL bUsedDefaultChar;
dwLen = (lstrlenW(pDevInfo->pszLocalName) + 1) * sizeof(char);
//
// Convert Unicode name to Ansi
//
if (WideCharToMultiByte(CP_ACP, 0, pDevInfo->szDeviceInternalName, -1, szName,
dwLen, NULL, &bUsedDefaultChar) && ! bUsedDefaultChar)
{
m_csaDeviceNames.Add(szName);
}
else
{
_RPTF0(_CRT_WARN, "Error converting internalName to Unicode name\n");
}
if (WideCharToMultiByte(CP_ACP, 0, pDevInfo->pszLocalName, -1, szName,
dwLen, NULL, &bUsedDefaultChar) && ! bUsedDefaultChar)
{
m_csaDisplayNames.Add(szName);
}
else
{
_RPTF0(_CRT_WARN, "Error converting deviceName to Unicode name\n");
}
#else
m_csaDeviceNames.Add(pDevInfo->szDeviceInternalName);
m_csaDisplayNames.Add(pDevInfo->pszLocalName);
#endif
}
EndEnumerate:
if (pBuffer)
{
LocalFree(pBuffer);
}
if (pSti)
{
pSti->Release();
}
if (hModule)
{
FreeLibrary(hModule);
}
return;
}
// Scanner Name Validity Check
BOOL CScannerList::IsValidDeviceName(LPCTSTR lpstrRef) {
if (!lpstrRef) return FALSE;
if (!Count())
Enumerate();
for (unsigned u = 0; u < Count(); u++)
if (!lstrcmpi(m_csaDeviceNames[u], lpstrRef))
break;
return u < Count();
}
// CAllDeviceList class enumerator
void CAllDeviceList::Enumerate() {
CMonitorList cml;
CPrinterList cpl;
CScannerList csl;
cml.Enumerate();
cpl.Enumerate();
csl.Enumerate();
for (unsigned u = 0; u < cpl.Count(); u++) {
m_csaDeviceNames.Add(cpl.DeviceName(u));
m_csaDisplayNames.Add(cpl.DisplayName(u));
}
for (u = 0; u < cml.Count(); u++) {
m_csaDeviceNames.Add(cml.DeviceName(u));
m_csaDisplayNames.Add(cml.DisplayName(u));
}
for (u = 0; u < csl.Count(); u++) {
m_csaDeviceNames.Add(csl.DeviceName(u));
m_csaDisplayNames.Add(csl.DisplayName(u));
}
}
// Device Name Validity Check
BOOL CAllDeviceList::IsValidDeviceName(LPCTSTR lpstrRef) {
if (!lpstrRef) return FALSE;
if (!Count())
Enumerate();
for (unsigned u = 0; u < Count(); u++)
if (!lstrcmpi(m_csaDeviceNames[u], lpstrRef))
break;
return u < Count();
}
// CProfile member functions
// The following static functions fills the appropriate array using the
// profiles that match the search criteria goven.
void CProfile::Enumerate(ENUMTYPE& et, CStringArray& csaList) {
// Enumerate the existing profiles
DWORD dwBuffer =0, dwcProfiles;
csaList.Empty();
EnumColorProfiles(NULL, &et, NULL, &dwBuffer, &dwcProfiles);
if (!dwBuffer) {
_RPTF2(_CRT_WARN,
"CProfile::Enumerate(String)- empty list- dwBuffer %d Error %d\n",
dwBuffer, GetLastError());
return;
}
union {
PBYTE pbBuffer;
PTSTR pstrBuffer;
};
pbBuffer = new BYTE[dwBuffer];
if (pbBuffer) {
if (EnumColorProfiles(NULL, &et, pbBuffer, &dwBuffer, &dwcProfiles)) {
for (PTSTR pstrMe = pstrBuffer;
dwcProfiles--;
pstrMe += 1 + lstrlen(pstrMe)) {
_RPTF1(_CRT_WARN, "CProfile::Enumerate(String) %s found\n",
pstrMe);
csaList.Add(pstrMe);
}
}
delete [] pbBuffer;
}
}
void CProfile::Enumerate(ENUMTYPE& et, CStringArray& csaList, CStringArray& csaDesc) {
// Enumerate the existing profiles
DWORD dwBuffer =0, dwcProfiles;
csaList.Empty();
EnumColorProfiles(NULL, &et, NULL, &dwBuffer, &dwcProfiles);
if (!dwBuffer) {
_RPTF2(_CRT_WARN,
"CProfile::Enumerate(String)- empty list- dwBuffer %d Error %d\n",
dwBuffer, GetLastError());
return;
}
union {
PBYTE pbBuffer;
PTSTR pstrBuffer;
};
pbBuffer = new BYTE[dwBuffer];
if (pbBuffer) {
if (EnumColorProfiles(NULL, &et, pbBuffer, &dwBuffer, &dwcProfiles)) {
for (PTSTR pstrMe = pstrBuffer;
dwcProfiles--;
pstrMe += 1 + lstrlen(pstrMe)) {
_RPTF1(_CRT_WARN, "CProfile::Enumerate(String) %s found\n",
pstrMe);
CProfile cp(pstrMe);
if (cp.IsValid()) {
CString csDescription = cp.TagContents('desc', 4);
if (csDescription.IsEmpty()) {
csaDesc.Add(pstrMe);
} else {
csaDesc.Add((LPTSTR)csDescription);
}
csaList.Add(pstrMe);
}
}
}
delete [] pbBuffer;
}
}
void CProfile::Enumerate(ENUMTYPE& et, CProfileArray& cpaList) {
// Enumerate the existing profiles
DWORD dwBuffer = 0, dwcProfiles;
cpaList.Empty();
EnumColorProfiles(NULL, &et, NULL, &dwBuffer, &dwcProfiles);
if (!dwBuffer) {
_RPTF2(_CRT_WARN,
"CProfile::Enumerate(Profile)- empty list- dwBuffer %d Error %d\n",
dwBuffer, GetLastError());
return;
}
union {
PBYTE pbBuffer;
PTSTR pstrBuffer;
};
pbBuffer = new BYTE[dwBuffer];
if (pbBuffer) {
if (EnumColorProfiles(NULL, &et, pbBuffer, &dwBuffer, &dwcProfiles)) {
for (PTSTR pstrMe = pstrBuffer;
dwcProfiles--;
pstrMe += 1 + lstrlen(pstrMe)) {
_RPTF1(_CRT_WARN, "CProfile::Enumerate(Profile) %s added\n",
pstrMe);
cpaList.Add(pstrMe);
}
}
delete [] pbBuffer;
}
}
// This retrieves the color directory name. Since it is a const, we whouldn't
// be calling it too often...
const CString CProfile::ColorDirectory() {
TCHAR acDirectory[MAX_PATH];
DWORD dwccDir = MAX_PATH;
GetColorDirectory(NULL, acDirectory, &dwccDir);
return acDirectory;
}
// This checks for profile installation
void CProfile::InstallCheck() {
// Enumerate the existing profiles, so we can see if this one's been
// installed, already.
ENUMTYPE et = {sizeof (ENUMTYPE), ENUM_TYPE_VERSION, 0, NULL};
CStringArray csaWork;
Enumerate(et, csaWork);
for (unsigned u = 0; u < csaWork.Count(); u++)
if (!lstrcmpi(csaWork[u].NameOnly(), m_csName.NameOnly()))
break;
m_bIsInstalled = u < csaWork.Count();
m_bInstallChecked = TRUE;
}
// This Checks for Associated Devices
void CProfile::AssociationCheck() {
m_bAssociationsChecked = TRUE;
// If the profile isn't installed, associations are moot...
if (!IsInstalled())
return;
// The final step is to build a list of associations
ENUMTYPE et = {sizeof (ENUMTYPE), ENUM_TYPE_VERSION, ET_DEVICENAME};
CStringArray csaWork;
for (unsigned u = 0; u < DeviceCount(); u++) {
et.pDeviceName = m_pcdlClass -> DeviceName(u);
Enumerate(et, csaWork);
// We track associations by index into the total device list...
for (unsigned uProfile = 0; uProfile < csaWork.Count(); uProfile++)
if (!lstrcmpi(csaWork[uProfile].NameOnly(), m_csName.NameOnly())){
m_cuaAssociation.Add(u); // Found one!
break;
}
}
}
// This determines the device list of related class...
void CProfile::DeviceCheck() {
// Enumerate the available devices of this type in the csaDevice Array
m_pcdlClass -> Enumerate();
m_bDevicesChecked = TRUE;
}
// Class constructor
CProfile::CProfile(LPCTSTR lpstrTarget) {
_ASSERTE(lpstrTarget && *lpstrTarget);
m_pcdlClass = NULL;
m_bIsInstalled = FALSE;
m_bInstallChecked = FALSE;
m_bDevicesChecked = FALSE;
m_bAssociationsChecked = FALSE;
// First, let's make sure it's the real McCoy
PROFILE prof = { PROFILE_FILENAME,
(LPVOID) lpstrTarget,
(1 + lstrlen(lpstrTarget)) * sizeof(TCHAR)};
m_hprof = OpenColorProfile(&prof, PROFILE_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
OPEN_EXISTING);
if (!m_hprof)
return;
if (!GetColorProfileHeader(m_hprof, &m_phThis)) {
CloseColorProfile(m_hprof);
m_hprof = NULL;
return;
}
m_csName = lpstrTarget;
// Init the DeviceList pointer, because it doesn't cost much...
switch (m_phThis.phClass) {
case CLASS_PRINTER:
// Our device list is a printer list
m_pcdlClass = new CPrinterList;
break;
case CLASS_SCANNER:
// Our device list is a scanner list
m_pcdlClass = new CScannerList;
break;
case CLASS_MONITOR:
// Our device list is a monitor list
#if 1 // ALLOW_MONITOR_PROFILE_TO_ANY_DEVICE
m_pcdlClass = new CAllDeviceList;
#else
m_pcdlClass = new CMonitorList;
#endif
break;
case CLASS_COLORSPACE:
// List everything we can count
m_pcdlClass = new CAllDeviceList;
break;
default:
// Use the base device class (i.e., no devices of this type).
m_pcdlClass = new CDeviceList;
}
}
// Destructor
CProfile::~CProfile() {
if (m_hprof)
CloseColorProfile(m_hprof);
if (m_pcdlClass)
delete m_pcdlClass;
}
// Tag retrieval function
LPCSTR CProfile::TagContents(TAGTYPE tt, unsigned uOffset) {
DWORD dwcNeeded = sizeof m_acTag;
BOOL bIgnore;
if (!GetColorProfileElement(m_hprof, tt, 8 + uOffset, &dwcNeeded, m_acTag,
&bIgnore))
return NULL; // Nothing to copy!
else
return m_acTag;
}
// Profile Installation function
BOOL CProfile::Install() {
if (!InstallColorProfile(NULL, m_csName)) {
CGlobals::ReportEx(InstFailedWithName,NULL,FALSE,
MB_OK|MB_ICONEXCLAMATION,1,m_csName.NameAndExtension());
return (FALSE);
} else {
m_bIsInstalled = TRUE;
CGlobals::InvalidateList();
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, (LPCTSTR) m_csName, NULL);
_RPTF1(_CRT_WARN, "CProfile::Install %s succeeded\n",
(LPCSTR) m_csName);
return (TRUE);
}
}
// Profile Uninstallation function
void CProfile::Uninstall(BOOL bDelete) {
while (AssociationCount()) { // Dissociate all uses
Dissociate(DeviceName(m_cuaAssociation[0]));
m_cuaAssociation.Remove(0);
}
if (m_hprof)
{
CloseColorProfile(m_hprof);
m_hprof = NULL;
}
if (!UninstallColorProfile(NULL, m_csName.NameAndExtension(), bDelete)) {
CGlobals::ReportEx(UninstFailedWithName,NULL,FALSE,
MB_OK|MB_ICONEXCLAMATION,1,m_csName.NameAndExtension());
} else {
m_bIsInstalled = FALSE;
CGlobals::InvalidateList();
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, (LPCTSTR) m_csName, NULL);
_RPTF1(_CRT_WARN, "CProfile::Uninstall %s succeeded\n",
(LPCSTR) m_csName);
}
}
// Association
void CProfile::Associate(LPCTSTR lpstrDevice) {
// if the profile is not installed, install it first.
BOOL bInstalled = FALSE;
// Install profile, if not installed, yet.
if (!IsInstalled()) {
bInstalled = Install();
} else
bInstalled = TRUE;
if (bInstalled) {
if (!AssociateColorProfileWithDevice(NULL, m_csName.NameAndExtension(),
lpstrDevice)) {
CGlobals::ReportEx(AssocFailedWithName,NULL,FALSE,1,
MB_OK|MB_ICONEXCLAMATION,m_csName.NameAndExtension());
} else
_RPTF2(_CRT_WARN, "CProfile::Associate %s with %s succeeded\n",
lpstrDevice, (LPCTSTR) m_csName.NameAndExtension());
}
}
// Dissociation
void CProfile::Dissociate(LPCTSTR lpstrDevice) {
if (!DisassociateColorProfileFromDevice(NULL, m_csName.NameAndExtension(),
lpstrDevice)) {
CGlobals::ReportEx(DisassocFailedWithName,NULL,FALSE,1,
MB_OK|MB_ICONEXCLAMATION,m_csName.NameAndExtension());
} else
_RPTF2(_CRT_WARN, "CProfile::Dissociate %s from %s succeeded\n",
lpstrDevice, (LPCTSTR) m_csName.NameAndExtension());
}
// CProfileArray class- Same basic implementation, different base type.
CProfile *CProfileArray::Borrow() {
CProfile *pcpReturn = m_aStore[0];
memcpy((LPSTR) m_aStore, (LPSTR) (m_aStore + 1),
(ChunkSize() - 1) * sizeof m_aStore[0]);
if (m_ucUsed > ChunkSize())
m_aStore[ChunkSize() - 1] = m_pcpaNext -> Borrow();
else
m_aStore[ChunkSize() - 1] = (CProfile *) NULL;
m_ucUsed--;
if (m_ucUsed <= ChunkSize() && m_pcpaNext) {
delete m_pcpaNext;
m_pcpaNext = NULL;
}
return pcpReturn;
}
CProfileArray::CProfileArray() {
m_ucUsed = 0;
m_pcpaNext = NULL;
memset(m_aStore, 0, sizeof m_aStore);
}
CProfileArray::~CProfileArray() {
Empty();
}
void CProfileArray::Empty() {
if (!m_ucUsed) return;
if (m_pcpaNext) {
delete m_pcpaNext;
m_pcpaNext = NULL;
m_ucUsed = ChunkSize();
}
while (m_ucUsed--)
delete m_aStore[m_ucUsed];
m_ucUsed = 0;
memset(m_aStore, 0, sizeof m_aStore);
}
// Add an item
void CProfileArray::Add(LPCTSTR lpstrNew) {
_ASSERTE(lpstrNew && *lpstrNew);
if (m_ucUsed < ChunkSize()) {
m_aStore[m_ucUsed++] = new CProfile(lpstrNew);
return;
}
// Not enough space! Add another record, if there isn't one
if (!m_pcpaNext)
m_pcpaNext = new CProfileArray;
// Add the profile to the next array (recursive call!)
// Note: if we failed to get memory above, we simply fail to add the
// object.
if (m_pcpaNext) {
m_pcpaNext -> Add(lpstrNew);
m_ucUsed++;
}
}
CProfile *CProfileArray::operator [](unsigned u) const {
return u < ChunkSize() ?
m_aStore[u] : m_pcpaNext -> operator[](u - ChunkSize());
}
void CProfileArray::Remove(unsigned u) {
if (u > m_ucUsed)
return;
if (u >= ChunkSize()) {
m_pcpaNext -> Remove(u - ChunkSize());
return;
}
delete m_aStore[u];
memmove((LPSTR) (m_aStore + u), (LPSTR) (m_aStore + u + 1),
(ChunkSize() - (u + 1)) * sizeof m_aStore[0]);
if (m_ucUsed > ChunkSize())
m_aStore[ChunkSize() - 1] = m_pcpaNext -> Borrow();
else
m_aStore[ChunkSize() - 1] = (CProfile *) NULL;
m_ucUsed--;
if (m_ucUsed <= ChunkSize() && m_pcpaNext) {
delete m_pcpaNext;
m_pcpaNext = NULL;
}
}