Prev: Implementing 3-Tabs to Existing Dialog Project
Next: problem with CAsyncSocket::Send after receiver closed
From: RB on 14 Jun 2010 17:31 Well thanks to the help of this group, I feel like I have a version control and serialize paradigm that is on the way to being something I can be comfortable with. I have analyzed the file dump enough to know what is being written so if I had to I could read it back in if one of MFC's Classes like CMapStringToString's serialize ever broke. But stepping thru all of the code that CmStS and CArchive offers shows me it is far surpassed to anything I could come up with at this point. Experimenting shows it handles end of file resulting from corrupt unexpected input data etc etc. In addition I have added my own File ID value at the beginning of the file just to check right off the bat if the file type is one previously written. However if anyone has the time, I would appreciate a final critique to shoot down any areas where I may be in errant structure, or destructive coding, since I consider the version and serialization to be the foundation of an App, and I don't want it foo barred coming out of the gate. So blast away at my ignorance, I will learn from it. Code below. // in MyDocClass header file ............. protected: CMapStringToString ExpMap1; struct VerStruct { CString Ver, CpyRt, Corp; }; VerStruct VerData; DWORD FileID; ............. // in MyDocClass constructor CFileHandlingDoc::CFileHandlingDoc( ) { // Fetch stuff from included AppVer.h file that also // feeds all App requests for version data, that idea // courtesy of David Webber VerData.Ver.Format( _T("Version %d.%d.%d.%d"), VERMAJ, VERMIN, VERFIX, BUILDNUMBER ); VerData.CpyRt = _T(CPY_RIGHT_YR); VerData.Corp = _T(CORP_NAME); FileID = 0x1234ABCD; } // and in MyDocClass Serialize func void CFileHandlingDoc::Serialize(CArchive& ar) { if (ar.IsStoring( ) ) { ar << FileID; ar << VerData.Ver << VerData.CpyRt << VerData.Corp; ar.SerializeClass(RUNTIME_CLASS(CMapStringToString)); ExpMap1.Serialize(ar); } else { ar >> FileID; if (FileID != 0x1234ABCD) { CWnd* pWnd = AfxGetMainWnd( )->GetActiveWindow( ); pWnd->MessageBox( _T("Incorrect File format, operation aborted"), _T("! ALERT !"), MB_OK | MB_ICONINFORMATION ); return; } ar >> VerData.Ver >> VerData.CpyRt >> VerData.Corp; if (VerData.Ver.Right(7) == _T("1.0.0.0")) { ar.SerializeClass(RUNTIME_CLASS(CMapStringToString)); ExpMap1.Serialize(ar); } if (VerData.Ver.Right(7) == _T("1.0.0.1")) { // 1.0.0.1 stuff } } } ---for any who may care or want to see the written file dump it is below, The 1st DWORD is my FileID of 1234ABCD (dump little endian) The 1st line 5th byte (4th byte 0 based) is 0Fh (15d) is the length of 1st CString, followed by the 15d bytes of string contents. The 2nd line 5th byte is 12h (18d) is the length of the 2nd CString, followed by the 18d bytes of string contents. The 3rd line, 8th byte is 03 is the length of the 3rd CString, followed by the 3 bytes ofstring contents. then the CmStS stuff. Which first starts with an FFFF (new class tag)i.e. different than the MyDocClass so far(?). Then the 0000 version schema of CmStS. Then 0012h length of the CmStS's class name, followed by the 18d bytes if the class name, then what appears to be a count of the total CmStS keys in the file, a value of 03. Then the value of 01 which appears to denote the size or length of the 1st key value to follow, then the first key of "a" . Then the value of 03 again which appears to denote the length of the assoc value which follows and is "WWW". Then a value of 01 for the length of 2nd key followed by the key value of "b" . Then a value of 04 for the length of the assoc value of "ZZZZ", then the value of 01 for the length of the 3rd key followed by the key value of "c". Then the value of 4 for the length of the following assoc value which is "DDDD". CD AB 34 12 0F 56 65 72 73 69 6F 6E 20 31 2E 30 ͫ4Version 1.0 2E 30 2E 30 12 43 6F 70 79 72 69 67 68 74 20 28 .0.0Copyright ( 43 29 20 32 30 31 30 03 44 54 4D FF FF 00 00 12 C) 2010RBC�� 00 43 4D 61 70 53 74 72 69 6E 67 54 6F 53 74 72 CMapStringToStr 69 6E 67 03 00 01 61 03 57 57 57 01 62 04 5A 5A ingaWWWbZZ 5A 5A 01 63 04 44 44 44 44 ZZcDDDD 0 1 2 3 4 5 6 7 8 9 A B C D E F zero base hex ct 1 2 3 4 5 6 7 8 9 A B C D E F 10 one base hex ct 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 one base dec ct
From: Goran on 15 Jun 2010 07:32 On Jun 14, 11:31 pm, "RB" <NoMail(a)NoSpam> wrote: > // in MyDocClass header file > ............. > protected: > CMapStringToString ExpMap1; > > struct VerStruct > { > CString Ver, CpyRt, Corp; > }; > VerStruct VerData; > > DWORD FileID; > ............. > > // in MyDocClass constructor > > CFileHandlingDoc::CFileHandlingDoc( ) > { > // Fetch stuff from included AppVer.h file that also > // feeds all App requests for version data, that idea > // courtesy of David Webber > > VerData.Ver.Format( _T("Version %d.%d.%d.%d"), > VERMAJ, VERMIN, VERFIX, BUILDNUMBER ); > VerData.CpyRt = _T(CPY_RIGHT_YR); > VerData.Corp = _T(CORP_NAME); > FileID = 0x1234ABCD; > > } > > // and in MyDocClass Serialize func > > void CFileHandlingDoc::Serialize(CArchive& ar) > { > if (ar.IsStoring( ) ) > { > ar << FileID; > ar << VerData.Ver << VerData.CpyRt << VerData.Corp; > ar.SerializeClass(RUNTIME_CLASS(CMapStringToString)); > ExpMap1.Serialize(ar); > } > else > { > ar >> FileID; > if (FileID != 0x1234ABCD) > { > CWnd* pWnd = AfxGetMainWnd( )->GetActiveWindow( ); > pWnd->MessageBox( _T("Incorrect File format, operation aborted"), > _T("! ALERT !"), MB_OK | MB_ICONINFORMATION ); > return; > } It's a bad idea to put up a message box in the middle of serialization. You should throw an exception with appropriate info in it (that's what's going to happen anyhow for reasons outside your control, and that's what MFC expects to happen). This will cause ReportSaveLoadException to be called and error message will be shown. Document will consequently fail to load. What you did is, you failed to lad, inform the user, but you continued as if you did load the document. That's a ___bad___ idea. > ar >> VerData.Ver >> VerData.CpyRt >> VerData.Corp; > if (VerData.Ver.Right(7) == _T("1.0.0.0")) > { > ar.SerializeClass(RUNTIME_CLASS(CMapStringToString)); > ExpMap1.Serialize(ar); > } > if (VerData.Ver.Right(7) == _T("1.0.0.1")) > { > // 1.0.0.1 stuff > } > } > > } Minor point: you should not be using string (text) information for version. You have numbers anyhow, use them. Major point: you should also not tie your executable version to these if-s there. Look at it this way: over the time, you will add features and bug fixes, and there will hundreds, if not thousands of them. Many/ most of them will not require change to serialization, so your if-s are simply misguided. If you go on with this, you will repeat all serialization code in each version. That's not how things usually work (exception in development, before feature stabilizes). Only from time to time, you may have significant enough changes to warrant "big" version switches like you have there. This is what we do at my work: we have "general" document version (a number). Each time we change the schema, we bump that number, (as well the schema number in the appropriate class). Each release knows it's own "general" schema number, and so, upon load, we can say, whoops, this file is newer, I don't support that (we only support backwards compatibility; that works well enough, because normally new files mean new features mean new code to run them). e.g. const int GENERAL_DOC_VERSION=X; doc::Serialize() { load: ... int generalDocVersion; ar >> generalDocVersion; if (generalDocVersion > GENERAL_DOC_VERSION) ExceptionTooNew(); ... store: ... ar << GENERAL_DOC_VERSION ... } Goran.
From: RB on 15 Jun 2010 10:20 > It's a bad idea to put up a message box in the middle of serialization. > You should throw an exception with appropriate info in it (that's > what's going to happen anyhow for reasons outside your control, and > that's what MFC expects to happen). > This will cause ReportSaveLoadException to be called and error message > will be shown. Document will consequently fail to load. Thank you Goran, this reply is exactly what I was hoping for when I posted. Yes, I agree and will study up on this, though still green in the implementation of ASSERT, TRY, THROW etc. yet. > What you did is, you failed to lad, inform the user, but you continued > as if you did load the document. That's a ___bad___ idea. A mute point since the context of your proposed use of exception handling is 100% correct, but however the MessageBox did tell the user the Operation was aborted, and there would be no loaded document for the user to work with. Maybe I misunderstood the direction of your statement there. > Minor point: you should not be using string (text) information for > version. You have numbers anyhow, use them. Well I used the string because it was already in existence (in a header file) for setting the About dialog Statics, and additionally I thought it might be helpful at some time, to be able to look at a file dump and actually see the full version number. If you would be so kind, please elaborate as to why a number would be better than a string for this. > Major point: you should also not tie your executable version to these > if-s there. Look at it this way: over the time, you will add features > and bug fixes, and there will hundreds, if not thousands of them. Many/ > most of them will not require change to serialization, so your if-s >.............. >This is what we do at my work: we have "general" document version (a number). Each time we change the schema, we bump that number, (as well the schema number in the appropriate class). Absolutely 100% correct, and the voice of experience again expands my view from narrow to wide. In retrospect MFC did it's best to alert me to this with the schema context of serialize but I fooed the version into the story. Again thanks for speeding me along over mistakes I would have had to learn the hard way. Greatly appreciated. If you lived near I would buy you a round of beer (unless you are like Joe and don't drink).
From: Goran on 16 Jun 2010 03:20 On Jun 15, 4:20 pm, "RB" <NoMail(a)NoSpam> wrote: > > What you did is, you failed to lad, inform the user, but you continued > > as if you did load the document. That's a ___bad___ idea. > > A mute point since the context of your proposed use of exception handling > is 100% correct, but however the MessageBox did tell the user the Operation > was aborted, and there would be no loaded document for the user to work > with. Maybe I misunderstood the direction of your statement there. Yes, there was the message, but MFC continues with loading (it does not know anything about your mesage box), which in the end means that you seemingly start the app with document C:\does\not \matter.yourfiletype open. But doc is actually empty, because you didn't really load it. Now imagine that your user forgets the message and saves what he has (an empty doc). Whoops! > > Minor point: you should not be using string (text) information for > > version. You have numbers anyhow, use them. > > Well I used the string because it was already in existence (in a header file) > for setting the About dialog Statics, and additionally I thought it might be > helpful at some time, to be able to look at a file dump and actually see the > full version number. > If you would be so kind, please elaborate as to why a number would > be better than a string for this. Look at the changes to your code to identify versions. You already have text.Right(7) that won't work as soon as your version becomes n.n.n.nn. Do you want to think about that (all the time, really)? Integral number, OTOH, is an integral. x == y, that's it. > > Major point: you should also not tie your executable version to these > > if-s there. Look at it this way: over the time, you will add features > > and bug fixes, and there will hundreds, if not thousands of them. Many/ > > most of them will not require change to serialization, so your if-s > >.............. > >This is what we do at my work: we have "general" document version (a > > number). Each time we change the schema, we bump that number, (as > well the schema number in the appropriate class). Additional comment here: what we do is OK, but the "more" MFC way is to version-enable your document class and use SerializeClass, e.g. #define GENERAL_DOC_VERSION X IMPLEMENT_SERIAL( CYourDoc, CYourDocBaseProbablyCDocument, VERSIONABLE_SCHEMA|GENERAL_DOC_VERSION) void CYourDoc::Serialize(...) { SerializeClass(GetRuntimeClass()); loading: UINT nSchema Schema = GetObjectSchema(); if (Schema > GENERAL_DOC_VERSION) ExceptionTooNew(); ... storing: ... } Notes: * this ties serialization with your document class name, so you will have trouble changing it * any use of serializeClass or operator<</>> tie serialization with your class name. (That might be a problem when/if a major overhaul of serialization occurs). > Absolutely 100% correct, and the voice of experience again expands > my view from narrow to wide. In retrospect MFC did it's best to alert > me to this with the schema context of serialize but I fooed the version > into the story. > Again thanks for speeding me along over mistakes I would have had > to learn the hard way. Greatly appreciated. If you lived near I would > buy you a round of beer (unless you are like Joe and don't drink). ;-) Beware, I live in Belgium now, so I am picky about beer ;-) Goran.
From: RB on 16 Jun 2010 10:02 > Goran wrote: > Yes, there was the message, but MFC continues with loading > (it does not know anything about your mesage box), which in the > end means that you seemingly start the app with document > C:\does\not\matter.yourfiletype open. But doc is actually empty, > because you didn't really load it. Now imagine that your user forgets > the message and saves what he has (an empty doc). Whoops! OHhhhhh! I can now see what you are talking about. He would have overwritten what "used to be" the file persistance of the doc. What a shallow oversight on my part. You saved me on this. >>> Goran wrote: >>> Minor point: you should not be using string (text) information for >>> version. You have numbers anyhow, use them. >> RB wrote: >> If you would be so kind, please elaborate as to why a number would >> be better than a string for this. > Goran wrote: > Look at the changes to your code to identify versions. You already > have text.Right(7) that won't work as soon as your version becomes > n.n.n.nn. Do you want to think about that (all the time, really)? > Integral number, OTOH, is an integral. x == y, that's it. Ah yes, I can see your point now, before in my limited foresight I never envisioned a build number going beyond 1 displacement. In other words I thought if I reached build number 9, I would change the version to 2. But I can see the fallacy in that now. I really have never gotten that far along with anything I write. >>> Goran wrote: >>> number). Each time we change the schema, we bump that number, >>> (as well the schema number in the appropriate class). > Goran Wrote: > Additional comment here: what we do is OK, but the "more" MFC > way is to version-enable your document class and use SerializeClass, > e.g. > #define GENERAL_DOC_VERSION X > IMPLEMENT_SERIAL( > CYourDoc, > CYourDocBaseProbablyCDocument, > VERSIONABLE_SCHEMA|GENERAL_DOC_VERSION) > void CYourDoc::Serialize(...) > { > SerializeClass(GetRuntimeClass( )); > loading: > UINT nSchema Schema = GetObjectSchema( ); > if (Schema > GENERAL_DOC_VERSION) > ExceptionTooNew( ); > ... > storing: > ... > } >Notes: >* this ties serialization with your document class name, so you will > have trouble changing it >* any use of serializeClass or operator<</>> tie serialization with > your class name. > (That might be a problem when/if a major overhaul of serialization > occurs). Albeit I do understand what you are saying about tieing to the serialization, this brings to bear a whole cloud that I have been confused on (but aware of ) for some time, and I know you explained this to me before. But I have not completely parameritized all of the logistical operations in the macros when I would expand them. Please see below. My input and/or questions are in the // comments ///////////////////////////////////////////////////// // CFileHandlingDoc IMPLEMENT_DYNCREATE(CFileHandlingDoc, CDocument) // Results in below macro, which appears to me to create the // MyDocDerivative object passing FFFF as the schema (which is not // a valid schema value but the default of -1 ) #define IMPLEMENT_DYNCREATE(class_name, base_class_name) \ CObject* PASCAL class_name::CreateObject( ) \ { return new class_name; } \ IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \ class_name::CreateObject) //--------------------------------------------------------------- // So I though all I had to do was set the schema from inside my doc // as in: CRuntimeClass* pMyDocRT = GetRuntimeClass( ); pMyDocRT->m_wSchema = 1; // set my schema //--------------------------------------------------------------- // But if I run the IMPLEMENT_SERIAL macro on on MyDocDerivative // after it is already in existence, isn't this going to create another occurance // of it's CRuntimeClass ? // I feel that I am still missing something in the structure here. #define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema) \ CObject* PASCAL class_name::CreateObject() \ { return new class_name; } \ _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, \ class_name::CreateObject) \ AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); \ CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) \ {pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); \ return ar; } \ >> Again thanks for speeding me along over mistakes I would have had >> to learn the hard way. Greatly appreciated. If you lived near I would >> buy you a round of beer (unless you are like Joe and don't drink). > ;-) Beware, I live in Belgium now, so I am picky about beer ;-) > Goran. Well if it is terribly expensive, how about a shot of Tequila with lime. On second thought I owe you what ever beer you ask for. Hopefully you will have the time and patience to respond to my confusion above on the CRuntime ramifications..........RB
|
Next
|
Last
Pages: 1 2 Prev: Implementing 3-Tabs to Existing Dialog Project Next: problem with CAsyncSocket::Send after receiver closed |