|
|
||||||||||||||||
|
For quite some time now people have been asking us to add an RSS feed to the blog and each time they do we mildly regret some of our life choices - especially Blogger. We will be moving to another host at some point but for now... Anyway - frabjous day! We think we've finally done it. Well, AI tells us it thinks we've done it. But ironically you won't know this as you're not subscribed yet! Just look down at the bottom right of the side bar and we now have a plethora of options, some of which we hope even work! I'm sure that we've all had to deal with the sort of user interface where we are working on a data aware form and only want to enable the OK/Save button when a series of conditions (other than simply required which the 4GL can happily enforce) are met. And like me you've probably written a generic validateOKRequirements: subroutine and wired it into every relevant LOSTFOCUS, CHANGED, CLICK event. Tedious, but necessary. Or so I thought. Enter SAVEWARN and SYSMSG. If you're thinking, "Oh I know about that" then please resume normal programming. If, like me, you apparently have been living under a rock for all these years then read on. The SAVEWARN property of the Window is a "Dirty" flag. If it is true then the data on the screen has changed from how it was when it was originally loaded. This is the flag the system checks the generate the "Changes will be lost... Continue?" prompt. So to enable/disable our own button all we need to do it check the value of SAVEWARN and if it is true, gosub our validation, and enable/disable accordingly. All well and good - but the thing that has evaded me all of these years, was how to actually do that. It seems the answer has been staring me in the face for years. The system already tells you when something becomes dirty and it does so through code 21 with SYSMSG.. The SYSMSG event is raised to a Window to allow the developer to intercept and modify/replace system responses to such situations as "Data will be lost, continue?" or "Are you sure you wish to delete?". Each of these passes the SYSMSG event a code to identify itself, such as 1 for the former and 2 for the latter. But scan to the end of the list and there at 21 is equ SYSMSG_SAVEWARNINFO$ to 21 ; // Save warn has been changed - null msg This is triggered each time SAVEWARN changes state. So when it becomes true and when it becomes false. So now we can do away with all of our LOSTFOCUS,CLICK,CHANGE type events and instead simply have one SYSMSG event with code like the following thereby centralising all of the checking in one easy to use place. We would label this a tricky gotcha but truth be told BUT if it got you you must have had more time on your hands than sense. A recent announcement from Revelation explained that the <> syntax has been updated - and that due to huge improvements in <>< processing speeds " you can continue using the familiar “<>” syntax for sequential dynamic array processing without needing to rewrite code using LOOP/REMOVE or LOOP/[] parsing patterns.". Historically nobody in their right mind would have processed the 65,536th element of a dynamic array using <>. If they wanted to get there they'd have used loop/remove. When the Revelation compiler was originally written, the maximum row size was 64K - so the maximum possible populated columns was 32K. So to speed up processing, the compiler replaced a < N > reference to a special opcode optimised for such extractions. Using < N, N > and < N, N, N > don't redirect to this special opcode, redirecting to the EXTRACT opcode instead. All well and good except for one slight snag. Remember the 64K limit? Well, then it made sense to store the value for the <> opcode as an unsigned 32 bit integer. Trouble is, that can only accommodate 64K. So what happens if you try and extract from an array where N is > 64K? A picture allegedly speaks louder than words One of the best parts of the new IDE is that every control type has its own designer: open it in-place, tweak the editable properties, and inspect the repository metadata. We live in these designers every day when we open a form, a program, a popup, or even a menu. What’s less obvious is that there’s also a designer for object code. It turns a lot of “system sleuthing” from writing code to take object code apart into something closer to browsing. To get to it: File → Open → Entity, then choose Stored Procedure Executables. Here's an example of using the designer to look at RLIST Open RLIST and the first thing you notice is how readable fragments are more easily visible due to them being plain text. Once you recognise the layout, that visibility gives you quick signals about what the routine is doing and which external routines it calls. On the right, the ObjectCode Properties panel tells you the basics immediately: RLIST is a subroutine (not a function) and it takes five parameters. The parameter names are obscured, but the Options button expands a popup showing the full list clearly. That’s the tool in the abstract. Here’s where it becomes genuinely useful. A concrete use: “How many users are in the system right now?” We can safely discard most of those if what we are looking for is a routine from RTI to GET the USERCOUNT. Let's try opening that up in the designer It's a function. If you call it with three blank variables, it populates them with:
One caveat: it only works when called with the UD in place. I’m sure you’ll find your own favourite uses for the object code designer, but if you haven’t tried it yet, it’s worth five minutes. If you’ve ever had to take object code apart by hand, this saves real time. OI 10 marked the introduction of a new licensing method, replacing the old `oengine.dll` stamping. Historically, the licensing information was baked into `oengine.dll`. If you wanted to “change” the characteristics of your OI system you could just drop in a different `oengine.dll` and, effectively, you had a new system. OI 10 introduces a separate licence metadata file called `revengine.lic`. It’s a simple XML file that looks like this (with some minor obfuscation): <OI> The `Signature` element is the important part. It’s a cryptographic signature over the real licence attributes – serial number, user count, expiry date and type – generated with a secret key that only Revelation knows. OI trusts *that* value and treats the rest of the XML purely as metadata for human consumption. You can edit `SerialNumber`, `NumUsers`, `ExpirationDate` and `ExpirationType` to your heart’s content; it doesn’t change the way OI behaves because those fields are not trusted. At this point you might think: “Fine, I’ll just copy the `.lic` file instead of the `.dll`.” But there’s a catch. OI 10 also moves to industry-standard security. Various user rows in the system are now encrypted using a key derived from (and salted with) the Signature. Change the Signature and you change the encryption seed. Anything that was encrypted under the old Signature will no longer decrypt under the new one and, if you’re using the default security policy, you will lock yourself out of the system. So yes, `revengine.lic` is technically portable – but once you’ve started using the system in anger, all of your sensitive user data is cryptographically tied to that specific licence Signature. Swapping licences is no longer a harmless way to “refresh” a system; it has real consequences for your encrypted data. An old client recently got in contact about the system we had written for them in AREV in the early 90s and subsequently ported to AREV32. Over the years, we had added some bells and whistles including an incredibly specifically tailored OI export utility which we installed about 10 years ago. Just recently, we added another OI export utility, and it was working as expected. But now, the client has reported that when including a specific set of columns, the report just hung - no messages, nothing. Eventually they just had to kill it in the task manager. They wondered if this was in any way connected to the recent upgrade? Now here's where it gets interesting. The columns in question are used frequently throughout the system, displayed on entry screens, reports and the like with no issues, so we thought the client's concern might be justified. We spun up our test system and attempted to produce the report the client was trying to create. Immediately upon selecting the columns in question, the system broke into the debugger with 'Unable to load program XXX'. At least this explained why the report was failing (the live system runs with the debugger disabled so they wouldn't see the error message). But it didn't explain how anything we might had done in the latest upgrade could have caused this. So down to TCL and EDIT BP XXX. The program is there. Perhaps it hadn't been compiled? Recompile. The issue was not resolved. Maybe, just maybe it hadn't been cataloged? EDIT VOC XXX. The catalog pointer is there and pointing to the correct program. But still the system crashes with being unable to load XXX. Then a vestigial memory oozed from the nether regions of our consciousness. In AREV, VOC and MD are synonyms. In AREV, CATALOG writes to the VOC file. In OI VOC and MD are two separate files. One used for AREV32 and one used for CTO/OI. So EDIT MD XXX New Record. COPY VOC XXX TO:(MD and all was fixed, nothing, fortunately, to do with the upgrade. Having been bitten by one missing VOC record we thought discretion was the better part of valour, and copied over all missing MD entries from VOC. All should be good yes? So we logged out of the system. Next time we logged in, disaster struck. ENG0711 RLIST. Cannot start engine... Once again it's LH logs to the rescue. We tried to start OI, and again it failed. But from the log we could see that the system wanted to load RLIST. So it went to the Global MD file and looked for an RLIST entry. Regretfully it found one pointing to the AREV RLIST which doesn't take parameters - it parses @SENTENCE - and called it instead of OI RLIST. Armed with this knowledge we could restore the Global MD files and sanity was restored. Almost happy with the solution we hit a realisation. ALL of the catalog pointers in the client's app are in VOC - not MD. So why isn't the system falling over left right and centre? The answer lies in OI's RTP27 program loader optimisations. When OI knows that it's running as a native windows app, RTP27 first looks in the Global MD for a pointer to the program in question, and if finding a pointer, executes that program, and if failing follows the inheritance chain looking for the program in SYSOBJ. In theory RTP27 then checks your VOC file, but this currently (10.2.3) tends not to work. When OI knows that it's running as AREVNN, it first checks VOC, then MD, then looks for the program. The issue here was that despite the fact we were running AREV32, the Export Window was running as a native window and so skipped the VOC step. If only we'd taken our own advice from over a decade ago... In summation, RTP27 loads programs in the following order
Some seven years ago, REVDEX documented the then newly introduced concept of #region to allow folding of code. We find this to be incredibly useful when working on large commuter programs as it allows easy navigation between code sections. There's no point in reproducing the linked article, so briefly, you can bracket sections of code between #region regionname and #endregion regionname and then fold that section of code. Here's an example of one of our commuter routines with the various code sections "regioned". The editor then takes me to the line number where that region begins. However, there is a minor caveat to be aware of when we're cutting and pasting collapsed regions. In the following pseudo-code (with a nod to Jackson Structured Programming throwback for those as ancient as myself) we've simply placed an individual subroutine within a region. That's not normally a real life scenario. You use regions to group logically connected groups of code. Of course you may choose to do this if you wish to be able to collapse individual subroutines. and collapse it to make cutting and pasting easier Let's decide that we want to move the process region to after the wrapup region, so we select the "#region process" and Ctrl-X to cut the region then move to the end of the program and press return and Ctrl-V to paste the region All looks sort of good. But let's expand everything again and see where we are It has brought across the process region line but inserted it within the wrapup region. So let's revisit and this time change the selection Note that we have now selected to the beginning of the next line - you can see the cursor. Cut, move down and paste and the expansion now looks like this the process region has been inserted within the wrapup region, not after. To fix this what we have to do is expand the wrapup region before doing the paste, like so Well worth paying a little attention to detail.
Previous Posts
Archives
BlogRoll
Subscribe
Subscribe via RSS
(For those who still appreciate civilised technology.)
Add to your reader
Scan to subscribe
Subscribe to
|
||||||||||||||||
| ||||||||||||||||