How to pass nil to var parameter?

There are many API routines that translate a pointer to a variable as a parameter that has been translated to var parameters, but can be specified as null pointers in accordance with Windows help files.

As an example, the ChangeDisplaySettings function is declared as:

function ChangeDisplaySettings(var lpDevMode: TDeviceMode; dwFlags: DWORD): Longint; stdcall; 

However, the Windows help file clearly states that "Passing NULL for the lpDevMode parameter is the easiest way to return to default mode after changing the dynamic mode." The correct translation should have been:

 function ChangeDisplaySettings(lpDevMode: PDeviceMode; dwFlags: DWORD): Longint; stdcall; 

I post this question and answer to help newcomers get around these issues without renaming them. I still remember that this was a problem for me in the beginning.

+6
source share
2 answers

One solution is to re-declare any such functions using pointers instead of var parameters, but there is an easier solution. Just add the dereferenced null pointer to the correct type, for example, for the ChangeDisplaySettings example, use the following parameters: reset the display mode for the default registry settings:

 ChangeDisplaySettings(TDeviceMode(nil^), 0); 

or

 ChangeDisplaySettings(PDeviceMode(nil)^, 0); 

Thus, you pass the var parameter, which, as it turned out, is located at memory address zero - the compiler is happy, and you can pass the nil pointer to the API procedure!

Edit: From David Hefferman's comment, it seems that standard practice is more like re-declaring such routines. Personally, I prefer to use standard declarations, if I can, in my personal units, but for professional work purposes, I suggest that developers follow standard methods.

+11
source

Besides the other answers and comments that are helpful, I still have another twist on this question. In this case, whoever translated this API from the header did not actually look carefully at the API documentation. If they did, it would be clear that accepting β€œzero” is the right thing.

In this case, the correct course of action should be to declare a set of overloads that reference the same import. One of them would be a good version of var, and the other would be a version of pointer-to-structure. This would make it so that you could go directly to the TDeviceMode variable (no need to take the var address) and still pass zero if necessary. The compiler will match nil with a pointer to a structure, which will then be referenced. Since both APIs allow the same API and the actual method of passing parameters is no different, everything works as expected.

Since there is no overloaded version of this API, when you should be able to pass "nil", which is an error for translating the API. Feel free to mention this in the report http://quality.embarcadero.com .

For recording, for many years I have been doing many API translations in the product ... Of course, it is possible that I was a stupid developer who did not do the right research on this :).

+6
source

All Articles