From: RB on
Hi Joe, I think I understand what you guys are saying so I will
leave it at that, albeit still leaving voids in my obsessive curiosity.
However I have a question on a separate thread (same code area).
I have reposted the below pertinant code for reference. Please
see question at bottom. (obviously I am doing something wrong)
--------------------------------------------------
class CFileHandlingDoc : public CDocument
{
protected: // create from serialization only
CFileHandlingDoc();
DECLARE_DYNCREATE(CFileHandlingDoc)

// Attributes
public:
CMapStringToString ExpMap1; // is serializable
CMapStringToString* pExpMap1;
.......
}
--------------------------------------------------
In the view class data gets put in the map, here
I have just plugged in some recognizable stuff,
(code crunched up to save posting space)

CFileHandlingDoc* DocPtr = GetDocument();
CString One, Two, Three;
One = _T("WWW"); Two = _T("ZZZZ"); Three = _T("DDDD");
DocPtr->ExpMap1.SetAt(_T("a"), One);
DocPtr->ExpMap1.SetAt(_T("b"), Two);
DocPtr->ExpMap1.SetAt(_T("c"), Three);
//so my map now has some stuff in it.
----------And the doc serialize function------------------
void CFileHandlingDoc::Serialize(CArchive& ar)
{
pExpMap1 = &ExpMap1;
if (ar.IsStoring())
{
ar << pExpMap1;
}
else
{
ar >> pExpMap1;
}
}
===================================
When I run the above serialized code I get the below msg from my
debug output window,
---I first write the file then read the file then exit app and see output---

Warning: CFile::GetStatus() returns m_attribute without high-order flags.

// I always get the above warning, never been able to decipher why, (this
// is still using my old VC 6 compiler, probably need to just leave it alone
// and use my 2005 ide )

// But the below leak, I only get with the above serialize code. When
// I change the serialize code to an alternate method (see below) I do
// not get memory leaks.

Detected memory leaks!
Dumping objects ->
strcore.cpp(118) : {333} normal block at 0x019074E0, 17 bytes long.
Data: < DDDD> 01 00 00 00 04 00 00 00 04 00 00 00 44 44 44 44
strcore.cpp(118) : {332} normal block at 0x01907488, 14 bytes long.
Data: < c > 01 00 00 00 01 00 00 00 01 00 00 00 63 00
strcore.cpp(118) : {331} normal block at 0x01907430, 17 bytes long.
Data: < ZZZZ> 01 00 00 00 04 00 00 00 04 00 00 00 5A 5A 5A 5A
strcore.cpp(118) : {330} normal block at 0x01907160, 14 bytes long.
Data: < b > 01 00 00 00 01 00 00 00 01 00 00 00 62 00
strcore.cpp(118) : {328} normal block at 0x01907380, 16 bytes long.
Data: < WWW > 01 00 00 00 03 00 00 00 03 00 00 00 57 57 57 00
strcore.cpp(118) : {327} normal block at 0x01907328, 14 bytes long.
Data: < a > 01 00 00 00 01 00 00 00 01 00 00 00 61 00
plex.cpp(31) : {326} normal block at 0x01907240, 164 bytes long.
Data: < 4s > 00 00 00 00 00 00 00 00 0C 00 00 00 34 73 90 01
map_ss.cpp(74) : {325} normal block at 0x019071B8, 68 bytes long.
Data: < > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
map_ss.cpp(394) : {322} client block at 0x019070B0, subtype 0, 28 bytes long.
a CMapStringToString object at $019070B0, 28 bytes long
Object dump complete.
The thread 0x334 has exited with code 0 (0x0).
The thread 0x134 has exited with code 0 (0x0).
The thread 0xE60 has exited with code 0 (0x0).
The program 'Z:\.........\FileHandling.exe' has exited with code 0 (0x0).

---------now I change "only" the serialize code (shown above) to this-----

void CFileHandlingDoc::Serialize(CArchive& ar)
{
// Write OR ReadClass CmStS's CRuntime data
ar.SerializeClass(RUNTIME_CLASS(CMapStringToString));

// Then call CmStS's serialize directly
ExpMap1.Serialize(ar);

if (ar.IsStoring())
{
}
else
{
}
}
----first write the file then read the file then exit app then output---
// I no longer get the memory leaks.

Warning: CFile::GetStatus() returns m_attribute without high-order flags.
The thread 0x498 has exited with code 0 (0x0).
The thread 0xEE4 has exited with code 0 (0x0).
The thread 0xB04 has exited with code 0 (0x0).
The program 'Z:\.........\FileHandling.exe' has exited with code 0 (0x0).
--------------------------------------------------------
// When I step thru the code (not that it is relevant, just my morbid
// curiosity) with this method I do not see the

