I'm curious if someone could explain to me why when I call a destructor on the following array:
CArray<READ_ENTRY> arr;
It takes about 30 seconds to finish?
The arr array at the time has about 240,000 entries, which I thought wasn't that many. Also to compare memory usage, I took the working set from the Task Manager for my process before that array is filled in and after, and the working set grew by about 322 MB.
Also if there's something that I can do to optimize/speed it up?
EDIT:
The data that is being stored in this array is distributed as such:
CString members contain relatively short strings that are between 10 to 300 wchars long.
My MEM_ARRAY byte arrays also contain relatively small amounts of data:
procPrivs is about 20-100 bytes on average.
userSID is even smaller, it's the size of the user's SID (no more than 50 bytes.)
The rest are just 4 or 8 byte members.
EDIT2: In Release build it takes exactly the same amount of time to run.
The following structures are defined as such:
struct READ_ENTRY{
DWORD flags;
FILETIME ftLogTimeUTC;
short wTimeDiffMin;
int nOSErrorCode;
int nSpecCode;
DWORD dwMessageID;
DWORD dwMessageCtr;
DWORD dwMessageAllowBackSecs;
BYTE wProductType;
DWORD dwOSType;
DWORD dwMajorVersion;
DWORD dwMinorVersion;
DWORD dwBuildNumber;
WORD wServicePackMajor;
WORD wServicePackMinor;
WORD wSuiteMask;
WORD wProcessorArchitecture;
ULONGLONG dwActiveProcessorMask;
DWORD dwProcID;
DWORD dwThreadID;
LCID lcidThread;
MEM_ARRAY procPrivs;
CString strUserName;
MEM_ARRAY userSID;
CString strModName;
CString strMsgTitle;
CString strMsgDesc;
READ_ENTRY()
{
flags = DBXF_NONE;
ftLogTimeUTC.dwLowDateTime = 0;
ftLogTimeUTC.dwHighDateTime = 0;
wTimeDiffMin = 0;
nOSErrorCode = 0;
nSpecCode = 0;
dwMessageID = 0;
dwMessageCtr = 0;
dwMessageAllowBackSecs = 0;
wProductType = 0;
dwOSType = 0;
dwMajorVersion = 0;
dwMinorVersion = 0;
dwBuildNumber = 0;
wServicePackMajor = 0;
wServicePackMinor = 0;
wSuiteMask = 0;
wProcessorArchitecture = 0;
dwActiveProcessorMask = 0;
dwProcID = 0;
dwThreadID = 0;
lcidThread = NULL;
}
};
and:
struct MEM_ARRAY{
BYTE* pMem;
int ncbSzMem;
MEM_ARRAY() :
pMem(NULL)
, ncbSzMem(0)
{
}
~MEM_ARRAY()
{
freeMem();
}
MEM_ARRAY(BYTE* pSrcSID, int ncbSz) :
pMem(NULL)
, ncbSzMem(0)
{
copyMem(pSrcSID, ncbSz);
}
MEM_ARRAY(const MEM_ARRAY& s) :
pMem(NULL)
, ncbSzMem(0)
{
copyMem(s.pMem, s.ncbSzMem);
}
MEM_ARRAY& operator = (const MEM_ARRAY& s)
{
copyMem(s.pMem, s.ncbSzMem);
return *this;
}
void freeMem()
{
if(pMem)
{
delete[] pMem;
pMem = NULL;
}
ncbSzMem = 0;
}
void copyMem(BYTE* pSrcSID, int ncbSz)
{
if(pSrcSID != pMem)
{
freeMem();
pMem = new (std::nothrow) BYTE[ncbSz];
ASSERT(pMem);
if(pMem)
{
memcpy(pMem, pSrcSID, ncbSz);
ncbSzMem = ncbSz;
}
}
else
ASSERT(ncbSz == ncbSzMem);
}
};
EDIT3: Here's a sample of how to fill in the array:
#define SIZEOF(f) (sizeof(f) / sizeof(f[0]))
{
CArray<READ_ENTRY> arr;
BYTE dummy[256];
srand((UINT)time(NULL));
for(int i = 0; i < sizeof(dummy); i++)
dummy[i] = (BYTE)rand();
READ_ENTRY re;
arr.SetSize(240000); //Optimize it, since we know the final size
for(int t = 0; t < 240000; t++)
{
static LPCTSTR words[] = {
L"success", L"added", L"new", L"entry",
L"didn't", L"counter", L"as", L"ran",
L"out", L"this", L"first", L"because",
L"the", L"just", L"error", L"if",
L"or", L"manually", L"simple", L"program",
L"how", L"what", L"title", L"great",
};
CString strTitle;
int nCntW = GetRandomNumber(0, 12);
for(int i = 0; i < nCntW; i++)
{
if(!strTitle.IsEmpty())
strTitle += L" ";
strTitle += words[GetRandomNumber(0, SIZEOF(words))];
}
CString strDesc;
int nCntWDesc = GetRandomNumber(0, 100);
for(int i = 0; i < nCntWDesc; i++)
{
if(!strDesc.IsEmpty())
strDesc += L" ";
strDesc += words[GetRandomNumber(0, SIZEOF(words))];
}
re.strMsgTitle = strTitle; //Use some randomness for these members
re.strMsgDesc = strDesc;
re.strModName = L"Test Module v.1.0.0.0";
re.strUserName = L"mycomputername\\my user name";
re.procPrivs.copyMem(dummy, GetRandomNumber(10, 100));
re.userSID.copyMem(dummy, GetRandomNumber(10, 50));
arr.SetAtGrow(t, re);
}
//Destructor will be called here
}
ULONG GetRandomNumber(ULONG nNumMin, ULONG nNumMax)
{
ULONG nMin, nMax;
if(nNumMin <= nNumMax)
{
nMin = nNumMin;
nMax = nNumMax;
}
else
{
nMin = nNumMax;
nMax = nNumMin;
}
ULONG nRnd = ((ULONG)(rand() & 0x7fff) << 17) |
((ULONG)(rand() & 0x7fff) << 2) |
((ULONG)(rand() & 0x3));
return nMax > nMin ? (nRnd % (nMax - nMin)) + nMin : nMin;
}
Is the problem happening in release build too? (I cant comment it seems noobs dont get enough reputation to comment). what platform?
If the slow down is there in release build as well and you are seeing CPU spikes on delete then you might want to fix your data structures, at the risk of stating the obvious you'r heap is being stressed out. Freeing large amount of memory is a highly CPU intensive task.
You can ... Try clubbing the memory blocks into a larger block. Use fixed length strings, it will cause external fragmentation but you can decide where to draw the line.
One approach is to have the Carray per CPU and a separate heap for each one of them. This will make allocation and deallocation more concurrent.
Related
I have a little complicated data structure but I have a problem with insertion to it, maybe I am thinking wrong.
This data structure is something like (we have a map which is a pair of uint16_t and structure, and three nested structures)
struct Struct4
{
uint8_t struct4ByteData1;
uint8_t struct4ByteData2;
uint16_t struct4ShortIntData1;
uint16_t struct4ShortIntData2;
};
struct Struct3
{
uint32_t struct3IntData1;
uint16_t struct3ShortIntData1;
uint16_t numOfElementsInVectorOfStruct4;
std::string struct3StringData;
std::vector<std::shared_ptr<Struct4>> struct4vector;
};
struct Struct2
{
uint32_t struct2IntData1;
uint16_t struct2ShortIntData1;
uint16_t numOfElementsInVectorOfStruct3;
int16_t struct2ShortIntData2;
std::string struct2StringData;
std::vector<std::shared_ptr<Struct3>> struct3vector;
};
struct Struct1
{
uint32_t struct1IntData1;
uint16_t numOfElementsInVectorOfStruct2;
std::vector<std::shared_ptr<Struct2>> struct2vector;
};
using uint16AndStruct1Map = std::multimap<uint16_t, Struct1>;
And here is a code snippet how data is inserted:
Struct1* struct1_ptr = new Struct1;
std::shared_ptr<Struct2> vectorOfStruct2;
std::shared_ptr<Struct3> vectorOfStruct3;
std::shared_ptr<Struct4> vectorOfStruct4;
uint16AndStruct1Map mapToBeInserted;
bool validData = false;
uint16_t numOfElementsInVectorOfStruct4;
uint16_t status;
uint16_t keyDataFromStruct2;
struct1_ptr->struct1IntData1 = getSomeData();
struct1_ptr->numOfElementsInVectorOfStruct2 = getSomeElements();
if (struct1_ptr->numOfElementsInVectorOfStruct2 == 0)
{
std::cout << "No Pim Avoidance Measurement in This Message, ignore" << std::endl;
return;
}
for(uint16_t i = 0; i < getNumOfElementsOfStruct2(); i++)
{
vectorOfStruct2 = std::make_shared<vectorOfStruct2>();
keyDataFromStruct2 = getKey();
vectorOfStruct2->struct2IntData1 = getSomeData();
vectorOfStruct2->struct2ShortIntData1 = keyDataFromStruct2;
vectorOfStruct2->struct2ShortIntData2 = getSomeData();
vectorOfStruct2->numOfElementsInVectorOfStruct3 = getSomeElements();
vectorOfStruct2->struct2StringData = getSomeStringData();
struct1_ptr->struct2vector.push_back(vectorOfStruct2);
for(uint16_t j = 0; j < getNumOfElementsOfStruct3(); j++)
{
vectorOfStruct3 = std::make_shared<vectorOfStruct3>();
vectorOfStruct3->struct3IntData1 = getSomeData();
vectorOfStruct3->struct3ShortIntData1 = getSomeData();
vectorOfStruct3->numOfElementsInVectorOfStruct4 = getSomeElements();
vectorOfStruct3->struct3StringData = getSomeStringData();
struct1_ptr->struct2vector[i]->struct3vector.push_back(vectorOfStruct3);
numOfElementsInVectorOfStruct4 = getNumOfElementsOfStruct4();
if(numOfElementsInVectorOfStruct4 < 10)
{
for(uint16_t k = 0; k < numOfElementsInVectorOfStruct4; k++)
{
vectorOfStruct4 = std::make_shared<vectorOfStruct4>();
status = getStatus();
if(status == SPECIAL_STATUS_TO_GET_INSIDE_THIS_IF_COND)
{
vectorOfStruct4->struct4ByteData1 = getSomeData();
vectorOfStruct4->struct4ByteData2 = getSomeData();
vectorOfStruct4->struct4ShortIntData1 = getSomeData();
vectorOfStruct4->struct4ShortIntData2 = status;
struct1_ptr->struct2vector[i]->struct3vector->struct4vector.push_back(vectorOfStruct4);
validData = true;
}
}
}
}
if(validData)
{
mapToBeInserted.insert (std::make_pair(keyDataFromStruct2,*struct1_ptr));
}
else
{
for (auto it = mapToBeInserted.begin(); it!=mapToBeInserted.end(); it++)
{
it->second.numOfElementsInVectorOfStruct2--;
}
}
validData = false;
}
After everything was inserted into map (and all of structures), when I want to get data from this map I probably get memory error. I am not 100% sure what the problem is.
The issue occurs at level of obtaining data from the map: mapToBeInserted.
Could you help?
if numOfElementsInVectorOfStruct4 is more than 10 you don't insert anything into struct4vector therefore this loop will access out of the bounds of struct4vector:
for(uint16_t l = 0; l < kv.second.struct2vector[j]->struct3vector[k]->numOfElementsInVectorOfStruct4; l++)
{
ss << "<"
<< kv.second.struct2vector[j]->struct3vector[k]->struct4vector[l]->struct4ByteData1 << ","
There are nothing wrong with the map insertion. The program crashed because of the vector index numOfElementsInVectorOfStruct4.
You have set the number of element :
vectorOfStruct3->numOfElementsInVectorOfStruct4 = getSomeElements();
But the data of struct4vector is inserted with CONDITION (there are two : < 10 and Status is OK)
So that the vector size struct4vector.size() and numOfElementsInVectorOfStruct4 can be different (when numOfElementsInVectorOfStruct4 < 10 and
one of its element has status OK).
Here are some advices :
Work with pointer (with map) to avoid the struct element copy
Work with vector size() function instead of getSomeElements() somewhere.
Check for the map element exists before used
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
When implementing a call stack trace for tracking allocation in my overridden new function, I am using ::malloc to create an untracked call stack object that is then put into a linked list. When my harness finishes new-ing off all of the test cases, the list is sound. However, when i go to report the list (print to console) there are now values that should not be there and are causing it to crash. Below is the simplified version (I apologize that even simplified it is still a lot of code), I am hoping someone can make since of this:
Macros
#define convertToKiB(size) size * 1024UL
#define convertToMiB(size) size * (1024UL * 1024UL)
#define convertToGiB(size) size * (1024UL * 1024UL * 1024UL)
#define convertToReadableBytes(size) ((uint32_t)size > convertToKiB(2) && (uint32_t)size < convertToMiB(2)) ? (float)size / (float)convertToKiB(1) : ((uint32_t)size > convertToMiB(2) && (uint32_t)size < convertToGiB(2)) ? (float)size / (float)convertToMiB(1) : ((uint32_t)size > convertToGiB(2)) ? (float)size / (float)convertToMiB(1) : (float)size
#define convertToReadableBytesString(size) ((uint32_t)size > convertToKiB(2) && (uint32_t)size < convertToMiB(2)) ? "KiB" : ((uint32_t)size > convertToMiB(2) && (uint32_t)size < convertToGiB(2)) ? "MiB" : ((uint32_t)size > convertToGiB(2)) ? "GiB" : "B"
Globals
const uint8_t MAX_FRAMES_PER_CALLSTACK = 128;
const uint16_t MAX_SYMBOL_NAME_LENGTH = 128;
const uint32_t MAX_FILENAME_LENGTH = 1024;
const uint16_t MAX_DEPTH = 128;
typedef BOOL(__stdcall *sym_initialize_t)(IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess);
typedef BOOL(__stdcall *sym_cleanup_t)(IN HANDLE hProcess);
typedef BOOL(__stdcall *sym_from_addr_t)(IN HANDLE hProcess, IN DWORD64 Address, OUT PDWORD64 Displacement, OUT PSYMBOL_INFO Symbol);
typedef BOOL(__stdcall *sym_get_line_t)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Symbol);
static HMODULE g_debug_help;
static HANDLE g_process;
static SYMBOL_INFO* g_symbol;
static sym_initialize_t g_sym_initialize;
static sym_cleanup_t g_sym_cleanup;
static sym_from_addr_t g_sym_from_addr;
static sym_get_line_t g_sym_get_line_from_addr_64;
static int g_callstack_count = 0;
static callstack_list* g_callstack_root = nullptr;
CallStack Object
struct callstack_line_t
{
char file_name[128];
char function_name[256];
uint32_t line;
uint32_t offset;
};
class CallStack
{
public:
CallStack();
uint32_t m_hash;
uint8_t m_frame_count;
void* m_frames[MAX_FRAMES_PER_CALLSTACK];
};
CallStack::CallStack()
: m_hash(0)
, m_frame_count(0) {}
bool CallstackSystemInit()
{
// Load the dll, similar to OpenGL function fecthing.
// This is where these functions will come from.
g_debug_help = LoadLibraryA("dbghelp.dll");
if (g_debug_help == nullptr) {
return false;
}
// Get pointers to the functions we want from the loded library.
g_sym_initialize = (sym_initialize_t)GetProcAddress(g_debug_help, "SymInitialize");
g_sym_cleanup = (sym_cleanup_t)GetProcAddress(g_debug_help, "SymCleanup");
g_sym_from_addr = (sym_from_addr_t)GetProcAddress(g_debug_help, "SymFromAddr");
g_sym_get_line_from_addr_64 = (sym_get_line_t)GetProcAddress(g_debug_help, "SymGetLineFromAddr64");
// Initialize the system using the current process [see MSDN for details]
g_process = ::GetCurrentProcess();
g_sym_initialize(g_process, NULL, TRUE);
// Preallocate some memory for loading symbol information.
g_symbol = (SYMBOL_INFO *) ::malloc(sizeof(SYMBOL_INFO) + (MAX_FILENAME_LENGTH * sizeof(char)));
g_symbol->MaxNameLen = MAX_FILENAME_LENGTH;
g_symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
return true;
}
void CallstackSystemDeinit()
{
// cleanup after ourselves
::free(g_symbol);
g_symbol = nullptr;
g_sym_cleanup(g_process);
FreeLibrary(g_debug_help);
g_debug_help = NULL;
}
// Can not be static - called when
// the callstack is freed.
void DestroyCallstack(CallStack *ptr)
{
::free(ptr);
}
CallStack* CreateCallstack(uint8_t skip_frames)
{
// Capture the callstack frames - uses a windows call
void *stack[MAX_DEPTH];
DWORD hash;
// skip_frames: number of frames to skip [starting at the top - so don't return the frames for "CreateCallstack" (+1), plus "skip_frame_" layers.
// max_frames to return
// memory to put this information into.
// out pointer to back trace hash.
uint32_t frames = CaptureStackBackTrace(1 + skip_frames, MAX_DEPTH, stack, &hash);
// create the callstack using an untracked allocation
CallStack *cs = (CallStack*) ::malloc(sizeof(CallStack));
// force call the constructor (new in-place)
cs = new (cs) CallStack();
// copy the frames to our callstack object
unsigned int frame_count = min(MAX_FRAMES_PER_CALLSTACK, frames);
cs->m_frame_count = frame_count;
::memcpy(cs->m_frames, stack, sizeof(void*) * frame_count);
cs->m_hash = hash;
return cs;
}
//------------------------------------------------------------------------
// Fills lines with human readable data for the given callstack
// Fills from top to bottom (top being most recently called, with each next one being the calling function of the previous)
//
// Additional features you can add;
// [ ] If a file exists in yoru src directory, clip the filename
// [ ] Be able to specify a list of function names which will cause this trace to stop.
uint16_t CallstackGetLines(callstack_line_t *line_buffer, const uint16_t max_lines, CallStack *cs)
{
IMAGEHLP_LINE64 line_info;
DWORD line_offset = 0; // Displacement from the beginning of the line
line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
unsigned int count = min(max_lines, cs->m_frame_count);
unsigned int idx = 0;
for (unsigned int i = 0; i < count; ++i) {
callstack_line_t *line = &(line_buffer[idx]);
DWORD64 ptr = (DWORD64)(cs->m_frames[i]);
if (FALSE == g_sym_from_addr(g_process, ptr, 0, g_symbol)) {
continue;
}
strcpy_s(line->function_name, 256, g_symbol->Name);
BOOL bRet = g_sym_get_line_from_addr_64(
GetCurrentProcess(), // Process handle of the current process
ptr, // Address
&line_offset, // Displacement will be stored here by the function
&line_info); // File name / line information will be stored here
if (bRet)
{
line->line = line_info.LineNumber;
strcpy_s(line->file_name, 128, line_info.FileName);
line->offset = line_offset;
}
else {
// no information
line->line = 0;
line->offset = 0;
strcpy_s(line->file_name, 128, "N/A");
}
++idx;
}
return idx;
}
Operators
// Treat as Linked List Node
struct callstack_list
{
CallStack* current_stack = nullptr;
uint16_t total_allocation = 0;
callstack_list* next = nullptr;
};
struct allocation_meta
{
uint16_t size;
callstack_list callstack_node;
};
void* operator new(const size_t size)
{
uint16_t alloc_size = (uint16_t)size + (uint16_t)sizeof(allocation_meta);
allocation_meta *ptr = (allocation_meta*)::malloc((size_t)alloc_size);
ptr->size = (uint16_t)size;
ptr->callstack_node.current_stack = CreateCallstack(0);
ptr->callstack_node.total_allocation = (uint16_t)size;
ptr->callstack_node.next = nullptr;
bool run = true;
callstack_list* currentNode = nullptr;
while (g_callstack_root != nullptr && run)
{
if (currentNode == nullptr)
{
currentNode = g_callstack_root;
}
if (currentNode->next != nullptr)
{
currentNode = currentNode->next;
}
else
{
currentNode->next = &ptr->callstack_node;
run = false;
}
}
if (g_callstack_root == nullptr)
{
g_callstack_root = &ptr->callstack_node;
}
return ptr + 1;
}
void operator delete(void* ptr)
{
if (nullptr == ptr)
return;
allocation_meta *data = (allocation_meta*)ptr;
data--;
if (data->callstack_node.current_stack != nullptr)
DestroyCallstack(data->callstack_node.current_stack);
bool run = true;
callstack_list* currentNode = nullptr;
while (g_callstack_root != nullptr && run && &data->callstack_node != NULL)
{
if (currentNode == nullptr && g_callstack_root != &data->callstack_node)
{
currentNode = g_callstack_root;
}
else
{
g_callstack_root = nullptr;
run = false;
continue;
}
if (currentNode->next != nullptr && currentNode->next != &data->callstack_node)
{
currentNode = currentNode->next;
}
else
{
currentNode->next = nullptr;
run = false;
}
}
::free(data);
}
Test Harness
void ReportVerboseCallStacks(const char* start_time_str = "", const char* end_time_str = "")
{
callstack_list* currentNode = g_callstack_root;
unsigned int totalSimiliarAllocs = 0;
uint32_t totalSimiliarSize = 0;
while (currentNode != nullptr)
{
callstack_list* nextNode = currentNode->next;
uint32_t& currentHash = currentNode->current_stack->m_hash;
uint32_t nextHash;
if (nextNode == nullptr)
nextHash = currentHash + 1;
else
nextHash = nextNode->current_stack->m_hash;
if (nextHash == currentHash)
{
totalSimiliarSize += currentNode->total_allocation;
totalSimiliarAllocs++;
}
if (nextHash != currentHash)
{
//Print total allocs for type and total size
float reportedBytes = convertToReadableBytes(totalSimiliarSize);
std::string size = convertToReadableBytesString(totalSimiliarSize);
char collection_buffer[128];
sprintf_s(collection_buffer, 128, "\nGroup contained %s allocation(s), Total: %0.3f %s\n", std::to_string(totalSimiliarAllocs).c_str(), reportedBytes, size.c_str());
printf(collection_buffer);
//Reset total allocs and size
totalSimiliarAllocs = 0;
totalSimiliarSize = 0;
}
// Printing a call stack, happens when making report
char line_buffer[512];
callstack_line_t lines[128];
unsigned int line_count = CallstackGetLines(lines, 128, currentNode->current_stack);
for (unsigned int i = 0; i < line_count; ++i)
{
// this specific format will make it double click-able in an output window
// taking you to the offending line.
//Print Line For Call Stack
sprintf_s(line_buffer, 512, " %s(%u): %s\n", lines[i].file_name, lines[i].line, lines[i].function_name);
printf(line_buffer);
}
currentNode = currentNode->next;
}
}
void Pop64List(int64_t* arr[], int size)
{
for (int index = 0; index < size; ++index)
{
arr[index] = new int64_t;
*arr[index] = (int64_t)index;
}
}
void Pop8List(int8_t* arr[], int size)
{
for (int index = 0; index < size; ++index)
{
arr[index] = new int8_t;
*arr[index] = (int8_t)index;
}
}
int main()
{
if (!CallstackSystemInit())
return 1;
const int SIZE_64 = 8000;
int64_t* arr_64[SIZE_64];
const int SIZE_8 = 10000;
int8_t* arr_8[SIZE_8];
Pop64List(arr_64, SIZE_64);
Pop8List(arr_8, SIZE_8);
ReportVerboseCallStacks();
CallstackSystemDeinit();
return 0;
}
I finally figured out the answer. In my reporting function I was using std::string to create some of the reporting objects. std::string calls ::new internally to create a small allocation, and then hammers additional memory as the string's internal array reallocates memory. Switching to C-strings solved my problem.
I want to delete old images I created in a for loop.
The first time clicking the Button the images are created. The second time or third, whatever, the old Images that were created should now be deleted (before the loop) and then directly recreated in the loop.
Because I am getting the Image properties from the database and when someone changed something in the database you should be enabled to get the newest Image properties from the database.
I tried it with delete Image[i], free() and delete[] (whole Array) but I am always getting an Access Violation Error. Here is my following code:
TImage *Image[c]= ; //c is 5
I want to delete the old Images here and then create the new in the loop below
for (int i = 0; i < c; i++)
{
str = " Test "
Image[i] = new TImage(this);
Image[i]->Parent = BoardItem ;
Image[i]->Height = 20 ;
Image[i]->Width = 20 ;
Image[i]->Position->X = d ; // The program asks you the coordinate at the begining of a new loop
Image[i]->Position->Y = e ;
Image[i]->Bitmap = Icon->Bitmap ;
Image[i]->StyleName = str ;
Image[i]->OnClick = ImageClick ;
}
#Mykola i pulled the example out of my code , so it is not that complicated to understand. in button2 i want to delete now all images that are on tabitem1
void __fastcall TForm2::Button2Click(TObject *Sender)
{
TImage *Image[5] ;
for (int i = 0; i < c; i++) {
Image[i] = new TImage(this);
Image[i]->Parent = TabItem1 ;
Image[i]->Height = 20 ;
Image[i]->Width = 20 ;
Image[i]->Position->X = 10 ;
Image[i]->Position->Y = 10 ;
Image[i]->Bitmap = Image1->Bitmap ;
Image[i]->StyleName = "Something" ;
Image[i]->OnClick = ImageClick ;
}
}
//--------------------------------------------------------------------------
void __fastcall TForm2::ImageClick(TObject *Sender)
{
TImage *Img = dynamic_cast<TImage *>(Sender);
ShowMessage(Img->StyleName);
}
//--------------------------------------------------------------------------
void __fastcall TForm2::Button1Click(TObject *Sender)
{
}
You must delete single image by simple delete operator like:
for (int i = 0; i < c; i++)
{
delete Image[i];
// NULL deleted pointer
Image[i] = NULL;
}
Access Violation also may be caused because you still use this images somewhere in your code. And why did you want delete this images? As they are pointers you may simple renew values.
To reserve values for unpredictable amount of pointers TImage* you can use:
TImage** ppImage= NULL;
than create amount of pointers you want:
ppImage = new TImage*[c];
after that you may work with those pointers like you did before.
You could go like this. In the header of the class you define the array and some methods:
int c; // Maximum (better as define?)
TImage *Image[];
bool CheckImagesLoaded();
void DeleteAllImages();
void CreateImage(int nIndex, AnsiString str);
void CreateAllImages();
Then, in the constructor you init the array:
c = 5;
for (int i = 0; i < c; i++)
{
Image[i] = NULL;
}
Now you can do the check if images were already loaded:
bool TForm1::CheckImagesLoaded()
{
return Image[0]!=NULL;
}
To delete them all:
void TForm1::DeleteAllImages()
{
for (int i = 0; i < c; i++)
{
delete Image[i];
Image[i] = NULL;
}
}
Create a a single image like this (you have to expand the parameters as you need them):
void TForm1::CreateImage(int nIndex, AnsiString str)
{
Image[nIndex] = new TImage(this);
/*Image[nIndex]->Parent = BoardItem ;
Image[nIndex]->Height = 20 ;
Image[nIndex]->Width = 20 ;
Image[nIndex]->Position->X = d ; // The programm asks you the coordinate at the begining of a new loop
Image[nIndex]->Position->Y = e ;
Image[nIndex]->Bitmap = Icon->Bitmap ;
Image[nIndex]->StyleName = str ;
Image[nIndex]->OnClick = ImageClick ;
*/
}
And in a loop you can create all images as you like:
void TForm1::CreateAllImages()
{
AnsiString str = " Test ";
for (int i = 0; i < c; i++)
{
// load data from anywhere...
CreateImage(i, str);
}
}
So, now you can operate in the Button-Event.
Delete all old Images, if existing.
Create all new Images.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// Delete old Images, if existing
if (CheckImagesLoaded())
{
DeleteAllImages();
}
// Create new Images
CreateAllImages();
}
Hope this helps...
The code I'm using to make output of pTemp:
if (m_pTImgFrame != NULL)
{
for (int i = 0; i < m_nFrameNumber; i++)
{
TImage* pTemp = (TImage*)m_pTImgFrame[i];
if (pTemp != NULL)
{
TCHAR buffer[256] = { 0 };
_stprintf_s(buffer, 256, _T("%d,%d,%d,%d,%d,%d,%d,%d\n"),
pTemp->attrib,
pTemp->h,
pTemp->pitch,
pTemp->w,
pTemp->data[0],
pTemp->data[1],
pTemp->data[2],
pTemp->data[3]);
OutputDebugString(buffer);
delete[] pTemp;
m_pTImgFrame[i] = NULL;
}
}
delete []m_pTImgFrame;
m_pTImgFrame = NULL;
}
The origin code which still crashes
if (m_pTImgFrame != NULL)
{
for (int i = 0; i < m_nFrameNumber; i++)
{
BYTE* pTemp = (BYTE*)m_pTImgFrame[i];
if (pTemp != NULL)
{
delete[] pTemp;
m_pTImgFrame[i] = NULL;
}
}
delete []m_pTImgFrame;
m_pTImgFrame = NULL;
}
declaration of variables:
typedef unsigned int UINT;
typedef struct _TImage
{
int w;
int h;
int pitch;
int attrib;
unsigned char data[4];
} TImage;
UINT m_nFrameNumber;
UINT* m_pnFrameDelay;
TImage** m_pTImgFrame;
PBITMAPINFOHEADER pbih;
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
Allocation:
m_pTImgFrame = new TImage*[1];
m_pTImgFrame[0] = (TImage*)new BYTE[sizeof(TImage) - 4 + 4 * width * pbih->biHeight];
m_pTImgFrame[0]->attrib = imageAttrib8888;
m_pTImgFrame[0]->w = width;
m_pTImgFrame[0]->h = pbih->biHeight;
m_pTImgFrame[0]->pitch = 4 * width;
LOG(_T("32width=%d, height=%d"), width, pbih->biHeight);
for(j=pbih->biHeight-1; j>=0; j--)
{
int indexDst = (pbih->biHeight-j-1) * width * 4;
//int indexSrc = j * pbih->biWidth * 4;
int indexSrc = j * ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8/*bmpInfo.bmWidthBytes*/;
for(i=0; i< width; i++)
{
m_pTImgFrame[0]->data[indexDst++] = lpBits[indexSrc++];
m_pTImgFrame[0]->data[indexDst++] = lpBits[indexSrc++];
m_pTImgFrame[0]->data[indexDst++] = lpBits[indexSrc++];
m_pTImgFrame[0]->data[indexDst++] = 255;
indexSrc++;
}
}
The code will crash when run the line
delete [] pTemp if the m_nFrameNumber is 1 (I only saw the crash happened when it is 1 but I'm not sure);The output of each value of the pTemp is correct, so why it crashes ?
The code will crash when run the line delete [] pTemp if the m_nFrameNumber is 1
Given what you just said and that the allocation is as follows:
m_pTImgFrame = new TImage*[1];
The only valid index of m_pTImgFrame is 0 because you only allocate the array to be of size 1. Trying to access m_pTImgFrame[1] is undefined behavior.
Second, your new operator needs to match your delete operator for both type and scalar.
You allocated as a byte array:
m_pTImgFrame[0] = (TImage*)new BYTE[sizeof(TImage) - 4 + 4 * width * pbih->biHeight];
Therefor the correct way to deallocate the element is like this:
BYTE* allocation = (BYTE*)m_pTImgFrame[0];
delete [] allocaiton;
This is wrong:
TImage* pTemp = (TImage*)m_pTImgFrame[i];
if (pTemp != NULL)
{
...
delete[] pTemp; // deleting as an array of TImage, when it should be deleted as an array of bytes
}
Unrelated tangent based on yoru code - It's not necessary to check for NULL before calling delete. Always has been safe to say delete NULL. Always will be.
Every time i use IsertColumn function it will return an int variable. That variable is a index of a column.
If we have a lot of columns, is there any way to get that index of a column with given name (header text of that column).
Thank you
Per your request, something similar to this should work....
This example derives a CListCtrl to be used within a dialog.
int MyListCtrl::FindColumn(const CString& ColumnText)
{
int index = -1;
CHeaderCtrl* pHeader = GetHeaderCtrl();
if (pHeader)
{
int NumberOfColumns = pHeader->GetItemCount();
for (int i = 0; i < NumberOfColumns; ++i)
{
HDITEM Item;
TCHAR buffer[256] = { 0 };
Item.mask = HDI_TEXT;
Item.pszText = buffer;
Item.cchTextMax = 256;
pHeader->GetItem(i, &Item);
if (Item.pszText == ColumnText)
{
index = i;
break;
}
}
}
return index;
}
To call the function:
int index = m_llistctrl.FindColumn(_T("Value"));
Where "Value" is the text of an actual column in the header.
CListCtrl::GetHeaderCtrl::GetItem(int iPos, HDITEM* pHeaderItem);
HDITEM::pszText is what you want - if I got your question correctly...