2
votes

Delphi Xe4. For example, there are two functions (Unicode):

CryptAcquireContext, CryptGetProvParam.

I read on MSDN description:

1) http://msdn.microsoft.com/en-us/library/windows/desktop/aa379886(v=vs.85).aspx

BOOL WINAPI CryptAcquireContext(
  _Out_  HCRYPTPROV *phProv,
  _In_   LPCTSTR pszContainer,
  _In_   LPCTSTR pszProvider,
  _In_   DWORD dwProvType,
  _In_   DWORD dwFlags);

2) http://msdn.microsoft.com/en-us/library/windows/desktop/aa379929(v=vs.85).aspx

BOOL WINAPI CryptEnumProviders(
  _In_     DWORD dwIndex,
  _In_     DWORD *pdwReserved,
  _In_     DWORD dwFlags,
  _Out_    DWORD *pdwProvType,
  _Out_    LPTSTR pszProvName,
  _Inout_  DWORD *pcbProvName);

If I understand correctly, then translated into Delphi it should be like this:

    {S} Function CryptAcquireContext(Out hpProv:PNativeUInt;Const Container:PWideChar;
Const Provider:PWideChar;Const ProvType:DWord;Const Flags:DWord):Bool; StdCall; External Advapi32dll Name 'CryptAcquireContextW';

    {S} Function CryptEnumProviders(Const Index:DWord;Const Reserved:PDWord;Const Flags:DWord;
Out ProvType:PDWord;Out pszProvName:DWord;Var pcbProvName:DWord):Bool; StdCall; External Advapi32dll Name 'CryptEnumProvidersW';

Are primarily interested in the return parametrametry, marked with "OUT" and "VAR" (Out, InOut). As such, I do not work all kinds of examples that are found in interente. For example calls:

Procedure Test;
var hProv:NativeUInt;provName: array[0..200] of char;dwProvType: DWORD;
begin
...
if not CryptAcquireContext(@hProv, nil, provName, dwProvType,CRYPT_VERIFYCONTEXT) then RaiseLastOSError;
...
while CryptEnumProviders(i, nil, 0,@dwProvType, nil, @cbName)) do
begin
..
end;

Give a compile error: "E2033 Types of actual and formal var parameters must be identical" - refers to the @ hProv and @ dwProvType. If you like to replace OUT on VAR and text @dwProvType on PDword(dwProvType), gives an error: "E2197 Constant object cannot be passed as var parameter".

If I do not specify the input and output parameters (like this - http://www.bvbcode.com/code/oyma7f3h-1618784, string №692), everything compiles, runs and work fine (Const - no effect):

{S} Function CryptAcquireContext(hpProv:PNativeUInt;Container:PWideChar;Provider:PWideChar;ProvType:DWord;Flags:DWord):Bool; StdCall; External Advapi32dll Name 'CryptAcquireContextW';

{S} Function CryptEnumProviders(Index:DWord;Reserved:PDWord;Flags:DWord;ProvType:PDWord;pszProvName:PWideChar;pcbProvName:PDWord):Bool; StdCall; External Advapi32dll Name 'CryptEnumProvidersW';

In the past, the question I was advised to take the values ​​of the functions of the JEDI API. I downloaded the latest version of (http://sourceforge.net/projects/jedi-apilib/), I see (unit JwaWinCrypt):

function CryptAcquireContext(var phProv: HCRYPTPROV; pszContainer: LPCTSTR;
  pszProvider: LPCTSTR; dwProvType: DWORD; dwFlags: DWORD): BOOL; stdcall;

function CryptEnumProviders(dwIndex: DWORD; pdwReserved: LPDWORD; dwFlags: DWORD;
  var pdwProvType: DWORD; pszProvName: LPTSTR; var pcbProvName: DWORD): BOOL; stdcall;

Instead, call the values ​​of "OUT" and "INOUT" write "VAR". But these my examples are not work fine. And pdwProvType and pcbProvName of type DWORD, although the description is DWORD * = PDWORD?

Questions:

1) How to do it right. MSDN OUT = Delphi OUT or VAR? IN_OUT = Delphi VAR? Or they do not specify?

2) Do I need to write Const? IN = Delphi Const?

3) The types with pointers. DWORD = Delphi Dword. Ok. DWORD* = Delphi PDWROD (or all marked * = Delphi Pointer type)?

p.s. Sorry for the bad English.

