Link to home
Start Free TrialLog in
Avatar of YodaMage
YodaMage

asked on

A Win 32 API Function Failed / Error Code 87

I've got an application which runs fine on XP and 2000 machines, but fires this error when running on 98/98SE stations. Out of curiosity I've even created a development machine running 98 and recompiled there, but no luck. The error does not occur on the same line of code, but rather almost randomly at different points in the app (though usually when creating a form with tons of components, rolling a pointer through a busy form, opening a bunch of MDI forms which usually have a pagecontrol on them....). Is it a file handle issue? Are file handles handled differently between 98 and 2000?
Avatar of simonet
simonet
Flag of Brazil image


You haven't given enough details for us to work the problem.

What Win32 API calls are you using?


Alex
Avatar of Madshi
Madshi

Might be a resource problem. Please check the system resources (right click on my computer) then run your program for a while, then check the system resources again. They may be a bit lower, but not too much. If they're near 0, this will result in a lot of API failures. Windows and bitmaps can't be created anymore, and so on...

Regards, Madshi.
P.S: The resources are quite limited in 9x/ME, but not in NT/2k/XP.
87 means a parameter is incorrect or invalid (I think). Maybe somewhere you call a function with a parameter that only makes sense on NT/2K?

I believe some functions exists on both 9x/ME and NT/2K, but can be called with parameters that are only valid on NT/2K.
There are 172 entries to Error 87 in the January '02 issue of MSDN.

The one that struck my attention the most is - as Madshi already pointed out - related to a incorrect parameter being passed to an API calls. It does't make any references to the nature of the error: invalid type, invalid data, invalid data size, etc.

Run your application in a 9x machine, steping through it with Delphi to see which call is generating the error.

Remember that not all calls that are present in WinNT are present in Win9x and vice-versa. Many functions that are present in WinNT are not supposed to work in Win95.

Without a code snippet or some more information, it's really hard to help you.

Alex
>>>he error does not occur on the same line of code, but rather almost randomly at different points in the app

I would say because of the above statement that I agree with Madshi. Becasaus it is conjucntion with this statement.

>>>though usually when creating a form with tons of components

http://www.windows-help.net/techfiles/win-resources.htm
l

"USER has a 16 bit heap and two 32 bit heaps. One of these 32 bit heaps stores WND (window) structures. There is a WND for every window in the system. The structure holds important info about the window. The other 32 bit heap stores menus. The 16 bit heap stores things like window classes, message queues etc.

The User component also manages input from the keyboard, mouse, and other input devices. It also manages interaction with the sound driver, timer, and communications ports. Windows 98 uses an asynchronous input model for all input to the system and applications. As the various input devices generate interrupts, the interrupt handler converts these interrupts to messages and sends the messages to a raw input thread area, which in turn passes each message to the appropriate message queue. Although each Win32-based thread can have its own message queue, all Win16-based applications share a common one.

GDI has a 16 bit heap and a 32 bit heap. GDI uses its heaps to store fonts, brushes, fonts, palettes, bitmaps and pens (graphical stuff).

The GDI is the graphical system that manages what appears on the screen. It also provides graphics support for printers and other output devices. It draws graphic primitives, manipulates bitmaps, and interacts with device-independent graphics drivers, including those for display and printer output device drivers.

"Free resources" are just the amount of memory left in the GDI and USER system heaps.

Windows 9.x incorporates the Windows 64KB system-resource limit for better performance when it is providing backward compatibility.


Resource limits in Windows 3.x / 9.x / NT

Resource  Windows 3.x  Windows 9.x  Windows NT  
Window/Menu Handles  about 200  32KB (each)  Unlimited  
Timers  32  Unlimited  Unlimited  
COM/LPT ports  4 each  Unlimited  Unlimited  
Listbox items (per listbox)  8KB  32KB  Unlimited  
Listbox data (per listbox)  64KB  Unlimited  Unlimited  
Edit control data (per control)  64KB  Unlimited  Unlimited  
Regions  All in 64KB segment  Unlimited  Unlimited  
Logical pens, brushes  All in 64KB segment  64KB segment  Unlimited  
Physical pens, brushes  All in 64KB segment  Unlimited  Unlimited  
Logical fonts  All in 64KB segment  750-800  Unlimited  
Installed fonts  250-300 (best case)  1000  Unlimited  
Device Contexts  200 (best case)  16KB  Unlimited  


The table is only an approximation. It is difficult to list exact limits for GDI objects in Windows 3.x, since all regions, physical objects, logical objects, DCs, and installed fonts have to fit in a single 64KB segment. Moving these into the 32-bit heap in Windows 9.x provides more room for the remaining (small) items such as logical pens and brushes. The remaining items in the Windows 9.x local heap are all less than 10-20 bytes each. "


