This is the first of what are likely to be a few posts today on Initialization. I’ve spent days troubleshooting issues I’ve had with initializing files properly and am working to carefully and thoroughly document my discoveries.
This morning I had a Notification of an Initialization Error on a file that launches automatically on startup. This was in the ServerMonitor posted to the Database Exchange and the Initialization of that file has been discussed here on other occasions.
But ErrorLog had no contents. Quitting and relaunching got no errors. No errors on a full restart either.
One thing I did find in the Instrumentation log is that on the first launch, with the error, Panorama ran account checks and other operations that it does not run otherwise. Does that somehow set up the error? Apparently I can’t test that until tomorrow since I’ve now launched Panorama on each of my computers. Maybe it also needs to be a startup file though. The machine with the error this morning does not restart every day but I’ll make sure it shuts down tonight.
FWIW, below is the full Initialization of the file:
It seems that how a file is opened effects what runs within the file. In order to collect the best data possible, I’ve cleared the Instrumentation Log prior every test.
Opening a particular file from the Desktop, the Instrumentation Log recorded the following:
TabPanelObject RUN CODE →
TabPanelObject RUN CODE →
TabPanelObject RUN CODE →
RUN CODE → .Initialize (Donees)
[TIME: 7:09 AM]
[Donees/.Initialize] Donees:Home
[Donees/.Initialize] fgDoneeData = Description
TabPanelObject RUN CODE →
[Donees/.Initialize] keys
[Donees/.Initialize] Selected Records
TabPanelObject RUN CODE →
[Donees/.Initialize] menus
[Donees/.Initialize] Fini Donees
After opening that very same file via a procedure named Open File, this was the recorded data:
RUN PROCEDURE TOOL → Open File
TabPanelObject RUN CODE →
TabPanelObject RUN CODE →
TabPanelObject RUN CODE →
RUN CODE → .Initialize (Donees)
[Donees/.Initialize] Donees:SECRET
[Donees/.Initialize] fgDoneeData = Description
[Donees/.Initialize] keys
[Donees/.Initialize] Selected Records
[Donees/.Initialize] menus
[Donees/.Initialize] Fini Donees
Opened as Auxiliary file
RUN CODE → .Initialize (Donees)
[Donees/.Initialize] Donees:Home
[Donees/.Initialize] fgDoneeData = Description
TabPanelObject RUN CODE →
[Donees/.Initialize] keys
TabPanelObject RUN CODE →
[Donees/.Initialize] Selected Records
TabPanelObject RUN CODE →
[Donees/.Initialize] menus
[Donees/.Initialize] Fini Donees
Each one has differences.
In all the above cases, Panorama was already running and the file had been previously opened. The Initialize has several zlog entries to record progress, one being info(“WindowName”).
Notice that when opened by a procedure it opens as a Secret window. What effect might that have?
The final zlog entry is the Fini at the very end of the Initialize procedure. You’ll see more on that on subsequent posts.
With Panorama not running and a launch of the file from the Desktop:
[Donees/.Initialize] Donees:Home
[Donees/.Initialize] fgDoneeData = Description
TabPanelObject RUN CODE →
[Donees/.Initialize] keys
[Donees/.Initialize] Selected Records
TabPanelObject RUN CODE →
[Donees/.Initialize] menus
[Donees/.Initialize] Fini Donees
Again there are differences in the sequence of events, and in the number of TabPanel runs.
I don’t know how much these do or don’t create the problems I’ve had in correctly initializing a file set, but they are among the items I’ve noted in trying to resolve those problems.
I have three files that each open and load properly if I open them one by one from the Desktop.
If I set one to open the other two as auxiliary files, the first loads and initially looks fine, but the second and third don’t display properly when they open and the first gets corrupted.
Some of the elements that should display in the subsequent files end up displaying on the first while other areas of the first simply go blank. If I close a file that has its elements erroneously being displayed in the first file, they disappear from the first file. If I save before closing it, there’s a fair frequency of permanently losing those elements.
The last file to open seems to have the most issues. Changing the order in the Auxiliary File window changes which one suffers the most.
I have no secret windows and I am not using Hide or NoShow anywhere.
If I have a procedure open the three files one after another, all three open quickly, but according the the Instrumentation Log, the Initialize procedures don’t run until after all three have opened. In this case there are drawing issues in the forms. The order in which they’re opened seems to have no influence.
Adding “wait 1” after open each one does get them all opened and displaying properly. The Initialize procedures were still being delayed until after all three were opened. No problem though if it gets them to all load properly.
But, another caveat. In this case it stalled well beyond 1 second after opening each file. I have yet to figure out what I do to get it to resume but by clicking from window to window the procedure eventually opens the next file. Otherwise it just sits.
Since they’re open as Secret window, I tried adding setactivedatabase but that made no difference.
Inserting a message instead of the wait eliminated the stalling, but restored the drawing issues
Though all of this I noticed that the form in the file from which I’m triggering all of these tests is always resized to an offset match of the first of the windows being opened.
I had errors like this with opening additional files in Pan X procedurally. I have made it work consistently by using a lot of redundant code and controlling window sizes. For a simple example, in the .Initialize procedure of the first file, I would use:
Open “2nd file”,
Call “.Initialize”
Make secret
window “original file:windowname”
Open “3rd file”,
Call “.Initialize”
Make secret
window “original file:windowname”
It’s funky but it works. I gave up trying to open additional files as I did in Panorama 6. Windows were wonky, files would hang when opening and stall the procedure, and sometimes it would never return to the original file even when I told it to, especially when loading a variable with the first window name so I didn’t have to use the full name each time.
I was also having a problem getting the first file to connect to the server. It would open, not connect and show a lock, then open and synchronize the additional files. I had to close and reopen the first file to get it connected or manually connect it to the server. I created an “Open set” database (not the actual name) that opens, then opens the first file of the set which would then connect to the server properly and open the remaining files.
I’m hoping to be able to write more simple code at some point and it’s much better in 10.2. I don’t use tab panels so I don’t have that additional complication.
The onerror statement only traps error if they are not trapped some other way. If the error is trapped with if error, or with try catch, then onerror will never see the error. (Note: The onerror documentation on this page was not updated to mention that try/catch will override onerror, I have just now submitted this correction.)
When a database is opened, Panorama X 10.2 uses the INITIALIZEDATABASE statement to run the .Initialize procedure, open auxiliary databases, and perform any other startup actions needed. You can open this statement and look at it, here is part of that code:
As you can see, this code uses try/catch to intercept any errors in the .Initialize code, so that’s why your onerror code does not work. (Panorama uses try/catch so that it can guarantee that the rest of the code in INITIALIZEDATABASE will run, even if there is an error in your .Initialize procedure.)
I think you started out wanting to know what the error message was from your .Initialize code. But you should already be able to see that simply by looking in Notification Center. Once you know what the error message is, you should have a better idea what the problem is.
If you still want to catch the error yourself, you need to use try/catch instead of onerror. You would write your initialization code like this:
try
letglobal ErrorLog = ""
... the rest of your initialization code (but not the onerror part)
catch
ErrorLog = "Panorama Error - "+info("error")
endcatch
You could also use global then define to set up the ErrorLog variable, but I don’t understand why you are doing that. It would make sense if you were appending entries to the log. Also, I would probably just do this:
At this point I can’t think of any reason to ever use onerror – it’s a rather blunt instrument. But try/catch is a very powerful tool that allows you to exactly control how errors are handled all through your code. (Extra tech note – you can no longer use onerror in server code, because Panorama X Server is using try/catch to trap errors. So if you want to trap errors in server code yourself, you’ll need to use try/catch.)
They look the same to me, except for the Tab Panel code. The Tab Panel code is run by the form, not the .Initialize procedure. In this case, it appears that your Tab Panel doesn’t contain any code, so nothing is running anyway.
That is not correct. Are you sure your code doesn’t use opensecret followed by an explicit call to .Initialize.
Again I don’t see any difference in the sequence of events, only in the “TabPanel runs”, which are triggered by the form, not the .Initialize code (at least not directly).
I don’t want to get too far into this, because I don’t know when you did this. The way Panorama X handles .Initialize procedures changed significantly (for the better) about a year ago (maybe 18 months?). There were a lot of problems with edge cases that were all cleared up. But I do want to make a couple comments on your code.
Open “2nd file”,
Call “.Initialize”
Make secret
window “original file:windowname”
First of all, this code will call the .Initialize procedure twice. I suspect that isn’t what you want.
Secondly, it appears that you want to open these additional files without windows. I would suggest just using opensecret to do that. If you need to run some initialization code for these secret files, you can call that explicitly.
opensecret "2nd file"
call ".InitializeSecret"
The name of this procedure could be anything. I would suggest that the first line of this procedure be:
activedatabase "2nd file"
or you could do:
activedatabase info("proceduredatabase")
It’s probably worth mentioning that auxiliary files are just a list of databases, Panorama’s OPENAUXILIARY statement just goes through the list and uses either opendatabase or opensecret to open them. Again, you can view the source for this yourself. So from the point of view of the database being opened, there is no difference between being opened as an auxiliary file and being opened by the .Initialize procedure. The only difference is that there is a nice user interface for managing the list.
That’s correct and what I would do in Pan 6. This didn’t work reliably in the past on Pan X which is why I opened the file then made it secret. It would often times stall out while opening secret databases and get confused even if I used setactivedatabase. As I said, it is much improved in 10.2.
Yes, I was trying to find the reason that the Notification Center had put up a Notification that said “Initialization Error”. That was the only clue I had. There have been numerous attempts to find that error in the past, but it very rarely appears. The file can be launched and launched under all sorts of situations without it ever coming up.
The Initialize is simple enough and runs reliably enough that I suspect it’s a Panorama error, possibly tied to Panorama’s effort to verify my account, per the Instrumentation Log notes that I posted. Others are using the same ServerMonitor file, downloaded from the Database Exchange, and have reported the error too.
Tonight I will shut down the machine and let it do an automatic startup in the morning since that matches the circumstances of the last such error.
My objective in all but my first post yesterday is simply to get three files to launch and load properly without cross file corruption of the graphic layouts. The View menu also gets corrupted and shows forms and procedures from another file.
After weeks of pursuing a solution, I’ve not found one. wait was the most promising, but completely stalls the process of opening the files.
The Tab Panels are the issue.
CatchError( for the Tab Panels is inadequate in this case. It requires complete removal of Tab Panels from the forms in order to sidestep the stall of wait.
Two weeks ago when posting about the problem, I floated the idea of using a simple form for startup. Since this is seeming more and more like an unachievable goal that may be my only choice.
If you look at the INITIALIZEDATABASE code I posted above, you can see that this notification will only occur if there is an error in your code, because your code is the only code that runs within that particular try/catch block. So this is not an error in some other part of Panorama’s code, which would cause a different error (I think a message, rather than a notification).
In that case, my suggestion would be that you should trap the error yourself and log it to a file. You could also display a notification, or even a message (though I like to avoid modal messages in the .Initialize code. So write your .Initialize code something like this:
try
your
initialization
code
catch
let errorMessage = info("error")
fileappend dbfilename+"_"+InitializeErrors.txt",superdatestr(supernow()+" - "+errorMessage+lf()
nsnotify "Initialize Error","text",errorMessage
endcatch
Then later you can review this file to see what the error is.
If you want to get really fancy, you could use the info(“errorstack”) function. This could be very useful if your .Initialize calls other procedures, as it will tell you exactly what procedure was running when the error occurred, and the line number.
Sounds like I will need to investigate this further. I’m not sure if I’ve ever used Tab Panels in an application where multiple databases open automatically.
I did some further investigating and I have definitely been able to reproduce a problem with tab panels when the database is opened using the opendatabase statement. It’s a weird one because I have another, seemingly more complicated database that works fine.
Ok, here’s a clue for me, and a possible workaround for you. I was able to get it to work by replacing the OpenDatabase statement with these three lines:
In spite of the Server Monitor file launching at least once every day, the Initialize error is truly rare. This morning it did occur again on the first launch after I re-installed the OS on my server.
Fortunately, this time I did capture more about the error:
Initialize Error (Server Monitor) SetFormOptions failed because database “” does not contain a form named “”
So the error message starts out naming the database correctly but then lacks the name of the database and the form when running SetFormOptions.
I have no such statement in my procedures so it must be called by Panorama when I invoke FormColor (used as a visual indication of the monitor status)
To (maybe) sidestep the error, I’ve replaced FormColor with SetFormOptions. Time will tell if that makes a difference but it does seem that Panorama’s implementation of FormColor is the source of the error. While running code from the only file open and the only form in the file, it lacks both file name and form name. It requires some fairly exceptional circumstances since otherwise it will launch over and over without issues.
The source of the error is that there is no guarantee that a particular form will be open and active when the .Initialize code runs. I would recommend putting code for initializing a form into the form event code instead of the .Initialize code.
Since the FormColor statement contains only one line of code,
I think it is unlikely that this change will make any difference, it will be the same code.
I am wondering why this code is even needed? The BackgroundColor is a permanent setting, it is saved with the form, so you shouldn’t need to change it when opening the form (unless I suppose you think it got saved with the wrong color?)
The only reason the background color is being set on launch is because it’s a part of the script that toggles a timer on or off. While running, if the status of the timer is changed, the color changes too.
The timer needs to be dealt with on launch, to run or not to run, so the background color has just been dragged along there.
It would make little sense to pursue it, but technically I could evaluate the color on launch to see if the timer should run or not.