pOb = pClassRef->CreateObject();

// being called, in fact it seems to skip ReadObject all together and
// just go from WriteClass to

CRuntimeClass* PASCAL CRuntimeClass::Load(CArchive& ar,
UINT* pwSchemaNum)

// and loads the runtime class description, which to me (down
// in my low foo bar knowledge) seems what I would think it would
// be doing.
// I think maybe as Goran had told me some time back that
// using the SerializeClass function is the safer method to use.


From: Joseph M. Newcomer on
See below....
On Sun, 6 Jun 2010 09:42:55 -0400, "RB" <NoMail(a)NoSpam> wrote:

>Hi Joe, I think I understand what you guys are saying so I will
>leave it at that, albeit still leaving voids in my obsessive curiosity.
>However I have a question on a separate thread (same code area).
>I have reposted the below pertinant code for reference. Please
>see question at bottom. (obviously I am doing something wrong)
>--------------------------------------------------
>class CFileHandlingDoc : public CDocument
>{
>protected: // create from serialization only
> CFileHandlingDoc();
> DECLARE_DYNCREATE(CFileHandlingDoc)
>
>// Attributes
>public:
> CMapStringToString ExpMap1; // is serializable
> CMapStringToString* pExpMap1;
****
Whenever I see something like this, I have to ask "why do you need the pointer variable?".
In fact, I cannot imagine a single reason you would need a pointer variable that pointed
to the map, if the sole purpose of this pointer variable is to hold a pointer to the map
directly above it.
****
> .......
>}
>--------------------------------------------------
>In the view class data gets put in the map, here
>I have just plugged in some recognizable stuff,
>(code crunched up to save posting space)
>
>CFileHandlingDoc* DocPtr = GetDocument();
>CString One, Two, Three;
>One = _T("WWW"); Two = _T("ZZZZ"); Three = _T("DDDD");
>DocPtr->ExpMap1.SetAt(_T("a"), One);
>DocPtr->ExpMap1.SetAt(_T("b"), Two);
>DocPtr->ExpMap1.SetAt(_T("c"), Three);
>//so my map now has some stuff in it.
>----------And the doc serialize function------------------
>void CFileHandlingDoc::Serialize(CArchive& ar)
>{
> pExpMap1 = &ExpMap1;
> if (ar.IsStoring())
> {
> ar << pExpMap1;
****
Is this the right way to serialize? I would think that this would serialize the POINTER
to the map, not the map itself, which would not be useful.

If you needed to serialize the pointer, you would have written
ar << &ExpMap1;

which means the variable, as I suspected, is completely useless. But I also suspect this
is the wrong usage. I just went and read the code to CStringMapToString::Serialize and it
would not be invoked if you asked it to serialize the *pointer* to the structure.
****
> }
> else
> {
> ar >> pExpMap1;
> }
>}
>===================================
>When I run the above serialized code I get the below msg from my
>debug output window,
>---I first write the file then read the file then exit app and see output---
>
>Warning: CFile::GetStatus() returns m_attribute without high-order flags.
>
>// I always get the above warning, never been able to decipher why, (this
>// is still using my old VC 6 compiler, probably need to just leave it alone
>// and use my 2005 ide )
>
>// But the below leak, I only get with the above serialize code. When
>// I change the serialize code to an alternate method (see below) I do
>// not get memory leaks.
****
Normally, the destructor invoked when your CDocument is destroyed should delete this.
However, I've just read the code for CMapStringToString::~CMapStringToString and I don't
see where this actually frees the storage. I'd have to do some experimentation to figure
this out, and I don't have time to do this right now. However, I'd suggest making sure
that your CDocument-derived object is, in fact, deleted before the program exits.
joe
****
>
>Detected memory leaks!
>Dumping objects ->
>strcore.cpp(118) : {333} normal block at 0x019074E0, 17 bytes long.
> Data: < DDDD> 01 00 00 00 04 00 00 00 04 00 00 00 44 44 44 44
>strcore.cpp(118) : {332} normal block at 0x01907488, 14 bytes long.
> Data: < c > 01 00 00 00 01 00 00 00 01 00 00 00 63 00
>strcore.cpp(118) : {331} normal block at 0x01907430, 17 bytes long.
> Data: < ZZZZ> 01 00 00 00 04 00 00 00 04 00 00 00 5A 5A 5A 5A
>strcore.cpp(118) : {330} normal block at 0x01907160, 14 bytes long.
> Data: < b > 01 00 00 00 01 00 00 00 01 00 00 00 62 00
>strcore.cpp(118) : {328} normal block at 0x01907380, 16 bytes long.
> Data: < WWW > 01 00 00 00 03 00 00 00 03 00 00 00 57 57 57 00
>strcore.cpp(118) : {327} normal block at 0x01907328, 14 bytes long.
> Data: < a > 01 00 00 00 01 00 00 00 01 00 00 00 61 00
>plex.cpp(31) : {326} normal block at 0x01907240, 164 bytes long.
> Data: < 4s > 00 00 00 00 00 00 00 00 0C 00 00 00 34 73 90 01
>map_ss.cpp(74) : {325} normal block at 0x019071B8, 68 bytes long.
> Data: < > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>map_ss.cpp(394) : {322} client block at 0x019070B0, subtype 0, 28 bytes long.
>a CMapStringToString object at $019070B0, 28 bytes long
>Object dump complete.
>The thread 0x334 has exited with code 0 (0x0).
>The thread 0x134 has exited with code 0 (0x0).
>The thread 0xE60 has exited with code 0 (0x0).
>The program 'Z:\.........\FileHandling.exe' has exited with code 0 (0x0).
>
>---------now I change "only" the serialize code (shown above) to this-----
>
>void CFileHandlingDoc::Serialize(CArchive& ar)
>{
> // Write OR ReadClass CmStS's CRuntime data
> ar.SerializeClass(RUNTIME_CLASS(CMapStringToString));
>
> // Then call CmStS's serialize directly
> ExpMap1.Serialize(ar);
>
> if (ar.IsStoring())
> {
> }
> else
> {
> }
>}
>----first write the file then read the file then exit app then output---
>// I no longer get the memory leaks.
>
>Warning: CFile::GetStatus() returns m_attribute without high-order flags.
>The thread 0x498 has exited with code 0 (0x0).
>The thread 0xEE4 has exited with code 0 (0x0).
>The thread 0xB04 has exited with code 0 (0x0).
>The program 'Z:\.........\FileHandling.exe' has exited with code 0 (0x0).
>--------------------------------------------------------
>// When I step thru the code (not that it is relevant, just my morbid
>// curiosity) with this method I do not see the
>
>pOb = pClassRef->CreateObject();
>
>// being called, in fact it seems to skip ReadObject all together and
>// just go from WriteClass to
>
>CRuntimeClass* PASCAL CRuntimeClass::Load(CArchive& ar,
> UINT* pwSchemaNum)
>
>// and loads the runtime class description, which to me (down
>// in my low foo bar knowledge) seems what I would think it would
>// be doing.
>// I think maybe as Goran had told me some time back that
>// using the SerializeClass function is the safer method to use.
>
****
It may be. I wouldn't touch the MFC serialization for any amount of money; I consider it
fragile and of very weak design. I prefer to do my own serialization as XML data, but
then, I always have control of every data structure used.
joe
****
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: RB on
I' m in and out today so I will try to respond to some of this
now before I head back out.

