Link to home
Start Free TrialLog in
Avatar of Evarest
Evarest

asked on

Error code 87

Hi,

I've a program that needs to run on both WinXP and Win2k systems. However, it seems not to run on a Win2k system, as it generates a System Error Code 87 (ERROR_INVALID_PARAMETER). As i only have Delphi installed on a WinXP system, i cannot debug the program on the Win2k system.

The program doesn't directly use many lowlevel WinAPI procedures. It uses the RunningProcessesList from JCL and it reads and writes some values from the registry.

I really have no clue why it won't run on Win2k. My experience was that most API calls where supported by both WinXP and Win2k. Do you have some list with API calls that aren't supported by Win2k? Maybe i'll then find the causer of my problems.

Also a list with procedures that might trigger such a very undescriptive error message would be welcome...

Thanks in advance for any help!
Evarest
Avatar of Wim ten Brink
Wim ten Brink
Flag of Netherlands image

It might be easier to just use WMI to get an overview of all running processes. But okay...

For a list of procedures and the platform requirements, visit http://msdn.microsoft.com/library/ for the MSDN library. Here's there are complete overviews of the Windows API, the valid parameters and on which platforms it runs. But the MSDN site is huge...

Now, about retrieving a list of all running processes, you could just use WMI. Do do so, you first need to import the C:\WINNT\system32\wbem\wbemdisp.tlb type library which will handle the process retrieval. Basically, all you'd need then is this code:

uses
  SysUtils,
  Windows,
  ActiveX,
  Classes,
  WbemScripting_TLB in 'WbemScripting_TLB.pas'; // This one was created by the import.

function ADsEnumerateNext( pEnumVariant: IEnumVARIANT; cElements: ULONG; var pvar: OleVARIANT; var pcElementsFetched: ULONG ): HRESULT; safecall; external 'activeds.dll'; // Delphi doesn't know this function.

procedure DumpProcesses( const List: TStrings ); // Fills list with executable names.
var
  Enum: IEnumVARIANT;
  varArr: OleVariant;
  lNumElements: ULong;
  Process: SWBemObject;
begin
  Enum := CoSWbemLocator.Create.ConnectServer( '', 'root\cimv2', '', '', '', '', 0, nil ).ExecQuery( 'Select * from Win32_Process', 'WQL', wbemFlagBidirectional, nil )._NewEnum as IEnumVariant;
  while ( Succeeded( ADsEnumerateNext( Enum, 1, varArr, lNumElements ) ) ) and ( lNumElements > 0 ) do begin
    Process := IUnknown( varArr ) as SWBemObject;
    List.Add( VarToStr( Process.Properties_.Item( 'ExecutablePath', 0 ).Get_Value ) );
  end;
  Process := nil;
  Enum := nil;
end;

Or do something else with the process information in the Process object above. There's lots of useful information inside it.

About the Windows API, my experience is that you really have to be very careful since even the smallest service pack might disable your nice procedures. Fortunately, those things won't happen very often.
Avatar of Evarest
Evarest

ASKER

Dear WorkShop_Alex,

as in the other post about WMI, i'd rather not use it (memoryleaks and slow). My app now does exactly what it should do, and oddly, it seems not to be the RunningProcessesList procedure which is causing all this trouble. My users are telling me that whatever they do, they get this code 87 error...

As i'm not calling WinAPI directly from anywhere in my code except the RunningProcessesList, I cannot say what's the problem.

As you pointed out, MSDN is not exactly the best place to go searching for an answer. And it's quite impossible for me to send my users an app with on every line of code a messagebox so i know where my app finally goes wrong :-)

I thought that maybe an expert had had a similar problem (code 87 but quite difficult to point the source). However, as it seems, it's not going to be the case...

Thanks anyway,
Evarest
you don't have to show a message box in every spot you want to find out what's going on... what you do is use a .log file instead, the user keeps going and finds the errors, and you send all the information, every exception, every error to the log file, if you're good enough with exceptions and error trapping, your .log file should tell exactly where the problem is
Avatar of Evarest

ASKER

"if you're good enough with exceptions and error trapping, your .log file should tell exactly where the problem is"

That's true: the only problem is that it only returns the very descriptive error message:

System Error.  Code: 87.Falscher Parameter.

And this is not really something that can help me any further...
Avatar of Evarest

ASKER

Quite a nice note: this error message occured after a program uptime of 220 milliseconds :-)

It seems that the problem is located somewhere in the beginning of my code. Also, it seems that not all systems running Win2k have these problems...

It's getting worse and worse...

Evarest
ASKER CERTIFIED SOLUTION
Avatar of Wim ten Brink
Wim ten Brink
Flag of Netherlands 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
Avatar of Evarest

ASKER

I've sent a debug version of the program to my users. I hope they will run it and send the debug file to me.

I'm using the registry LOCAL_MACHINE, but i've encapsuled that into a try...except block. That should be OK then...
Avatar of Evarest

ASKER

Workshop_Alex,

i think you struck gold: the problem seems to lay in the access restrictions posed upon limited accounts. Stupid me, but it never crossed my mind that the user with the problems could be working on a company PC, which of course only has limited rights.

I think I've solved most problems with the Registry, but i still have the following problem:

I need to get the complete path of a process by its PID. You can do this by calling

OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, PID);

and extracting some info, but this is ONLY possible in administration accounts (thus full rights). PROCESS_QUERY_INFORMATION will give you an error in limited accounts.

Is it possible to get the full path of a process by its PID?

Thanks in advance!
Evarest
1. Take a look at your Win2K maintenance level.  Some WinXP API calls were retrofitted into Win2K with one of the service packs.

2. Is your program running as a service?

3. Have you done an installation on the Win2K machine or just copied the executable?  There may be some dependent DLLs you are missing.

4. For debugging purposes, you consider Codesite (from Raize Software).
Evarest, it was a lucky guess but hey, I remembered that I did have similar problems once which were related to the registry being write-protected in some areas.

And yes, limited accounts are as the name implies, limited. To get around such problems you need to create a work-around solution. In one of the projects that I've worked on we solved these issues by creating a system service that would be running under an administrator account, thus having full access. By communicating with this service we could give normal users access to things that normally only administrators could do. Later we changed the model from a service to COM+ applications, that were set to run on a server with administration rights but this was because we changed to a multi-tier solution.

I'm not sure if there's an easy way for a limited user to override the security in some way. It would actually be a breach of security if this was possible because technically this could mean the limited user might also disable or close certain processes. (E.g. the antivirus or firewall software.)

I know you dislike WMI because of the memory leaks but still, it bypasses these security issues if I'm not mistaken. I've chosen for WMI in many situations just because of time limits. If you have a deadline in 5 days, you just can't spend 4 days trying to find some alternative solution, then do the rest in one day... If your project has a time-limit too, considering using WMI for now so your users will have a working version. That should buy you some time to improve things for the next version.
Avatar of Evarest

ASKER

Dear WorkShop_Alex,

yesterday i managed to create a more stable version of the program by limiting access to the registry and setting the OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, PID); between try...excepts.

The OpenProcess will now only give problems in an WinNT4 environment, but that's then a pitty for them... The problem is that there i can only use EnumProcesses to get information about the running processes. With the PIDs I'm able to extract the paths, however, the  PROCESS_QUERY_INFORMATION will fail in a limited account, resulting in no paths for me.

In WinXP, this problem can be prevented by using also the filename (not the entire path), which i get from the ProcessEntry32 of CreateToolhelp32Snapshot. Using this filename, i can scan for files with the same name and returnt their paths.

Actually, there's only a problem with getting PROCESS_QUERY_INFORMATION from files residing in the C:\Windows or C:\Windows\System32 folders... The rest doesn't pose any problems...

Sadly, WMI still isn't an option, as the monitoring program needs to be running indefinately, and i cannot afford any memoryleaks (have had some in the past, and that wasn't the nicest experience to have...)

Wrt Codesite which Aikimark suggested: as i'm only a poor student :-) I can't really afford such a tool...

Evarest
It might be a good idea to just write a test application that uses WMI in a tight loop and see how much memory it will waste if you keep it running for a day. If it continuously keeps leaking memory it's not usable. But if it's stops wasting memory after a while, it can be used. Some components are just reserving memory for any further calls, cashing data if need be. WMI is probably doing something similar too.
As an alternative, you might create a small process that just gets the information you wants and writes it in a temporary file. All you have to do then is call the process and read the file afterwards. The lost memory will be reclaimed when this small process ends.

Running indefinitely on Windows systems is a bit rare since there are quite a few parts of Windows that will sooner or later bring down the system. I did write a scheduler application once which was supposed to run indefinitely. This too needed to run indefinitely but it was leaking a bit of memory too. However, the amount of memory loss after a week was still not enough (about 5 MB) to be very troublesome. Thus, as I say, find out how much memory you will be losing and check if this is an acceptable amount.
Or use a little trick and let your application restart itself every N days, thus the process is cleared and with it all lost memory. ;-)

I know, these are just work-arounds. But limited user accounts will never be able to use PID's just because of security reasons. But there's an alternative by creating a system service application. These can be executed by a specific user account, which should be the administrator. All you need then is a way to communicate with the service, telling the service to provide you whatever you need to know. The server will be running under administrator privileges, thus it will be able to read whatever you need to read. And services aren't too hard to code...
Avatar of Evarest

ASKER

[quote]But there's an alternative by creating a system service application. These can be executed by a specific user account, which should be the administrator. All you need then is a way to communicate with the service, telling the service to provide you whatever you need to know. The server will be running under administrator privileges, thus it will be able to read whatever you need to read. And services aren't too hard to code...[/quote]

For the project i'm working on, this isn't really of use. However it might come in handy lateron, in another project. Thanks for the idea!

Evarest