NetWkstaGetInfo ACCESS_VIOLATION under WIN64 - delphi

I'm have code, which works fine during many years under WIN32. But yesterday, after compiling under WIN64 I'm got strange ACCESS_VIOLATION error when trying get wki100_langroup field. Where wrong this code? (Computer not in domain and in Delphi 11 debug window I can see, that this field is empty)
const
NERR_SUCCESS = 0;
type
WKSTA_INFO_100 = record
wki100_platform_id: DWORD;
wki100_computername: LPWSTR;
wki100_langroup: LPWSTR;
wki100_ver_major: DWORD;
wki100_ver_minor: DWORD;
end;
LPWKSTA_INFO_100 = ^WKSTA_INFO_100;
function NetWkstaGetInfo(ServerName: LPWSTR; Level: DWORD; var BufPtr: Pointer): DWORD; stdcall;
external 'netapi32.dll' Name 'NetWkstaGetInfo';
function GetDomain: string;
var
PBuf: Pointer;
Res: Integer;
begin
Result := '';
Res := NetWkstaGetInfo(nil, 100, PBuf);
if (Res = NERR_Success) then begin
Result := LPWKSTA_INFO_100(PBuf)^.wki100_langroup; // ACCESS_VIOLATION here
if Assigned(PBuf) then
NetApiBufferFree(PBuf);
end;
end;
Update:
After changing Pointer to PByte I have the same exception
function NetWkstaGetInfo(ServerName: LPWSTR; Level: DWORD; BufPtr: PByte): DWORD; stdcall;
external 'netapi32.dll' Name 'NetWkstaGetInfo';
function GetDomain: string;
var
PBuf: PByte;
Res: Integer;
begin
Result := '';
Res := NetWkstaGetInfo(nil, 100, #PBuf);
if (Res = NERR_Success) then begin
Result := LPWKSTA_INFO_100(PBuf)^.wki100_langroup; // ACCESS_VIOLATION here
if Assigned(PBuf) then
NetApiBufferFree(PBuf);
end;
end;
Screenshots from Win32:
and Win64 breakpoints:

The symptoms suggest that some other code changed default record alignment for WKSTA_INFO_100 record type.
Under 32-bit compiler that wouldn't have an impact because all values in the record are also 32-bit therefore they will be correctly aligned even if other alignment size is specified.
Under 64-bit compiler DWORD is 32-bit, while LPWSTR is 64-bit. With default 8 byte alignment that means there will be 4 padding bytes inserted after wki100_platform_id. If some other alignment is used following fields will not be at correct positions.
To correct this you need to specify 8 byte alignment {$A8} before type declaration.
type
{$A8}
WKSTA_INFO_100 = record
wki100_platform_id: DWORD;
wki100_computername: LPWSTR;
wki100_langroup: LPWSTR;
wki100_ver_major: DWORD;
wki100_ver_minor: DWORD;
end;

Related

Any RTL function to remove accents from a char?

Nowadays with Sydney, is there any RTL function to remove accents from a char (é becomes e for exemple) in a String? I know this question was already asked in the past but I would like to know if the answers are still accurate with Sydney - I would especially love to find a function that work on all platforms (the one I use right now works only through WideString and Windows API).
Found and modified an implementation that uses NormalizeString() from this article:
How to use NormalizeString function in delphi?
This works for me in Delphi 10.3 Rio (include System.Character in your uses clause):
function NormalizeString(NormForm: NORM_FORM; lpSrcString: LPCWSTR; cwSrcLength: Integer; lpDstString: LPWSTR; cwDstLength: Integer): Integer; stdcall; external 'C:\WINDOWS\system32\normaliz.dll';
function NormalizeText(Str: string): string;
var
nLength: integer;
c: char;
i: integer;
temp: string;
CatStr:string;
begin
nLength := NormalizeString(NormalizationD, PChar(Str), Length(Str), nil, 0);
SetLength(temp, nLength);
nLength := NormalizeString(NormalizationD, PChar(Str), Length(Str), PChar(temp), nLength);
SetLength(temp, nLength);
CatStr:='';
for i := 1 to length(temp) do
begin
c:=temp[i];
if (TCharacter.GetUnicodeCategory(c) <> TUnicodeCategory.ucNonSpacingMark) and
(TCharacter.GetUnicodeCategory(c) <> TUnicodeCategory.ucCombiningMark) then
CatStr:=CatStr+c;
end;
result:=CatStr;
end;

How to pass a pointer to a list of handles to the UpdateProcThreadAttribute function

I have an application that spawns multiple CreateProcess threads and I'm successfully redirecting the stdout and stderr output to text files for each one.
However, I've discovered the feature whereby the stdout/strderr handles are inherited by all such threads and not just the ones I want them to be inherited by. So I've embarked on a journey to use the InitializeProcThreadAttributeList, UpdateProcThreadAttribute functions and EXTENDED_STARTUPINFO_PRESENT and a STARTUPINFOEX structure in the CreateProcess function to get around this but I'm stuck.
If I use PROC_THREAD_ATTRIBUTE_HANDLE_LIST as the Attribute argument in UpdateProcThreadAttribute procedure it expects the lpValue parameter to be a pointer to a list of handles to be inherited by the child process.
For the List I've tried using a
TList<Cardinal>
and also creating an array of Cardinals but couldn't get either approaches to compile!
Question: How do I create and populate such a list?
Secondly, in this example it is using the functions and procedures from kernel32.dll but they also exist in the Windows unit too (I'm using Delphi 10.3) although the definitions differ:
For example, InitializeProcThreadAttributeList( nil, 1, 0, vAListSize ); won't compile using the Windows unit due to the nil argument because Types of actual and formal var parameters must be identical but I have no such issue using the one in kernel32
Question: Which version of these functions/procedures should I be using?
Thanks.
In case it is useful, here's my code to implement all of this:
type
TStartupInfoEx = record
StartupInfo: TStartupInfo;
lpAttributeList: Pointer;
end;
const
PROC_THREAD_ATTRIBUTE_HANDLE_LIST = $00020002;
function InitializeProcThreadAttributeList(
lpAttributeList: Pointer;
dwAttributeCount: DWORD;
dwFlags: DWORD;
var lpSize: SIZE_T
): BOOL; stdcall; external kernel32;
function UpdateProcThreadAttribute(
lpAttributeList: Pointer;
dwFlags: DWORD;
Attribute: DWORD_PTR;
lpValue: Pointer;
cbSize: SIZE_T;
lpPreviousValue: PPointer;
lpReturnSize: PSIZE_T
): BOOL; stdcall; external kernel32;
function DeleteProcThreadAttributeList(
lpAttributeList: Pointer
): BOOL; stdcall; external kernel32;
function CreateProcessWithInheritedHandles(
lpApplicationName: LPCWSTR;
lpCommandLine: LPWSTR;
lpProcessAttributes,
lpThreadAttributes: PSecurityAttributes;
const Handles: array of THandle;
dwCreationFlags: DWORD;
lpEnvironment: Pointer;
lpCurrentDirectory: LPCWSTR;
const lpStartupInfo: TStartupInfo;
var lpProcessInformation: TProcessInformation
): Boolean;
var
i: Integer;
StartupInfoEx: TStartupInfoEx;
size: SIZE_T;
begin
Assert(Length(Handles)>0);
StartupInfoEx.StartupInfo := lpStartupInfo;
StartupInfoEx.StartupInfo.cb := SizeOf(StartupInfoEx);
StartupInfoEx.lpAttributeList := nil;
Win32Check(not InitializeProcThreadAttributeList(nil, 1, 0, size) and (GetLastError=ERROR_INSUFFICIENT_BUFFER));
GetMem(StartupInfoEx.lpAttributeList, size);
try
Win32Check(InitializeProcThreadAttributeList(StartupInfoEx.lpAttributeList, 1, 0, size));
try
Win32Check(UpdateProcThreadAttribute(
StartupInfoEx.lpAttributeList,
0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
#Handles[0],
Length(Handles) * SizeOf(Handles[0]),
nil,
nil
));
for i := 0 to High(Handles) do begin
Win32Check(SetHandleInformation(Handles[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));
end;
Result := CreateProcess(
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
True,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
StartupInfoEx.StartupInfo,
lpProcessInformation
);
finally
DeleteProcThreadAttributeList(StartupInfoEx.lpAttributeList);
end;
finally
FreeMem(StartupInfoEx.lpAttributeList);
end;
end;
From your post it would seem that there are some declarations of InitializeProcThreadAttributeList, UpdateProcThreadAttribute and DeleteProcThreadAttributeList in the Windows unit in the latest versions of Delphi, but your post implies that they are incorrectly declared. The above code is known to work correctly.

Getting Manufacturer, Serial Number, Model from Drive Letter

I'm trying to have some effective code to get vendor, serial number, and model of a USB drive, using its drive letter. I've searched a lot and found several solutions. But I can not determine which one is the best one.
First one is here. Uses hid.pas from JEDI, but I don't know how to use it with a single drive. Specially, function GetHidDeviceInfo is very interesting but requires symbolic link rather that a drive letter. I tried to invite a symbolic link for the drive letter at no avail.
Second one is here. Uses WMI which doesn't seem very clean. My experience tells me that WMI doesn't work on all PCs. The code doesn't work on my own laptop, saying 'The RPC server is unavailable'.
Please advice me on the best way to achieve my goal. Are other ways around?
Update: I'm posting some sample code, combining the results of comments below.
{$APPTYPE CONSOLE}
uses
Windows, Messages, SysUtils, Variants;
type
PHIDDAttributes = ^THIDDAttributes;
HIDD_ATTRIBUTES = record
Size: ULONG; // size of structure (set before call)
VendorID: Word;
ProductID: Word;
VersionNumber: Word;
//
// Additional fields will be added to the end of this structure.
//
end;
THIDDAttributes = HIDD_ATTRIBUTES;
THIDUSBDeviceInfo = Record { contains interface level information of each device}
SymLink : String;
BufferSize : Word;
Handle : THandle;
VID : DWord;
PID : DWord;
VersionNumber : Word;
ManufacturerString : String;
ProductString : String;
SerialNumberString : String;
end;
function GetVolumeNameForVolumeMountPointW(const lpszVolumeMountPoint: LPCWSTR;
lpszVolumeName: LPWSTR; cchBufferLength: DWORD): BOOL; stdcall;
external kernel32;
function HidD_GetAttributes(HidDeviceObject: THandle;
var HidAttrs: THIDDAttributes): LongBool; stdcall;external 'hid.dll' name 'HidD_GetAttributes';
function HidD_GetManufacturerString(HidDeviceObject: THandle;
Buffer: PWideChar; BufferLength: Integer): LongBool; stdcall;external 'hid.dll' name 'HidD_GetManufacturerString';
function HidD_GetProductString(HidDeviceObject: THandle;
Buffer: PWideChar; BufferLength: Integer): LongBool; stdcall;external 'hid.dll' name 'HidD_GetProductString';
function HidD_GetSerialNumberString(HidDeviceObject: THandle;
Buffer: PWideChar; BufferLength: Integer): LongBool; stdcall;external 'hid.dll' name 'HidD_GetSerialNumberString';
function GetVolumeName(Name: string): string;
var
Volume: array [0..MAX_PATH] of Char;
begin
FillChar(Volume[0], SizeOf(Volume), 0);
GetVolumeNameForVolumeMountPointW(PChar(Name), #Volume[0], SizeOf(Volume));
Result := Volume;
end;
Function GetHidDeviceInfo( Symlink : PChar) : THIDUSBDeviceInfo;
Var
pstr : pWideChar;
DevHandle : THandle;
HidAttrs : THIDDAttributes;
Begin
FillChar(Result, SizeOf( Result), 0);
Result.SymLink := SymLink+ #0;
GetMem( pstr, 512);
DevHandle := CreateFile( Symlink,
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil,
OPEN_EXISTING,
0,
0);
If DevHandle <> INVALID_HANDLE_VALUE then
begin
If HidD_GetAttributes( DevHandle,HidAttrs) then
begin
result.VID := HidAttrs.VendorID;
result.PID := HidAttrs.ProductID;
result.VersionNumber := HidAttrs.VersionNumber;
end;
If HidD_GetManufacturerString( DevHandle, pstr, 512) then
Result.ManufacturerString := pStr;
If HidD_GetProductString( DevHandle, pstr, 512) then
Result.ProductString := pStr;
If HidD_GetSerialNumberString( DevHandle, pstr, 512) then
Result.SerialNumberString := pStr;
closeHandle( DevHandle);
end;
FreeMem( pStr);
End;
procedure Main;
var
VolumeName: string;
info: THIDUSBDeviceInfo;
begin
VolumeName:=GetVolumeName('i:\'); //assuming that I: is a USB drive
info:=GetHidDeviceInfo(pchar(VolumeName));
Writeln(info.SerialNumberString);
end;
begin
Main;
Readln;
end.
You can try to obtain SerialNumber of disk (and more information) using WMI.
Usin WMI and the Win32_DiskDrive class, you can get the Serial Number. The documentation say: "Windows Server 2003 and Windows XP: This property is not available."
In Windows Vista,7/8 works fine.
To try if this method is good for you, try this simple demo on clubdelphi ftp(source included and binary included). In Windows 7 you get information like this:
If you can obtain a correct serial, you can use WMI.
Good library for work with WMI is GLibWMI on Sourceforge. Include a specific component (DiskDriveInfo) that you can use with 0 code lines.
See demo.
Regards

Delphi code to get Owner of a Netware file not working

I'm a Delphi developer and have never programmed for netware. But I need to find the owner of a file on a netware share. After some research, I got this code snippet from a newsgroup (original author: Chris Morgan). It's basically a way to dynamically load netware dll and get the "owner" information of a file. Please look at the function GetNetwareFileOwner.
The problem is, I don't have direct access to a netware share for testing. I'm sending a small test program every time to a user who tests it by selecting a file on the netware share and then reports the results. I'm getting the error code by a small code insert after the call NWIntScanExtenedInfo where it fails with the error codes given below. Any ideas what can be wrong?
Error codes:
1) At first, the following code gave error 899E (INVALID_FILENAME) on the above call. The file name was in English--no special characters there. And the file was selected on the share with a regular File Open dialog.
2) After that, suspecting a case problem, I commented the two AnsiUpperCase lines to keep the name in original case exactly as the File Open Dialog received it. This gives the error 89FF now (NO_FILES_FOUND_ERROR).
P.S. I compiled the test with Delphi 2007. May be there is a structure problem of the top structure. I haven't checked the byte length and alignment. Will do so.
// netware records and function definitions
type
// sizeof(NW_EXT_FILE_INFO) should be 140 bytes - check byte alignment
NW_EXT_FILE_INFO = record
sequence: integer;
parent: integer;
attributes: integer;
uniqueID: shortint;
flags: shortint;
nameSpace: shortint;
nameLength: shortint;
name: array[0..11] of shortint;
creationDateAndTime: integer;
ownerID: integer;
lastArchiveDateAndTime: integer;
lastArchiverID: integer;
updateDateAndTime: integer;
lastUpdatorID: integer;
dataForkSize: integer;
dataForkFirstFAT: integer;
nextTrusteeEntry: integer;
reserved: array[0..35] of shortint;
inheritedRightsMask: word;
lastAccessDate: word;
deletedFileTime: integer;
deletedDateAndTime: integer;
deletorID: integer;
reserved2: array[0..15] of shortint;
otherForkSize: array[0..1] of integer;
end;
// functions defined in CALWIN32.DLL
TNWCallsInit = function(reserved1: pointer;
reserved2: pointer): integer; stdcall;
TNWCallsTerm = function(reserved: pointer): integer; stdcall;
TNWParseNetWarePath = function(const path: pchar; var conn: cardinal;
var dirhandle: cardinal; newpath: pchar): integer; stdcall;
TNWAllocTemporaryDirectoryHandle = function(conn: cardinal;
dirhandle: cardinal; const path: pchar; var newdirhandle: cardinal;
rightsmask: pshortint): integer; stdcall;
TNWDeallocateDirectoryHandle = function(conn: cardinal;
dirhandle: cardinal): integer; stdcall;
TNWIntScanExtendedInfo = function(conn: cardinal; dirhandle: cardinal;
attrs: shortint; iterhandle: Pinteger; const searchPattern: pchar;
var entryinfo: NW_EXT_FILE_INFO; augmentflag: shortint): integer;
stdcall;
TNWGetObjectName = function(conn: cardinal; objID: integer;
objname: pchar; objtype: pword): integer; stdcall;
const
FA_NORMAL = $00;
FA_HIDDEN = $02;
FA_SYSTEM = $04;
// return codes
SUCCESSFUL = $00;
NOT_MY_RESOURCE = $883C;
// get file owner for Netware server file
function GetNetwareFileOwner(const FilePath: string): string;
var
hcalwin: HINST;
NWCallsInit: TNWCallsInit;
NWParseNetWarePath: TNWParseNetWarePath;
NWAllocTemporaryDirectoryHandle: TNWAllocTemporaryDirectoryHandle;
NWIntScanExtendedInfo: TNWIntScanExtendedInfo;
NWGetObjectName: TNWGetObjectName;
NWDeallocateDirectoryHandle: TNWDeallocateDirectoryHandle;
NWCallsTerm: TNWCallsTerm;
hconn,
hdir,
retcode: cardinal;
filedir: string; { DOS path of parent folder
(upper case) }
nwfilename: string; { DOS filename (upper case) }
nwfiledir: array[0..255] of char; { Netware path of
parent folder }
rights: shortint;
i: integer;
entryinfo: NW_EXT_FILE_INFO;
objtype: word;
begin
Result := '';
// load netware client library and required functions
hcalwin := LoadLibrary('calwin32.dll');
if hcalwin<=0 then exit; // netware client not present on PC
#NWCallsInit := GetProcAddress(hcalwin,'NWCallsInit');
#NWParseNetWarePath := GetProcAddress(hcalwin,'NWParseNetWarePath');
#NWAllocTemporaryDirectoryHandle := GetProcAddress(hcalwin,
'NWAllocTemporaryDirectoryHandle');
#NWIntScanExtendedInfo :=
GetProcAddress(hcalwin,'NWIntScanExtendedInfo');
#NWGetObjectName := GetProcAddress(hcalwin,'NWGetObjectName');
#NWDeallocateDirectoryHandle := GetProcAddress(hcalwin,
'NWDeallocateDirectoryHandle');
#NWCallsTerm := GetProcAddress(hcalwin,'NWCallsTerm');
// initialise netware libs
if NWCallsInit(nil,nil)<>SUCCESSFUL then exit;
try
filedir := AnsiUpperCase(ExtractFileDir(FilePath));
retcode := NWParseNetWarePath(pchar(filedir),hconn,hdir,nwfiledir);
if retcode=NOT_MY_RESOURCE then exit; // local or non-netware disk
// get a dir handle
NWAllocTemporaryDirectoryHandle(hconn,0,nwfiledir,hdir,#rights);
// get the file info
i := -1;
nwfilename := AnsiUpperCase(ExtractFileName(FilePath));
retcode := NWIntScanExtendedInfo(hconn,hdir,
FA_NORMAL+FA_SYSTEM+FA_HIDDEN,
#i,pchar(nwfilename),entryinfo,0);
if retcode=SUCCESSFUL then begin
// get file owner name from ID
SetLength(Result,MAX_PATH);
retcode := NWGetObjectName(hconn,entryinfo.ownerID,
pchar(Result),#objtype);
if retcode=SUCCESSFUL then
SetLength(Result,Length(Result)) // got owner
else SetLength(Result,0); // failed to get owner
end;
// deallocate dir handle
NWDeallocateDirectoryHandle(hconn,hdir);
finally
// clean up
NWCallsTerm(nil);
FreeLibrary(hcalwin);
end;
end;
Are you sure about stdcall? Tru cdecl and so on.
Also, You done give information about delphi's version.
If you use a version BEFORE delphi2009 pchar is a one-byte char.
But if you use delphi2009 or next, pchar is 2 byte char.
So, if you need one byte char you must use PAnsiChar insthead.
I don't know if netware dll parameters are unicode or ansi...
Cher.
A.

EnumerateTraceGuids returns "The parameter is incorrect" (87)

i am trying to call the Windows API function EnumerateTraceGuids:
ULONG EnumerateTraceGuids(
__inout PTRACE_GUID_PROPERTIES *GuidPropertiesArray,
__in ULONG PropertyArrayCount,
__out PULONG GuidCount
);
Starting from the code sample on MSDN:
ULONG status = ERROR_SUCCESS;
PTRACE_GUID_PROPERTIES *pProviders = NULL;
ULONG RegisteredProviderCount = 0;
ULONG ProviderCount = 0;
pProviders = (PTRACE_GUID_PROPERTIES *) malloc(sizeof(PTRACE_GUID_PROPERTIES));
status = EnumerateTraceGuids(pProviders, ProviderCount, &RegisteredProviderCount);
i convert the code to Delphi:
var
providers: PPointerList;
providerCount: LongWord;
registeredProviderCount: LongWord;
res: LongWord;
begin
providerCount := 0;
registeredProviderCount := 0;
providers := AllocMem(SizeOf(Pointer));
ZeroMemory(providers, SizeOf(Pointer));
res := EnumerateTraceGuids(providers, providerCount, {out}registeredProviderCount);
end;
with the api call:
function EnumerateTraceGuids(
GuidPropertiesArray: Pointer;
PropertyArrayCount: Cardinal;
var GuidCount: Cardinal): Cardinal; stdcall; external 'advapi32.dll';
i get the result code ERROR_INVALID_PARAMETER (87, The parameter is incorrect).
What am i doing wrong?
MSDN describes what would cause ERROR_INVALID_PARAMETER:
ERROR_INVALID_PARAMETER
One of the following is true:
PropertyArrayCount is zero
GuidPropertiesArray is NULL
The first case is true, my 2nd parameter PropertyArrayCount is zero - just like the sample says it should be.
So far as I can see, your code should be identical to the MSDN sample. However, as Code says, the MSDN sample does look a bit funky. Indeed, it seems to me that the MSDN sample is only working by chance.
Note that comment in that code that states:
// EnumerateTraceGuids requires a valid pointer. Create a dummy
// allocation, so that you can get the actual allocation size.
Then it allocates space in pProviders to store a single pointer. However, the value contained in pProviders actually matters. It cannot be NULL. In your Delphi code you zeroise that memory twice in fact. Once with AllocMem and once with ZeroMemory. If you just change your Delphi code to make the contents of providers non-zero then the Delphi code will start working.
Here is a very simple project that illustrates exactly what is going on:
program _EnumerateTraceGuidsFaultDemo;
{$APPTYPE CONSOLE}
function EnumerateTraceGuids(
GuidPropertiesArray: Pointer;
PropertyArrayCount: Cardinal;
var GuidCount: Cardinal): Cardinal; stdcall; external 'advapi32.dll';
var
providers: Pointer;
providerCount: LongWord;
registeredProviderCount: LongWord;
res: LongWord;
begin
providerCount := 0;
registeredProviderCount := 0;
providers := AllocMem(SizeOf(Pointer));//zeroises memory
res := EnumerateTraceGuids(providers, providerCount, registeredProviderCount);
Writeln(res);//outputs 87
PInteger(providers)^ := 1;
res := EnumerateTraceGuids(providers, providerCount, registeredProviderCount);
Writeln(res);//outputs 234
Readln;
end.
So I think that explains the problem, but I'd actually solve it more completely than that. I would move on to the next step of your work and declare EnumerateTraceGuids fully using a real Delphi equivalent to the TRACE_GUID_PROPERTIES struct.
I'd probably write the code something like this:
program _EnumerateTraceGuids;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, Windows;
type
PTraceGuidProperties = ^TTraceGuidProperties;
TTraceGuidProperties = record
Guid: TGUID;
GuidType: ULONG;
LoggerId: ULONG;
EnableLevel: ULONG;
EnableFlags: ULONG;
IsEnable: Boolean;
end;
function EnumerateTraceGuids(
var GuidPropertiesArray: PTraceGuidProperties;
PropertyArrayCount: ULONG;
var GuidCount: ULONG
): ULONG; stdcall; external 'advapi32.dll';
function GetRegisteredProviderCount: ULONG;
var
provider: TTraceGuidProperties;
pprovider: PTraceGuidProperties;
providerCount: LongWord;
registeredProviderCount: ULONG;
res: ULONG;
begin
providerCount := 0;
pprovider := #provider;
res := EnumerateTraceGuids(pprovider, providerCount, registeredProviderCount);
if (res<>ERROR_MORE_DATA) and (res<>ERROR_SUCCESS) then
RaiseLastOSError;
Result := registeredProviderCount;
end;
var
i: Integer;
provider: TTraceGuidProperties;
pprovider: PTraceGuidProperties;
providers: array of TTraceGuidProperties;
pproviders: array of PTraceGuidProperties;
providerCount: ULONG;
registeredProviderCount: ULONG;
res: ULONG;
begin
providerCount := GetRegisteredProviderCount;
SetLength(providers, providerCount);
SetLength(pproviders, providerCount);
for i := 0 to providerCount-1 do
pproviders[i] := #providers[i];
res := EnumerateTraceGuids(pproviders[0], providerCount, registeredProviderCount);
if res<>ERROR_SUCCESS then
RaiseLastOSError;
//do stuff with providers
end.
Rather than trying to be too cute in GetRegisteredProviderCount, I have passed a pointer to a real TRACE_GUID_PROPERTIES.

Resources