2
Out is next to not-implemented for Delphi. Aside from Interface-references it is mostly synonim to varArioch 'The

2 Answers

4
votes

AS. The very function declaration below is not correct. It just was copied verbatim from the question body to demonstrate the logic behind compilation error.

Give a compile error: "E2033 Types of actual and formal var parameters must be identical" - refers to the @ hProv and @ dwProvType.

And that is correct. The function (as it was declared) returns a pointer, not an integer. Your code is

var hProv:NativeUInt;
const pProv = @hProv;
if not CryptAcquireContext(pProv,...

var dwProvType: DWORD;
const pPropType = @dwPropType;
while CryptEnumProviders(... @dwProvType, ...

But the function cannot write values into the constant. The correct code should be

var hProv: NativeUInt;
VAR pProv: PNativeUInt;
pProv := @hProv;           (** see remarks **)
if not CryptAcquireContext(pProv,...

var dwProvType: DWORD;
VAR pPropType: ^DWORD;
pPropType := @dwPropType;  (** see remarks **)
while CryptEnumProviders(... @dwProvType, ...

Well... Actually. since those parameters are OUT-only you do not have to assign values to them - those marked lines should be omitted. I put them just to highlight the difference between variables and constants; Consequently hProv and dwProvType are to be removed as well - they are not used.


1) How to do it right. MSDN OUT = Delphi OUT or VAR?

Delphi has poor support for OUT. Except for some narrow case like IUnknown Delphi takes OUT as synonim for VAR.

  • Personally I think that you should specify OUT - just for code being self-documented.
  • Others argue that using OUT instead of VAR in Delphi is just fooling yourself.

If you're coming from C++ land, then VAR-parameter is direct analogue of C++ reference types like in int Name(int& var; char& Var);

However - as David Heffernan pointed it - for C++ those _In_ and _Out_ are merely documentation for intentions, they are not affect compiled code. So actually the declarations of the 1st parameter to CryptAcquireContext should read - depending on your preferences as /*Out*/ HCRYPTPROV *phProv or /*Out*/ HCRYPTPROV &hProv which in Delphi would correspond respectively to const phProv: PNativeUInt or out hProv: PNativeUInt. Depending on you mood, you either pass (by-value, constant) pointer to the result's container, or you pass (by-ref, volatile) container itself. Binary those options are the same.

And i believe FPC H2Pas and Jedi API Lib would give the correct declaration and not miss the fault like i did.

2) Do I need to write Const? IN = Delphi Const?

IMHO: You better do - for self-documenting of code. However the thing is about binary compatibility of passing different values in the given code convention. I don't think that CONST (or its absence) would be a silver bullet to eensure correct transferring of any data type. So overall - it is a matter of personal taste and self-discipline, i think.

3) The types with pointers. DWORD = Delphi Dword. Ok. DWORD* = Delphi PDWROD (or all marked * = Delphi Pointer type)?

By default Delphi inherits from Pascal the notion that "every pointer is typeless". To me that ruins Pascal's type safety and in my projects i alwways check Typed Pointers in options - or put pragma {$T+} into sources.

So, depending on that compiler setting DWORD* may be Pointer or ^DWORD; the PDWORD type is just a named alias (C++: typedef) for ^DWORD.

6
votes

Declare a type for HCRYPTPROV:

type
  HCRYPTPROV = NativeUInt;

Then declare the functions:

function CryptAcquireContext(
    out hpProv: HCRYPTPROV; 
    Container: PWideChar;
    Provider: PWideChar;
    ProvType: DWORD;
    Flags: DWORD
):BOOL; stdcall; external Advapi32dll name 'CryptAcquireContextW';

function CryptEnumProviders(
    Index: DWORD;
    Reserved: PDWORD;
    Flags: DWORD;
    out ProvType: DWORD;
    pszProvName: PWideChar;
    var pcbProvName: DWORD
):BOOL; stdcall; external Advapi32dll name 'CryptEnumProvidersW';

Note that var and out parameters are passed as pointers to the actual parameter. So in your code you would have had too much indirection.

In my translation here I have adopted the following policy:

  • Value parameters don't use const. There seems little benefit for an external declaration.
  • Pointer parameters are passed by var or out by preference. For simple types like these, out and var have the same implementation and the only reason for using one or the other is to document the parameter semantics.
  • Optional pointer parameters are declared as pointers to allow the caller to pass nil.