The Crazy One
Avatar of YodaMage

ASKER

I posted more info last Friday, but somewhere down the line it was eaten.

We tracked the issue to TwwDBComboBox and file handles as Madshi alluded to, then got this back from Paul Woll:

Windows 98 has limitations on the number of windows handles, that is easily
exceeded when people use page controls.  Generally combo controls use two or
three window handles depending on what type of control.   Windows 2000 is
pretty much unlimited, so you won't have this problem.

Your solution would be:

1) Make sure you have a more recent version of InfoPower as we have
optimized this a bit.
2) Don't use tabsheets in this manner.


Some good general techniques are some tips to reduce resource usage from
www.tamaracka.com/search.htm are:

1) Create a form for each tab page and parent the form to the tab sheet at
runtime when changing pages.  There are examples in
www.tamaracka.com/search.htm that demonstrate how to do this.  Then free it
when changing pages as  well.
2) Don't auto-create forms. Only create a form if it needs to be displayed
and free it if you are done with it. You can remove a form from the
auto-create list in Project | Options... | Forms | Auto-create forms. The
easiest way to free a form after it closes is to set Action to caFree in the
OnClose event.
3) Don't put too many controls on a single form. Move less used controls to
a
second form that is only created if the used clicks a 'detail' button.
4) Avoid using TComboBox controls (and descendants). These controls take up
3
handles instead of 1 !
5) Use the following OnChange event for every TPageControl to free all
window
handles on hidden tabsheets:

type
  TMyTabSheet = class(TTabSheet);

procedure TForm1.PageControl1Change(Sender: TObject);
var
  i: Integer;
begin
  with Sender as TPageControl do
    for i := 0 to PageCount - 1 do
      if ActivePageIndex <> i then
        TMyTabSheet(Pages[i]).DestroyHandle;
end;

Lloyds Helpfile (you can find it on the Inprise website:
www.borland.com -> delphi developer support -> delphi downloads, at the
bottom of the page - Delphi Extras - Lloyd's help file) has an example for
this.

When you do this, you will be able to see the controls destroy
themselves from the TabSheet before the page changes, which causes the
page change to be slower and is visually somewhat distracting. To avoid
this, you can use the LockWindowUpdate() API function to lock the
PageControl prior to the page change, then unlock it after the change is
completed, in the OnChanging and OnChange events, respectively. This
improves page change performance dramatically. Here is the code:

procedure TForm1.PageControl1Changing(Sender: TObject;
  var AllowChange: Boolean);
begin
   LockWindowUpdate(PageControl1.Handle);
   TWinControlClass(PageControl1.ActivePage).DestroyHandle;
end;

procedure TForm1.PageControl1Change(Sender: TObject);
begin
   LockWindowUpdate(0);
end;

6) Replace groups of Tedit controls with a TStringgrid, a grid uses only two
window handles, regardless how many cells it has.

7) Try to replace controls that use Window handles with TGraphicControl
descendents, e.g. TPanels and TGroupboxes by TBevels. TGraphicControl do not
use window handles.

********I believe the best way is to make forms for each tab page and then
parent them. As this way you will use at least 1/4 the number of window
handles.  Thus preventing problems with other running apps.

If you want to keep only one form in existence then use both the OnChange
and OnChanging handlers. And i would also use a TTabcontrol with a panel
aligned beneath it as host for the forms instead of a TPageControl, saves
some more form handles for the tabsheets.

Ok, in the OnChanging handler you destroy the form on the tabsheet beeing
switched away from:

  With Sender As TPagecontrol do
    if ActivePage.Controlcount = 1 Then
      With ActivePage.Controls[0] Do Begin
        Perform( CM_DEACTIVATE, 0, 0 );
        Free;
      End;

In the OnChange handler you create the form for the new tabsheet:

  Var
    pc: TPageControl;
    fc: TformClass;
  begin
    pc:= Sender As TPagecontrol;
    Case pc.ActivePageIndex Of
      0: fc := TForm2;
      1: fc := TForm4;
      2: fc := TForm5;
    Else
      fc:= Nil;
    End;
    If Assigned( fc ) Then Begin
      With fc.Create( pc ) Do begin
        Parent:= pc.ActivePage;
        Align := alClient;
        Show;
        Perform( CM_ACTIVATE, 0, 0 );
      End;
    End;
  end;

Sending these CM_ messages will fire the embedded forms OnActivate and
OnDeacticate events, which comes in handy for initialization and cleanup
operations.

-Paul


 
I ended up having to drop parented forms on each page of my pagecontrols to account for the handle issue with the ComboBox components.
Wow...I requested a delet on this thing LONG ago...
ASKER CERTIFIED SOLUTION
Avatar of Computer101
Computer101
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial