I didn't test this in WinDev 7/8/9, but under WinDev 5.5, you can achieve some quite interesting spider web using multitask and events... I suppose that you can generate the same kind of problems under the last version of WinDev, and that one answer to that problem is the new function MultitaskRedraw...

There is a reason why the Help file tells you that you should never use Multitask in an event code... The only problem is that it's not enough to be on the safe side...

First, let's see what's happening when you use a multitask -1 to interrupt a process, which is a legitimate way to code, as long as you are careful: In your main process loop, you are going to write something like:
multitask(-1) 
if gwbInterruptRequested=true then...
and in the then, you are going to write the code necessary to end your process cleanly
At the same time, in the window where this process occurs, you will have grayed all the controls except the interrupt button, in which the only code will be:
gwbInterruptRequested=true

When you are doing that, your main process loop is interrupted each time you reach the multitask(-1), and if the user has previously clicked on the interrupt button, the click event will fire and your interrupt button code will be executed. At the end of this code, your main process code will continue at the line just after the multitask (i.e. the test on the flag)...

Problems are starting as soon as you deviate from that very simple schema... First, if you put more code in your button than just setting a flag.... And the problem is to define precisely what MORE code means... At some point, when the number of line executed (and therefore when the execution time) exceed some limit, the main process will NOT restart at the line following the multitask... It looks like the return address was lost somewhere... You have therefore a code that was interrupted and never finished, and if you were allocating dynamic objects in that code, you didn't have the chance to delete them... Here is your memory leak...

That case is quite simple and easy to find, just remember not to have any lengthy code in that button (or in a procedure called fro that button).

But things are getting more complicated when you are doing more complex process... Let's say that you are have multiple windows open at any given time (like any modern UI does), and that you are using events in some of them (by example to process the resizing on the fly, detect the click on a table column, etc...)... And now you have this lengthy process in one window and you want to display the content of the record currently processed... The normal way to achieve that is to do a multitask() (no minus sign this time) in your process, just after you set the value you want to display in a window field...

The one thing that is NOT written in any help file I'm aware of is that when the code encounter a multitask, events are firing... Which means that if during your process, the user TRIED to resize a window, or clicked on a table column, these events that were quietly waiting to be processed are going to be processed now, in the middle of your normally not interruptible code... At least some of them will... And depending of the length of the event management code, the program will continue or NOT at the line following the multitask...

And that's not the end of it... Till now, we just had one process interrupted, and possibly some objects not cleaned in memory, so nothing TOO bad (except that your process never finished of course :-)

But what will happen when all that code is part of the same class? More precisely, what will happen when one dynamic object was running the main process in one of its methods, and when the event management is executed in another of its methods... Of course, I'm not aware of any inside information on the WinDev compiler (or should I say interpreter), but I have done enough work both on compilers in general and with WinDev to have a good idea on how things are organized... Each instantiated object clearly has internal pointers, stack of calls, and so on... It can be organized in one general stack or with one stack per object, but still, any compiler/interpreter needs to keep track of the returning address for each call of a method.... Let's have a look at what this kind of stack looks like when the main process has been called, and when it has called a secondary process:
The stack will obviously contain a return address for main process, and as the external event is also calling a method in the class, another returning address for this call... So when the event code doesn't return to the main process, what happen to the object stack and pointers? They still contain some information that is not accurate anymore... The funny part with that one is that not only the error message doesn't appear immediately, but depending of what process is called after that, you can in certain cases have a program working perfectly well... And in others crashing 10 minutes later...
Under WinDev 5.5, a class method name is supposed to support up to 30 characters, but it's a very good idea to never use more than 25... The trap is that you can enter up to 30 when you define a new method, but if you do, you WILL get a GPF... Of course, not always immediately (seems OS dependent)... 
What's even more amusing is that it's not the name of the method per se who is causing the problem but the name used on the first line of the method, in the procedure xxx() statement... Nothing is done to avoid that you modify the name of the procedure on the first line (of course I strongly suggest that you keep that name the same than the procedure name itself), but when you rename a method, this line is NOT changed by the editor, so you can have discrepancies here... So, if you rename a method and extend its name, but don't change the first line of the code and therefore don't exceed 25 characters here, nothing happen (at least immediately)... And on the contrary, if you have a method whose name is no more than 25 long, but where you have change the fist line of code with a name longer than 25, you will get a GPF at some point...
During my test, I created a very simple class with 2 methods (one 25, one 30), each only doing an info("Test25") or Info("Test30")... Of course these two methods had name 25 and 30 long. In my only window, I was doing:
oTest is clMyClass
oTest:Test567890123456789012345()
oTest:Test56789012345678901234567890()
You would think that you would get the GPF when trying to execute the 3rd line above... But no, I got a GPF FIRST, and when I clicked ignore, I got the display of Test25, and then the display of Test30... In a 3 lines long program, the GPF was already not occurring at the exact time of the execution... Imagine the kind of problem to find what's going on in a big and complex project...
 
And the same thing is true for the file fields, try it, it's very interesting... You can type up to 30 characters in the file field name... Let's say that you define a file named Test2530 (abbr. TE), and a field in it called sName3089012345678901234567890... Everything works well in the analysis editor...
If you use in your code TE.sName3089012345678901234567890, everything works fine (sometime), but if you use Test2530.sName3089012345678901234567890, you have not one message but TWO in the code editor saying that the identifier is too long... Of course, if you are using the indirection operators { and }, you have no message in the code editor, but you'll get one at the execution time...
Try it with sName25890123456789012345, and you'll have the same problem... Try it with a name 24 characters long, and you have no problem...
And in some cases (but I haven't been able to identify exactly when), you will get a GPF, and of course not exactly when you are using this variable, but later...
 
 
 
Nearly every printer has a technical margin on at least some of the paper sides. What am I talking about? A technical margin is a space on the side of the paper where the printer can not print, by construction. It's basically the space reserved for the printer to handle the paper and move it. 
 
A few years ago, you could find some rare printers (laser and inkjet) with 0 technical margins... The result was that you were able to print a page black in it's entirety... Today, there is generally a technical margin on each side of the paper, for several reasons:


Building printers with a technical margin simplify the mechanical problem and allows to sell them cheaper
The need for a zero technical margin has nearly disappeared: programs are now working under windows, with printer drivers able to dynamically 'fit' your print job into the available space, and background images have replaced using preprinted paper for all administrative tasks or nearly all of them
The few people still using preprinted paper are generally doing so with several layers of paper and need to use the old and noisy impact printers anyway
Now that you know what I'm talking about, what is the problem?
When you design a report, or when you code it using iPrint instruction, be really careful not to print in the physical margins... If you do so, you will end up with unpredictable behavior when you print. It's unpredictable because depending on your printer driver (yes, even the version of the driver can make a difference) and also on your operating system. 
It seems that some printer driver have a built in safety and ignore the print order in the margin, while others are interpreting them erroneously (ever seen 200 blank pages being ejected between two of your report pages?), and in some cases you will even have a bad and hard crash (it seems that Windows XP really doesn't like this kind of things)...
So what can you do to prevent that?
If you are printing using the report editor, leave big enough margins all around your report to accommodate ANY printer (yes, you ARE loosing real estate)...
If you want to use all the available space all the time, you will need to code you report by hand using iPrint instructions (no, it's not that hard, and I even have a file available on the subject, with a complete methodology and class that you can reuse coming soon on this web site)... In that case, you can start your job by:

Testing that there is a printer declared on the system (see Printing on no printer)
Calculating the physical margin on each side of the paper
Then printing only inside those physical margins, and assigning the available real estate on the fly to the columns/area that need it the most
This was not per se a RANDOM error, but it certainly looked like it till we found the cause.
A customer of mine had a report working perfectly well and suddenly not working at all anymore, both at a customer's site and in test on his development machine. So, of course, the first question was: what changed? And there was the problem. It didn't look like anything had changed at all.
So let's give you the end result, instead of going through each different unsuccessful test... The problem was coming from a combination of:
- 32 bit OS (limited RAM)
- query using nearly all the memory before the printing
- not enough memory remaining for the printing operation
As a result, the printing would either display a misleading message (impossible to connect to printer), freeze the screen completely, or just exit the current window. The printer itself would not print the report but would get enough out of the operation to be unable to print anything else afterward without a reset. And nothing at all would say anything about memory.
The fact that the problem was occurring on two different system was also highly misleading, and due only to the fact that both systems were 32 bits and the same dataset was used on the two systems, using the same amount of memory. Now why did it suddenly stop working? Probably because a few records were added to the dataset, using just enough extra memory to create the problem.
The solution? Modifying the query to remove unused fields was enough! Just a reminder that even with today's computers, saving memory should always remain important.
Of course, not everybody calls UpdateWindow in the first place... A lot of developers don't even know this API.... So first, a quick overview on why and how I'm using it:
 
When you are processing a file (by example) and you have to do a complex statistical calculation of all of its records, you may want to display the name of the record currently processed... At this point, you should know that to do that, you will have to use the multitask instruction. If you already know the MultitaskRedraw instruction, stay with me, because this can interest you anyway...
In that case, the multitask instruction problem is that it's going to slow down your process... a LOT... And that's at least one of the reason why the MultitaskRedraw instruction has been introduced somewhere between the versions 5.5 and 9.
Let's say that a process duration is around 1 hour without display... With the display and the multitask, you can double that time. In some case, you can even have it worse than that (depends of the hardware you are using and your process)...
By using the API call UpdateWindow, you are requesting from the API that your window be refreshed. You can even give the handle of the FIELD you are using for the display instead of the handle of the whole window for an even faster processing... And suddenly you can display the name of every record you are processing and see no noticeable time difference.
Once again, I'm not privy to any PCSoft insider information, but it seems to me that the MultitaskRedraw instruction is doing precisely that, calling the UpdateWindow API...
So how can that create a random error? The good news is that it's not easy... And it seems that it's OS dependent... So if your program is supposed to work only on recent machine with the last version of Windows on it, you are probably safe... Bit if it can also work on older version like W98, be careful...
I had the problem when running a lengthy process (even with the UpdateWindow optimization, it was running for something around 1h30 or 2 hours). After perhaps 45 minutes of processing, I had a windows crash because of a stack overflow...
It took me some time to figure out that the UpdateWindow call that I had been using for years was responsible for that... It seems that this call is (like any other API call) setting information in one of windows stacks... And my process was so long, so resource intensive, and so not releasing the processor ever by doing a multitask that the stack was never cleaned.... After a while, the stack overflowed!
At this point, I modified my method encapsulating UpdateWindow to add a counter, and each 'n' calls (generally 100), I did a multitask() instead of the UpdateWindow. The multitask here is giving the OS enough time to clean after itself and solves the problem.
So why consider using UpdateWindow instead of multitaskRedraw? For SPEED!
MultitaskRedraw will aways redraw the WHOLE window, while you can give the handle of ONE field to UpdateWindow. And this can make a HUGE difference in the time taken by the screen being refreshed.
 
 
Once again, it's a gray area and something that is easy to find in simple cases... But when you are working in a complex UI and your process involves several classes and subclasses, you can in fact quite easily end up sawing the branch you are sitting on: 
 
Let's say that a process starts in a window, by a right click menu, by example. This process is in charge of opening another window, resizing this and that, loading data to populate the window and so on... And somewhere in this code, it also closes the calling window... 
Even if the closing instruction was the last line of code executed, you would still be in trouble.
At this point, you have destroyed the object that was calling your processing code, which means, if you think again in terms of compiler and stack, that you have removed from the stack the object to which you have a returning address pointing in your current code...
The amazing part is that your program is going to continue working fine after that, at least for some time... And then, when you are executing some perfectly legit code, you will have a GPF...
Of course, the first question that comes to mind is why do such a thing? Why don't you just set a flag, close the first window, and in the main window do everything else because of the flag? The answer? Requirements... When the visual effect of the first window closing and then another one opening is outlawed, you have to find a solution...
So how can you solve that problem? One solution is a simple action list managed in the main window... In your originating event (in this example the right click menu item), you just add a line in the action list. In the main window, you have a timer looking for new actions to execute. When you start to execute one action, you stop your timer, as usual, and then you start your processing. Now there is 0 processing code originating in a window that will be closed, everything originates from the main window.
If you prefer to send a PostMessage to the window to be closed, JUST DON'T...
The first reason why you shouldn't is that your code is still originating from the window you are closing, and even if this window is closed after the code you are running, you don't know exactly when the code of the new window will run (before or after the original one is closed) and therefore what will happen to your address stack...
The second reason is that if you are confronted to a situation that requires this kind of problem solving, there is a very good chance that you are not working alone, and that your classes and subclasses are at some point calling code that you didn't write. You don't know if in this code somewhere there isn't a Multitask that will let the receiving event of your PostMessage fire before your own code is finished... And if you want more information about the Multitask/event problem, read the multitask article.
The fRead instruction allows you to read N bytes at any position in to a direct file... Depending of the version of WinDev the number of bytes you can read in one operation vary greatly, but the fundamentals of the function remain the same.

The only problem that is not written in any help file I was able to read, is that if the number of bytes you request to read is greater than the size of the variable in which you are setting them, nothing will happen... at first...
The number of bytes you requested WILL be read... The only problem is that you don't know WHERE the end of the data will be stored... You will therefore end up with a part of your memory space overwritten by the data you read from the file... And depending of what is stored in memory after your variable, the results will be different...
You can have changed the value of another variable of your program, and with luck, the value you set is binary compatible with the type of variable you have overwritten... In that case, you will just have an incorrect result the next time you will use the variable content... Anywhere in your program...
If the binary content is incompatible with the variable type (which is much more likely), next time you try to use it, you will have an error message that will seem to come out of nowhere...
And of course, at the time any of this symptom becomes visible, the guilty fRead is buried far far away from the code that seems to crash... I have personally reproduced that problem in WinDev 5.5 and 7.5/8... I haven't tested it in WinDev 10... So let me know if you see any difference.
Building a beautiful and modern UI, with lots of panes/windows interacting with each other can become a nightmare really incredible... Of course, in that case, you have a lot of events firing, because you want to do some special things when resizing, when right clicking, and so on...
 
Well if you want to play with that kind of things, you'd better keep in mind that just knowing what window has the focus can become a very difficult task... Imagine some of your code running and at the same time the user click on another window...
- When does the focus leave your previous window?
- Do you have any multitask somewhere in your code that will let another code execute during this time?
So, if everything works well in test mode, but the behavior in executable mode is totally different (or the contrary), and of course, if you run your code in the debugger, everything is perfect, then there is a great chance that you have a focus problem...
If you use the debugger, your debug window is stealing the focus anyway, therefore don't expect any result for debugging that way...
If you do a TRACE, an info, or any other kind of display, you are ALSO changing the focus management. Isn't it great when as soon as you display anything in the trace window, everything works fine?
How do you solve this kind of problem?
- First, you verify your code: you should NEVER rely on instructions like WinInput/FenEnExecution in complex UI. You can generally use them during the window initialization to get the window alias, but you should store that information and use the stored version everywhere else. WinInput gives you the window currently having the FOCUS, even if you are executing a code in another window (WinDev 5.5 behavior, not tested in 7/8/9...)
- Another NO-NO is to access the window field by the field name alone... Always use the WindowAlias.FieldName syntax... If your UI uses the same window several times, you'll be happy to see the current field being used.
- Finally, never suppose that a window HAS the focus at any given time... Give the focus explicitly to the window who needs it in your code logic... And DON'T put any code in the gain focus/loose focus sections of your windows... If you do, you can end up with bad loops going on in a complex UI...
- Basically, if you are planning on building such a complex UI, you should manage everything from the outside, in a manager class by example, and NOT rely on the code in each window being executed by events. If you choose that last method, you will depend on your events being executed or not, and you will never be sure of when the code is executed, and how many times...
Finally, how do you debug this kind of problems? We already saw that the debugger is out, and so are trace and info windows... The first thing you can try is to have a second executable that will display the information instead of your main one, solving the internal focus stealing problem. If you do that by message and shared memory, you end up with your main program waiting for the secondary one to do the display, introducing delays that WILL change your UI behavior (events being fired before or after, etc...).
Remains only the old 'write in a file/read from the file method'... You still need an external EXE to read from a trace file and display your debug info, but in your main program, you just need to write debug lines in a direct file, without waiting for anything. Of course the resulting trace display is asynchronous, and writing in a file will slightly slow down your program, but it's the best solution for this kind of problems.
 
And what if it was NOT your code, for once?
 
From time to time, you can have a problem that is coming entirely from the outside world... How do you make the difference? A lot of testing I'm afraid...
Of course, if the same problem happens on every PC and every OS, you can probably be sure that the problem is internal. But what if it happens only on some machines? Some of the coding problems described in the "random error page" can give you these exact symptoms (Printing in the margins, printing on no printer, not mapped network drive, using updatewindow on machines of different speed...)
Well, if you are not in one of the known problems that can seems to happen only on some machines, you can try one of the following things, on the machine on which the problem happens. All these thing should be tried one after the other, with a test reproducing the problem between each...
- First be sure that you cannot reproduce the problem by following the same steps on your development machine
- Then be sure that your OS is up to date (Windows Update is clearly NOT optional in that case), that your drivers are up to date too, and that any resident program running on the machine is of the last available version
- Deactivate any resident program (anti-virus, anti-spyware, firewall, and so on)... All these programs are trapping events and are sometime confusing a perfectly legitimate code with malicious behavior
- Also deactivate any automatic OS behavior (scheduled task, drive content indexation, etc.)
- Deactivate any non necessary service running on the machine
- If your program still doesn't work, uninstall these programs... I saw once an anti-virus being deactivated and still blocking the install of one of my software...
- If you have a machine with the same hardware available, you can also install only the OS, and try again... If you still have the problem, you are in either for a big debugging of your code, or a big hardware swapping feast... You can try to replace part of your machine (network card, video card, and so on, to see if your problem is driver related)
- And if none of that works, you can try to blame it on WinDev... Of course that will NOT solve your problem, but you can always ask PCSoft if they have ever seen the problem and found a solution... By the way, is your WinDev Up to date? Each update brings it's share of debug, and staying in the back of the class is not a good idea...
 
v. 2.5.288.87
WinDev, WebDev and WinDev Mobile consulting
(empty)
Loading...