Как подключить (импортировать) функцию WinApi в Delphi

Delphi Xe4. Например, есть две функции (Unicode):

CryptAcquireContext, CryptGetProvParam.

Я прочитал описание MSDN:

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);

Если я правильно понял, то в переводе на Delphi должно быть так:

    {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';

В первую очередь интересуют параметры возврата, отмеченные «OUT» и «VAR» (Out, InOut). Таким образом, я не работаю со всеми видами примеров, которые встречаются в интеренте. Например звонки:

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;

Выдайте ошибку компиляции: «E2033 Типы фактических и формальных параметров var должны быть идентичны» - относится к @ hProv и @ dwProvType. Если вы хотите заменить OUT на VAR и текст @dwProvType на PDword (dwProvType), выдает ошибку: «E2197 Constant object не может быть передан как параметр var».

Если я не укажу параметры ввода и вывода (например, http://www.bvbcode.com/code/oyma7f3h-1618784, строка №692), все компилируется, работает и работает нормально (Const - без эффекта):

{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';

В прошлом вопросе мне советовали брать значения функций JEDI API. Я загрузил последнюю версию (http://sourceforge.net/projects/jedi-apilib/), Я вижу (блок 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;

Вместо этого вызовите значения «OUT» и «INOUT» и напишите «VAR». Но эти мои примеры не работают. И pdwProvType и pcbProvName типа DWORD, хотя описание DWORD * = PDWORD?

Вопросы:

1) Как правильно сделать. MSDN OUT = Delphi OUT или VAR? IN_OUT = Delphi VAR? Или они не уточняют?

2) Надо ли писать Const? IN = Delphi Const?

3) Типы с указателями. DWORD = Delphi Dword. В порядке. DWORD * = Delphi PDWROD (или все отмеченные * = тип указателя Delphi)?

p.s. Простите за плохой английский.


person Gu.    schedule 16.07.2013    source источник
comment
Попробуйте wiki.freepascal.org/H2Pas   -  person Arioch 'The    schedule 16.07.2013
comment
Out почти не реализован для Delphi. Помимо ссылок на интерфейс, это в основном синоним var   -  person Arioch 'The    schedule 16.07.2013


Ответы (2)


КАК. Само объявление функции ниже неверно. Он просто был дословно скопирован из текста вопроса, чтобы продемонстрировать логику ошибки компиляции.

Выдайте ошибку компиляции: «E2033 Типы фактических и формальных параметров var должны быть идентичны» - относится к @ hProv и @ dwProvType.

И это правильно. Функция (как было объявлено) возвращает указатель, а не целое число. Ваш код

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

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

Но функция не может записывать значения в константу. Правильный код должен быть

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, ...

А на самом деле. поскольку эти параметры предназначены только для OUT, вам не нужно присваивать им значения - эти отмеченные строки следует опустить. Я поместил их только для того, чтобы подчеркнуть разницу между переменными и константами; Следовательно, hProv и dwProvType также должны быть удалены - они не используются.


1) Как правильно сделать. MSDN OUT = Delphi OUT или VAR?

Delphi плохо поддерживает OUT. За исключением некоторых узких случаев, таких как IUnknown, Delphi принимает OUT как синоним для VAR.

  • Лично я считаю, что вам следует указать OUT - только для самодокументированного кода.
  • Другие утверждают, что использование OUT вместо VAR в Delphi просто обманывает себя.

Если вы из страны C ++, то VAR-параметр является прямым аналогом ссылочных типов C ++, как в int Name(int& var; char& Var);

Однако, как указал Дэвид Хеффернан, для C ++ эти _In_ и _Out_ являются просто документацией для намерений, они не влияют на скомпилированный код. Итак, на самом деле объявления 1-го параметра для CryptAcquireContext должны читаться - в зависимости от ваших предпочтений как /*Out*/ HCRYPTPROV *phProv или /*Out*/ HCRYPTPROV &hProv, которые в Delphi будут соответствовать соответственно const phProv: PNativeUInt или out hProv: PNativeUInt. В зависимости от вашего настроения вы либо передаете (по значению, константа) указатель на контейнер результата, либо передаете сам контейнер (по ссылке, изменчивый). Бинарные эти параметры одинаковы.

