2

I'm trying to use a function that calls SetupDiGetDeviceRegistryProperty with Delphi 7. The call is from the example function SetupEnumAvailableComPorts. It looks like this:

SetupDiGetDeviceRegistryProperty(
  DevInfoHandle,
  DeviceInfoData,
  RegProperty,
  @PropertyRegDataType,
  nil,
  0,
  @RequiredSize
);

I get the error "Types of actual and formal parameters must be identical" on the parameters @PropertyRegDataType, and @RequiredSize. These parameters are declared:

var
  RequiredSize: Cardinal;
  PropertyRegDataType: DWORD;

MSDN describes these parameters as: "RequiredSize [out, optional] A pointer to a variable of type DWORD that receives the required size, in bytes, of the PropertyBuffer buffer that is required to hold the data for the requested property. This parameter is optional and can be NULL." and "PropertyRegDataType [out, optional] A pointer to a variable that receives the data type of the property that is being retrieved. This is one of the standard registry data types. This parameter is optional and can be NULL."

The declaration of SetupDiGetDeviceRegistryProperty (in SetupAPI.pas from JVCL) looks like:

function SetupDiGetDeviceRegistryProperty(
  DeviceInfoSet: HDEVINFO;
  const DeviceInfoData: TSPDevInfoData; 
  Property_: DWORD;
  var PropertyRegDataType: DWORD; 
  PropertyBuffer: PBYTE; 
  PropertyBufferSize: DWORD;
  var RequiredSize: DWORD
): BOOL; stdcall; {$EXTERNALSYM SetupDiGetDeviceRegistryProperty}

Since PropertyRegDataType and RequiredSize are var parameters, they should be able to be passed without the @ operator. In fact, if I remove the @ operators from the function call parameters, the code compiles, but crashes with an access violation (read of address 0). The original code was written for Delphi 7, so why would they use the @ operator on these parameters? What am I missing?

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
tim11g
  • 1,935
  • 5
  • 27
  • 41
  • You should report this issue to JEDI's WINAPI team, because `var` modifier violates `optional` clause in function contract (it is still possible to pass `nil` but using rather dirty hack) – Free Consulting Feb 15 '11 at 17:27
  • Are you sure the code really compiled in Delphi 7? It's not unheard of for people to publish code that doesn't compile. Also consider that the Jedi code could have changed during the six years since your code was published. Besides, the Jedi declaration is wrong — pointer parameters that are allowed to be null should always be declared as pointers in Delphi. – Rob Kennedy Feb 15 '11 at 18:51

2 Answers2

4

Delphi supports "reference parameters" via the var and out keywords and in some API conversions, the C-like pointers may be replaced by this convention; these paremeters are mandatory and must be passed as-is, w/o the address operator @.
If the parameter declaration is left as pointer, allowing you to pass nil, you have to ensure the parameter is optional -or- set it to valid memory memory location of appropriate size via the @ operator; no typechecking is performed in this case.

Viktor Svub
  • 1,451
  • 10
  • 15
  • Type checking is performed if you switch `$TYPEDADDRESS` on – David Heffernan Feb 15 '11 at 15:00
  • Which you can't do if a variable-sized buffer is expected, as in this case, cause Delphis' `PByte` is "pointer to a single byte", and *not* C-like "pointer to the start of a byte array". – Viktor Svub Feb 15 '11 at 15:06
  • @Viktor Well you can if you have an array of bytes. You just return the address of the first one. In this case though it seems just to be a void pointer. It's more common in the Win32 API for these optional parameters to be pointers to single structs in which case `$TYPEDADDRESS` does result in static type safety. – David Heffernan Feb 15 '11 at 15:08
  • @David I know what you mean, and I agree that `$TYPEDADDRESS` gives you some additional type safety. I only want to point out that it may not be desireable in some cases, including this question. Also, interpreting array of byte as `PByte` will result in an ocean of pain if you ever try to refactor to dynamic arrays :) – Viktor Svub Feb 15 '11 at 17:19
  • @Viktor OK. By the way, I wasn't meaning to criticise, only to clarify and (hopefully) add some helpful commentary! – David Heffernan Feb 15 '11 at 17:22
  • @David I appreciate the clarification as I completely forgot about the possible use of `$TYPEDADDRESS` as I answered the question :) – Viktor Svub Feb 15 '11 at 17:40
2

Both of your variables PropertyRegDataType and RequiredSize should be declared as DWORD. You actually declare RequiredSize as Cardinal although that is equivalent.

You are correct that since they are var parameters you should not include the @ address operator.

It's hard to say why you are getting the error message without actually knowing what you pass for the other parameters.

EDIT

As commentators have pointed out, the JEDI translation is incorrect and PropertyRegDataType, because it is an optional parameter, should be passed by value and typed as PDWORD so that you are able to pass nil.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks - I got it working. The example on delphi3000.com has multiple errors. After removing the @ address operators, I debugged the crash, which was at a different point. There was a call to SetupDiClassGuidsFromName with an uninitialized value for ClassGuidListSize. – tim11g Feb 16 '11 at 15:22
  • I'll post the working function on my original questions that was on the topic of com-port enumeration rather than passing parameters: http://stackoverflow.com/questions/4999964/serial-port-enumeration-in-delphi-using-setupdigetclassdevs – tim11g Feb 16 '11 at 15:23