>> CMapStringToString ExpMap1; // is serializable
>> CMapStringToString* pExpMap1;
> ****
> Whenever I see something like this, I have to ask "why do you
> need the pointer variable?" In fact, I cannot imagine a single reason
> you would need a pointer variable that pointed to the map, if the sole
> purpose of this pointer variable is to hold a pointer to the map
> directly above it.
> ****

Well you are correct, there is no real reason and you have told me
so before so I have no excuse. I should have coded this as you
showed further down.

> ar << &ExpMap1;

> ****
> Is this the right way to serialize? I would think that this would serialize
> the POINTER to the map, not the map itself, which would not be useful.
> If you needed to serialize the pointer, you would have written
> ar << &ExpMap1;
> which means the variable, as I suspected, is completely useless. But I
> also suspect this is the wrong usage. I just went and read the code to
> CStringMapToString::Serialize and it would not be invoked if you asked
> it to serialize the *pointer* to the structure.
> ****

Well the way I understood it was that if the class was serializable, which
CMapStringToString is, that there is an operator in the CArchive code
to handle the object pointed to with it's cruntime data. It does in fact write
the same data to file as the alternative method (and reads it back in) but the
memory is leaking.

> ****
> Normally, the destructor invoked when your CDocument is destroyed
> should delete this. However, I've just read the code for
> CMapStringToString::~CMapStringToString and I don't see where this
> actually frees the storage. I'd have to do some experimentation to figure
> this out, and I don't have time to do this right now. However, I'd suggest
> making sure that your CDocument-derived object is, in fact, deleted before
> the program exits. joe
> ****

Ok, when I get in tonight I will see if I can step thru and find that. I would
certainly hope so though since it was all project created when I first created
the project with the whole Document template macros.

>>// I think maybe as Goran had told me some time back that
>>// using the SerializeClass function is the safer method to use.
> ****
> It may be. I wouldn't touch the MFC serialization for any amount of money;
> I consider it fragile and of very weak design. I prefer to do my own
> serialization as XML data, but then, I always have control of every data
> structure used. joe
> ****