И я верю, что FPC H2Pas и Jedi API Lib дадут правильное заявление и не пропустят ошибку, как это сделал я.

2) Надо ли писать Const? IN = Delphi Const?

ИМХО: Лучше сделайте - для самодокументирования кода. Однако речь идет о двоичной совместимости передачи разных значений в данном соглашении о коде. Я не думаю, что CONST (или его отсутствие) будет серебряной пулей для обеспечения правильной передачи любого типа данных. В общем, я думаю, это вопрос личного вкуса и самодисциплины.

3) Типы с указателями. DWORD = Delphi Dword. В порядке. DWORD * = Delphi PDWROD (или все отмеченные * = тип указателя Delphi)?

По умолчанию Delphi наследует от Паскаля представление о том, что «каждый указатель не имеет типа». Для меня это разрушает безопасность типов Паскаля, и в своих проектах я всегда проверяю Typed Pointers в параметрах или помещаю прагму {$T+} в исходные тексты.

Итак, в зависимости от настройки компилятора DWORD* может быть Pointer или ^DWORD; тип PDWORD - это просто именованный псевдоним (C ++: typedef) для ^DWORD.

person Arioch 'The    schedule 16.07.2013
comment
Этот ответ был принят, но не исправляет ошибочные объявления типа параметра. Например, параметр 1 для CryptAcquireContext. - person David Heffernan; 16.07.2013
comment
@DavidHeffernan, там три объявления, и Out hpProv:PNativeUInt кажется мне подходящим (хотя, возможно, не строго типобезопасным) представлением для _Out_ HCRYPTPROV *phProv,. Я упустил что-то очевидное? - person Arioch 'The; 16.07.2013
comment
Это неверно. Это должно быть в соответствии с моим ответом. Макрос Out превращается в ничего. Это чистая документация. - person David Heffernan; 16.07.2013
comment
@DavidHeffernan, значит, _In_ и _Out_ - всего лишь комментарии и не влияют на последовательность вызовов? их можно удалить без изменения двоичного кода вызова? Если да, то да. Я был невнимателен. PS. О, ты уже ответил на это, спасибо. - person Arioch 'The; 16.07.2013
comment
да. Заявления в моем ответе верны. Я просто дважды проверил. - person David Heffernan; 16.07.2013
comment
@DavidHeffernan, конечно, ты прав. Виноват. Просто не мог выйти из слипстрима топикстартера :-) - person Arioch 'The; 16.07.2013

Объявите тип для HCRYPTPROV:

type
  HCRYPTPROV = NativeUInt;

Затем объявите функции:

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';

Обратите внимание, что параметры var и out передаются как указатели на фактический параметр. Таким образом, в вашем коде у вас было бы слишком много косвенного обращения.

В моем переводе здесь я принял следующую политику:

  • Параметры значения не используют const. Кажется, мало пользы от внешнего объявления.
  • Параметры указателя передаются с помощью var или выходят по выбору. Для таких простых типов, как эти, out и var имеют одинаковую реализацию, и единственная причина для использования того или другого - документировать семантику параметра.
  • Необязательные параметры указателя объявляются как указатели, позволяющие вызывающему объекту передать значение nil.
person David Heffernan    schedule 16.07.2013
comment
А теперь можете вдумчиво отругать меня за рекламу с помощью out-params :-D Или вдумчиво проверить мои замечания и найти в них ошибки. - person Arioch 'The; 16.07.2013
comment
@Arioch, используя параметр out вместо var, вы сразу понимаете его значение. - person TLama; 16.07.2013
comment
@TLama a-ha. Так что тогда вы представите мою сторону. И я бы подогрел свою сумку с попкорном и кон-тэ-тэ :-D - person Arioch 'The; 16.07.2013