{"id":143,"date":"2008-02-03T13:34:00","date_gmt":"2008-02-03T13:34:00","guid":{"rendered":"\/lisa\/post\/2008\/02\/03\/Beyond-TMM-analysis-of-a-bug-and-aftermath-of-architecture-work-What-happens-now.aspx"},"modified":"2021-08-30T14:27:36","modified_gmt":"2021-08-30T21:27:36","slug":"beyond-tmm-analysis-of-a-bug-and-aftermath-of-architecture-work-what-happens-now","status":"publish","type":"post","link":"https:\/\/spacefold.com\/lisa\/2008\/02\/03\/beyond-tmm-analysis-of-a-bug-and-aftermath-of-architecture-work-what-happens-now\/","title":{"rendered":"Beyond TMM, analysis of a bug and aftermath of architecture work:  What happens now?"},"content":{"rendered":"<p>Bo Durban of <a title=\"Bo's site\" href=\"http:\/\/moxiedata.com\/\" target=\"_blank\" rel=\"noopener\">Moxie data<\/a>\u00a0wrote to tell me about what I am sure is a bug even though I haven&#8217;t repro&#8217;d it yet. Not horrible, easy workaround, but a bug or bugs nonetheless.<\/p>\n<p class=\"NB\">What, you thought there weren&#8217;t any?\u00a0 We&#8217;re very proud of the reporting work, and we appreciate your high opinion of it! Still, while the new-in-SP2 architecture elements are quite robust in general, this is bound to happen, and it <em>will <\/em>happen, more than once, I&#8217;m sure.<\/p>\n<p>Try not to faint.<\/p>\n<h3>Backstory<\/h3>\n<p class=\"MsoNormal\" style=\"margin: 0in 0in 0pt;\">FXListener has some supported helper classes to perform the FX and GFX services supported by ReportBuilder extensions out-of-the box.<\/p>\n<p class=\"NB\">We shipped more FX and GFX classes than FXListener has specialized class information for, and this distinction is critical, so I&#8217;ll say\u00a0this again:<\/p>\n<p>The ones you see supported explicitly by FXListener with\u00a0*class, *classlib, *module, and explicit instantiation\u00a0are the ones required to supply behavior when you use ReportBuilder dialogs to specify extension behavior; we didn&#8217;t want to take the chance that these weren&#8217;t available at runtime.\u00a0 We also explicitly instantiate\u00a0the one that replaced UpdateListener&#8217;s therm behavior for default runtime feedback. <a title=\"TMM UpdateListener\" href=\"\/articles\/tmm\/tmm-updatelistener\">UpdateListener<\/a>\u00a0got replaced by <a title=\"TMM - fxTherm\" href=\"\/articles\/tmm\/tmm-fxtherm\/\">fxTherm<\/a> in SP2 for some good reasons, which I&#8217;ll explain if anybody cares,\u00a0and we didn&#8217;t want that behavior to suddenly &#8220;go missing&#8221; in default output runs either.<\/p>\n<p>You can put other ones into the collection with impunity, and you can supply setup behavior that ensures yours are available in any number of ways &#8212; including, if you wish, extending the checkCollectionMembers method if you wish, with or without additional *class, *classlib, *module properties added to your subclass. It probably makes more sense to\u00a0simply set up the _oReportOutput collection of reportlisteners with the appropriate collection members at the beginning of your application.\u00a0 If you build design-time extension behavior, you can also ensure that any previews from the designer are preloaded with your collection members very easily.<\/p>\n<p>Read <a href=\"\/articles\/tmm\/tmm-reportoutput\">http:\/\/spacefold.com\/articles\/tmm\/ReportOutputApp<\/a>\u00a0and the original helpfile entry it extends, if you are unfamiliar with the idea of the cache.\u00a0 Read Colin&#8217;s article <a href=\"\/articles\/datavizreports\/\">http:\/\/spacefold.com\/articles\/DataVizReports<\/a>\u00a0for step by step instructions if you are unfamiliar with\u00a0the idea of adding design-time controls into the builder (specifically the section on\u00a0<strong>Enabling the custom behavior by default <\/strong>will show you how the cache applies in this case). You could easily register behavior into your report\u00a0design environment that ensures the availability of\u00a0your decoration objects when the\u00a0designer is opened.\u00a0 Start here <a href=\"\/articles\/rsfundamentals-part1\">http:\/\/spacefold.com\/articles\/RSFundamentals-part1<\/a>\u00a0 if you are unfamiliar with the idea of registering new behavior elements into the Report Builder.<\/p>\n<p>Let&#8217;s\u00a0return\u00a0to our backstory.<\/p>\n<p>Each of the supported helper classes (which included, kind of late and <a title=\"blog post with beta bug info for gfxNoRender\" href=\"\/lisa\/2007\/08\/16\/SP2-Sedna-FFC-ReportListener-Beta-Info\/\">clearly\u00a0very hastily<\/a>, gfxNoRender) have the own get* method, called by CheckCollectionMembers, precisely because the logic to decide when they are useful is different for each type. (This is covered in <a href=\"\/articles\/tmm\/tmm-fxlistener\">FxListener<\/a>. )<\/p>\n<p>For example, I really thought it was possible that the memberscript object might be useful even to a Successor (and I still do).\u00a0\u00a0<em>But, generally speaking,\u00a0fx and gfx behavior is only supposed to be going on for the lead listener, the one attached to the internal report engine, not for Successors.<\/em><\/p>\n<p>This fact is pretty obvious if you look at fxListener&#8217;s SendFX method, which has the following call at the top:<\/p>\n<p class=\"code\"><span style=\"font-size: 10pt; color: blue; font-family: 'Courier New';\">IF THIS<\/span><span style=\"font-size: 10pt; color: black; font-family: 'Courier New';\">.IsSuccessor<\/span><br \/>\n<span style=\"font-size: 10pt; color: black; font-family: 'Courier New';\">\u00a0\u00a0\u00a0\u00a0 <\/span><span style=\"font-size: 10pt; color: green; font-family: 'Courier New';\">* Only the lead does this work.\u00a0\u00a0\u00a0\u00a0 <\/span><br \/>\n<span style=\"font-size: 10pt; color: green; font-family: 'Courier New';\">\u00a0 <\/span><span style=\"font-size: 10pt; color: blue; font-family: 'Courier New';\">RETURN <\/span><span style=\"font-size: 10pt; color: black; font-family: 'Courier New';\">m.liRenderBehavior<\/span><br \/>\n<span style=\"font-size: 10pt; color: blue; font-family: 'Courier New';\">ENDIF <\/span><\/p>\n<p>&nbsp;<\/p>\n<p>CheckCollectionMembers doesn&#8217;t have the same unilateral call at the top, just in case some collection objects are useful even in Successors.<\/p>\n<p>As I told Bo and Martin Haluza (<a href=\"http:\/\/www.eqeus.com\/\">http:\/\/www.eqeus.com\/<\/a>)\u00a0 this morning, I think that I should have put such a check at the top of fxListener.getNoRenderGFXobject(), and probably at the top of fxListener.getRotateGFXObject() as well.\u00a0 There is no earthly reason to check for the availability of, or the necessity to load, a gfx collection member in a Successor.\u00a0 Only the lead listener will\u00a0be decorating and certainly only the lead should be trying to write to the page using GDIPlus calls.\u00a0 (For one thing, the return value that determines whether the additional GDIPlus rendering will replace or add to the native rendering only makes sense in the lead listener, which is communicating with the engine.)<\/p>\n<p>Bo asked me &#8220;does this mean that a Successor\u00a0should not descend from FXListener?&#8221; And the answer is there is absolutely nothing wrong with a Successor\u00a0deriving from FXListener or one of its derived classes.\u00a0 The job of a Successor is to create extension output result types.\u00a0\u00a0The same\u00a0listener class\u00a0may do this sometimes as the lead (in which case it may also be decorating) and sometimes as a Successor (in which case it receives the benefit of the decorations, but is not in charge of creating them).<\/p>\n<h3>The real-life scenario(s)<\/h3>\n<p>In fact,\u00a0Bo and Martin have a classic scenario that both illustrates this and manifests the bug:<\/p>\n<p>Bo has written a listener that he\u00a0uses (for some reason)\u00a0as the lead.<\/p>\n<p class=\"NB\">I am not sure why that is, and I haven&#8217;t asked him. If\u00a0Bo is writing content decorations he could do the whole thing as fx and gfx objects and he shouldn&#8217;t care what listener &#8220;hosts&#8221; his decoration objects.\u00a0 But perhaps he is adding more value to fxListener in other ways&#8230; and you could see the bug even if he only creates fx and gfx objects&#8230; so just assume Bo&#8217;s listener, or your listener,\u00a0is the lead listener for some good reason&#8230; \u00a0Read on.<\/p>\n<p>Meanwhile Martin has written a listener, also derived from FXListener, that creates extension output.\u00a0 In fact, as you probably already know, he can chain many copies of his listener together as Successors, to create multiple forms of output (such as PDF, DOC, and HTML) in the same report run.<\/p>\n<p class=\"NB\">The fact that Martin could chain many different copies of his own listener together for different forms of output should illustrate why you don&#8217;t need multiple listener classes contributed by different authors to see this problem.\u00a0 One of Martin&#8217;s fxListener-derived objects will function as lead, the others will be Successors.\u00a0 Read on.<\/p>\n<p>So now Martin&#8217;s Successor listener (as well as\u00a0the lead) gets a gfxNoRender helper object, and\u00a0gfxNoRender\u00a0does a LoadReport check to see if any NoRender instructions have been included in this FRX.<\/p>\n<p>gfxNoRender has to do this work in LoadReport if it wants to take advantage of SP2&#8217;s ability to change the contents of the FRX dynamically during the LoadReport event.\u00a0 (If you don&#8217;t already know about this, <a title=\"TMM LoadReport docoid\" href=\"\/articles\/tmm\/tmm-loadreport\/\">start here<\/a> and read the referenced docoids).\u00a0 Since\u00a0LoadReport\u00a0occurs before the engine has loaded up the contents of the FRX, gfxNoRender &#8220;peeks ahead&#8221; and opens the FRX temporarily at this point.\u00a0 It actually has to do a lot of work to do this, check to see if it&#8217;s a built-in FRX, etc.<\/p>\n<p>Unfortunately &#8212; and Bo identified this &#8212; the Successor doesn&#8217;t yet have the information it needs to get into the correct FRXDataSession. At the time I wrote this part of the _ReportListener code, I don&#8217;t even think we <em>had<\/em> the correct FRXDataSession information in LoadReport, and I certainly knew that the engine-provided copy of the FRX\u00a0would not be available in it yet.\u00a0As a result\u00a0_ReportListener doesn&#8217;t do this until BeforeReport, and, Bo&#8217;s right, we should certainly do it earlier now.\u00a0 Arrgh #1.<\/p>\n<p>One point that Bo missed was that my matching reset of the Successor&#8217;s FRXDataSession in UnloadReport is there but it is not working correctly; the lead still has its FRXDataSession value filled out so the Successor&#8217;s isn&#8217;t get set back to -1, as it should.\u00a0 That&#8217;s arggh #2.<\/p>\n<p>But the thing that really had me gnashing my teeth (big arggh #3) here was&#8230; <em>why is a Successor\u00a0even doing these checks?<\/em>\u00a0 Hence my suggesting that the get* methods for the supported collection members need to be fixed.<\/p>\n<h3>So what happens now?<\/h3>\n<p>We&#8217;ve got\u00a0at least 3 things we&#8217;d like to fix here.<\/p>\n<p>You have some easy, and obvious, workarounds you can use in your derived classes right now, which you can implement without worrying that they will screw up anything later when your classes re-inherit from a newer version of FXListener:<\/p>\n<ul>\n<li>\n<div>\n<p>As Bo suggests, you can assign the correct value of FRXDataSession in LoadReport in your derived class:<\/p>\n<p>IF <span style=\"font-family: Courier New; font-size: small;\">NOT <\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">ISNULL<\/span><span style=\"font-family: Courier New; font-size: small;\">(<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">THIS<\/span><span style=\"font-family: Courier New; font-size: small;\">.Successor)<br \/>\n<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">\u00a0\u00a0 THIS<\/span><span style=\"font-family: Courier New; font-size: small;\">.Successor.<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">FRXDataSession <\/span><span style=\"font-family: Courier New; font-size: small;\">= <\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">THIS<\/span><span style=\"font-family: Courier New; font-size: small;\">.<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">FRXDataSession<br \/>\nENDIF<br \/>\n<\/span><span style=\"color: #008000; font-family: Courier New; font-size: small;\">* the above should come before:<br \/>\n<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">DODEFAULT<\/span><span style=\"font-family: Courier New; font-size: small;\">() <\/span><\/p>\n<p>&nbsp;<\/p>\n<\/div>\n<\/li>\n<li>\n<div>\n<p>\u00a0As I would also suggest, you should also prevent the Successor from doing a lot of extra work, which it is currently and ridiculously doing.\u00a0 You can do it like this:<\/p>\n<p class=\"code\">LOCAL <span style=\"font-family: Courier New; font-size: small;\">lcgfxClassName<br \/>\n<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">IF <\/span><span style=\"font-family: Courier New; font-size: small;\">NOT <\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">ISNULL<\/span><span style=\"font-family: Courier New; font-size: small;\">(<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">THIS<\/span><span style=\"font-family: Courier New; font-size: small;\">.Successor)<br \/>\n<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">\u00a0\u00a0 THIS<\/span><span style=\"font-family: Courier New; font-size: small;\">.Successor.<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">FRXDataSession <\/span><span style=\"font-family: Courier New; font-size: small;\">= <\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">THIS<\/span><span style=\"font-family: Courier New; font-size: small;\">.<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">FRXDataSession<br \/>\nIF TYPE<\/span><span style=\"font-family: Courier New; font-size: small;\">(&#8220;THIS.Successor.gfxNoRenderClass&#8221;)=&#8221;C&#8221;<br \/>\nlcgfxClassName = <\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">THIS<\/span><span style=\"font-family: Courier New; font-size: small;\">.Successor.gfxNoRenderClass<br \/>\n<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">\u00a0\u00a0\u00a0\u00a0\u00a0 THIS<\/span><span style=\"font-family: Courier New; font-size: small;\">.Successor.gfxNoRenderClass = &#8220;&#8221;<br \/>\n<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">\u00a0\u00a0 ENDIF<br \/>\nENDIF<br \/>\n<\/span><span style=\"color: #008000; font-family: Courier New; font-size: small;\">* the above should come before DODEFAULT():<br \/>\n<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">DODEFAULT<\/span><span style=\"font-family: Courier New; font-size: small;\">()<br \/>\n<\/span><span style=\"color: #008000; font-family: Courier New; font-size: small;\">* the below should come after DODEFAULT():<br \/>\n<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">IF VARTYPE<\/span><span style=\"font-family: Courier New; font-size: small;\">(lcgfxClassName) = &#8220;C&#8221;<br \/>\n<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">\u00a0\u00a0 THIS<\/span><span style=\"font-family: Courier New; font-size: small;\">.Successor.gfxNoRenderClass = lcgfxClassName<br \/>\n<\/span><span style=\"color: #0000ff; font-family: Courier New; font-size: small;\">ENDIF<\/span><\/p>\n<p>&nbsp;<\/p>\n<\/div>\n<\/li>\n<li>For\u00a0\u00a0neatness&#8217; sake, you might also want to change the FRXDataSession of your listener to -1 after a DODEFAULT() in UnloadReport IF THIS.IsSuccessor&#8230; although I don&#8217;t think the current behavior is hurting anything in particular.<\/li>\n<\/ul>\n<h3>Is that all there is?<\/h3>\n<p>Now, having said that&#8230; how can we fix all\u00a03 items in FXListener to make them available to everybody?<\/p>\n<p>Right now you know only slightly less than I do about that &#8212; and feel free to post your suggestions here, you&#8217;ve never been shy before and I&#8217;m sure you have opinions now. Just don&#8217;t expect me to suggest anything resembling a Microsoft-hosted refresh that would include these items. Even if there were such a thing, remember the part where this is bound to happen &#8220;more than once&#8221;?<\/p>\n<p>I&#8217;m going to have a chat with Bo and Martin and Colin, and get back to you about this.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Bo Durban of Moxie data\u00a0wrote to tell me about what I am sure is a bug even though I haven&#8217;t repro&#8217;d it yet. Not horrible, easy workaround, but a bug or bugs nonetheless. What, you thought there weren&#8217;t any?\u00a0 We&#8217;re very proud of the reporting work, and we appreciate your high opinion of it! Still,<a class=\"more-link\" href=\"https:\/\/spacefold.com\/lisa\/2008\/02\/03\/beyond-tmm-analysis-of-a-bug-and-aftermath-of-architecture-work-what-happens-now\/\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8,9],"tags":[],"class_list":["post-143","post","type-post","status-publish","format-standard","hentry","category-vfp-tmm","category-visual-foxpro"],"_links":{"self":[{"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/posts\/143","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/comments?post=143"}],"version-history":[{"count":5,"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/posts\/143\/revisions"}],"predecessor-version":[{"id":413,"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/posts\/143\/revisions\/413"}],"wp:attachment":[{"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/media?parent=143"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/categories?post=143"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/spacefold.com\/lisa\/wp-json\/wp\/v2\/tags?post=143"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}