Well that is the very reason I want to understand what is going on with
the MFC serialize because if I use it to store my app's file data and it someday
breaks I need to know what has been written so I can code to read it back
in. I have learned by experimentation and reading the Archive docs that I can
readily identify every byte in a file dump and know what it is whether it be the
FFFF new class tag or the schema or the filename string length or that actual
CmStS value length counts, and the actual keys and values.
Of course that statement is somewhat self defeative, since I guess I should
about now be thinking of just writing my own serialize code from the start so
I know going in what is going on. But the serialize of MFC looked so
convenient I kinda wanted to utilize it but also wanted to feel confident
in using it which is why I stepped thru so much of it's code.
Later.......


From: RB on
Ok I think I have found what I'm doing wrong that is causing the leak.

First off I kept doing experiments, I verified that the uncommented code
writes exactly the same contents to file as the embedded call of
// ar.SerializeClass(RUNTIME_CLASS(CMapStringToString));
// ExpMap1.Serialize(ar);

if (ar.IsStoring())
{
ar << &ExpMap1; // the & works here.
}
else
{ // However I still had to use a ptr here, a & operator flags an err.
ar >> pExpMap1; // &ExpMap1 here flags an error C2679
}
//---------------------
And further the above uncommented method does read the data back
in and can be accessed from the CmStS calls. In fact as far as the file
goes both coding methods write and read the exact same file contents.
BUT the ptr passing method leaks memory.
And it appears to be my fault in that I did not search the docs for enough
details. I did find this one example in my docs that is pertinant to the problem.
Evidently if you create your object as an embedded member of the class then
you must call it's serialize method with an embedded call as in

SerializableClassObject.Serialize(ar);

whereas objects allocated inside constructors or on the heap
can be serialized with the CObject* ptr passed method.
Obviously by my passing a ptr to an embedded member was throwing
a wrench into the ReadObject method.
---------------------------------
The following example illustrates the cases:

class CMyObject : public CObject
{
// ...Member functions
public:
CMyObject() { }
virtual void Serialize( CArchive& ar ) { }

// Implementation
protected:
DECLARE_SERIAL( CMyObject )
};


class COtherObject : public CObject
{
// ...Member functions
public:
COtherObject() { }
virtual void Serialize( CArchive& ar ) { }

// Implementation
protected:
DECLARE_SERIAL( COtherObject )
};


class CCompoundObject : public CObject
{
// ...Member functions
public:
CCompoundObject();
virtual void Serialize( CArchive& ar );

// Implementation
protected:
CMyObject m_myob; // Embedded object //<-This is my scenario !
// !!! *****************************************
// Only difference is my CmStS Obj is in MyDocClass
// !!!
COtherObject* m_pOther; // Object allocated in constructor
CObject* m_pObDyn; // Dynamically allocated object
//..Other member data and implementation

DECLARE_SERIAL( CCompoundObject )
};

IMPLEMENT_SERIAL(CMyObject,CObject,1)
IMPLEMENT_SERIAL(COtherObject,CObject,1)
IMPLEMENT_SERIAL(CCompoundObject,CObject,1)


CCompoundObject::CCompoundObject()
{
m_pOther = new COtherObject; // Exact type known and object already
//allocated.
m_pObDyn = NULL; // Will be allocated in another member function
// if needed, could be a derived class object.
}

void CCompoundObject::Serialize( CArchive& ar )
{
CObject::Serialize( ar ); // Always call base class Serialize.
// !!! *****************************************
m_myob.Serialize( ar ); // Call Serialize on embedded member.
m_pOther->Serialize( ar ); // Call Serialize on objects of known exact type.

// Serialize dynamic members and other raw data
if ( ar.IsStoring() )
{
ar << m_pObDyn;
// Store other members
}
else
{
ar >> m_pObDyn; // Polymorphic reconstruction of persistent
// object
//load other members
}
}
// end example


From: RB on
Actually I got the one part of the previous post wrong
For Exact types known and object already allocated
on the heap in the constructor, these should be called
with the embedded type call also.

m_pOther = new COtherObject; // Exact type known and
// object already allocated.
m_pObDyn = NULL; // Will be allocated in another
// member function if needed, could be a
// derived class object.

m_myob.Serialize( ar ); // Call Serialize on embedded member.
m_pOther->Serialize( ar ); // Call Serialize on objects of known exact type.
See previous post for complete example with explanations:
In retrospec I don't regret running into all of this. The simple looking
MyDocClass::Serialize(ar) function looked so easy. But in learning
to understand it and expanding all the MFC macros has taught me
more than I ever knew about the MFC structure. Sorry to waste all
of you Pro's time with all my newb questions, but I have learned a
great deal from you guys on this group.