【问题标题】:How to connect (import) the WinApi function in DelphiDelphi中如何连接(导入)WinApi函数
【发布时间】:2013-07-14 10:28:53
【问题描述】:

德尔福 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”标记(OutInOut)。 因此,我不会处理在 interente 中找到的所有类型的示例。例如调用:

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 Types of actual and form var parameters must be same”——指的是@hProv和@dwProvType。如果您想在 VAR 上替换 OUT 并在 PDword(dwProvType) 上替换文本 @dwProvType,则会出现错误:“E2197 常量对象不能作为 var 参数传递”。

如果我不指定输入和输出参数(像这样 - http://www.bvbcode.com/code/oyma7f3h-1618784,字符串№692),一切都会编译、运行和工作正常(常量 - 无效):

{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”。但是我的这些例子不能正常工作。以及DWORD类型的pdwProvType和pcbProvName,虽然描述是DWORD * = PDWORD?

问题:

1) 如何正确地做。 MSDN OUT = Delphi OUT 还是 VAR? IN_OUT = 德尔福 VAR?还是他们没有具体说明?

2) 我需要写 Const 吗? IN = 德尔福常量?

3) 带有指针的类型。 DWORD = 德尔福双字。好的。 DWORD* = Delphi PDWROD(或所​​有标记 * = Delphi 指针类型)?

附言抱歉英语不好。

【问题讨论】:

  • Out 几乎没有为 Delphi 实现。除了接口引用之外,它主要是 var 的同义词

标签: delphi winapi delphi-xe4


【解决方案1】:

作为。下面的非常函数声明是不正确的。它只是从问题正文中逐字复制来演示编译错误背后的逻辑。

给出一个编译错误:“E2033 Types of actual and form var parameters must be same”——指的是@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-only,所以您不必为它们分配值 - 那些标记的行应该被省略。我放它们只是为了突出变量和常量之间的区别;因此hProvdwProvType 也将被删除——它们未被使用。


1) 如何正确地做。 MSDN OUT = Delphi OUT 还是 VAR?

Delphi 对 OUT 的支持很差。除了 IUnknown 等一些狭义的情况外,Delphi 将 OUT 作为 VAR 的同义词。

  • 我个人认为您应该指定 OUT - 仅用于自我记录的代码。
  • 其他人认为在 Delphi 中使用 OUT 而不是 VAR 只是在自欺欺人。

如果您来自 C++ 领域,那么 VAR 参数是 C++ 引用类型的直接模拟,例如 int Name(int& var; char& Var);

但是 - 正如 David Heffernan 指出的那样 - 对于 C++,那些 _In__Out_ 仅仅是意图的文档,它们不会影响已编译的代码。所以实际上CryptAcquireContext 的第一个参数的声明应该是 - 根据您的偏好,/*Out*/ HCRYPTPROV *phProv/*Out*/ HCRYPTPROV &hProv 在 Delphi 中将分别对应于 const phProv: PNativeUIntout hProv: PNativeUInt。根据您的心情,您可以将 (by-value, constant) 指针传递给结果的容器,或者传递 (by-ref, volatile) 容器本身。二进制那些选项都是一样的。

我相信FPC H2PasJedi API Lib 会给出正确的声明,不会像我一样错过错误。

2) 我需要写 Const 吗? IN = 德尔福常量?

恕我直言:您最好这样做 - 用于代码的自我记录。然而,事情是关于在给定代码约定中传递不同值的二进制兼容性。我认为 CONST(或它的缺席)不会是确保正确传输任何数据类型的灵丹妙药。所以总的来说 - 我认为这是个人品味和自律的问题。

3) 带有指针的类型。 DWORD = 德尔福双字。好的。 DWORD* = Delphi PDWROD(或所​​有标记 * = Delphi 指针类型)?

默认情况下,Delphi 从 Pascal 继承“每个指针都是无类型的”的概念。对我来说,这破坏了 Pascal 的类型安全,在我的项目中,我总是在选项中检查 Typed Pointers - 或将 pragma {$T+} 放入源代码中。

因此,根据编译器设置DWORD* 可能是Pointer^DWORDPDWORD 类型只是 ^DWORD 的命名别名(C++:typedef)。

【讨论】:

  • 这个答案已被接受,但没有更正错误的参数类型声明。例如,CryptAcquireContext 的参数 1。
  • @DavidHeffernan 那里有三个声明,Out hpProv:PNativeUInt 在我看来是_Out_ HCRYPTPROV *phProv, 的合适(尽管可能不是严格类型安全)表示。我错过了一些明显的东西?
  • 错了。它需要按照我的回答。 Out 宏扩展为空。这是纯粹的文档。
  • @DavidHeffernan 所以 _In__Out_ 只是 cmets 并且不会影响调用顺序?它们可以在不更改二进制调用代码的情况下被删除?如果是这样,那么是的。我没有专心。 PS。哦,你已经回答了,谢谢。
  • 是的。我的回答中的声明是准确的。我只是仔细检查了。
【解决方案2】:

为 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 优先传递。对于像这样的简单类型,out 和 var 具有相同的实现,使用其中之一的唯一原因是记录参数语义。
  • 可选的指针参数被声明为允许调用者传递 nil 的指针。

【讨论】:

  • 现在你可以考虑用 out-params 来抨击我做广告了 :-D 或者仔细检查我的评论并找出其中的任何错误。
  • @Arioch,用out参数代替var,你马上就明白它的意思了。
  • @TLama a-ha。所以你会出现在我身边。我会加热我的爆米花袋和 con-tem-pla-te :-D
猜你喜欢
  • 1970-01-01
  • 2022-07-06
  • 1970-01-01
  • 2020-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-06
相关资源
最近更新 更多