首页 文章

如何在Delphi中连接(导入)WinApi函数

提问于
浏览
2

德尔福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) . 因此,我不会使用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实际和正式var参数的类型必须相同” - 指的是@hProv和@dwProvType . 如果您想在VAR上替换OUT并在PDword(dwProvType)上替换文本@dwProvType,则会出现错误:“E2197常量对象不能作为var参数传递” .

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 - 无效果):

{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 = Delphi VAR?或者他们没有说明?

2)我需要写Const吗? IN = Delphi Const?

3)带指针的类型 . DWORD = Delphi Dword . 好 . DWORD * = Delphi PDWROD(或所有标记的* = Delphi指针类型)?

附:对不起英语不好 .

2 回答

  • 6

    如 . 下面的函数声明不正确 . 它只是从问题正文中逐字复制,以演示编译错误背后的逻辑 .

    给出编译错误:“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,因此您不必为它们赋值 - 应省略那些标记的行 . 我把它们只是为了突出变量和常量之间的区别;因此, hProvdwProvType 也将被删除 - 它们不会被使用 .


    1)如何正确行事 . MSDN OUT = Delphi OUT还是VAR?

    德尔福对OUT的支持很差 . 除了像_1403599之类的一些狭隘情况外,Delphi将VAR作为VAR的synonim .

    • 我个人认为你应该指定OUT - 只是为了自我记录的代码 .

    • 其他人认为在Delphi中使用OUT代替VAR只是在欺骗自己 .

    如果你是来自C land,那么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-ref,volatile)容器本身 . 二进制那些选项是相同的 .

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

    2)我需要写Const吗? IN = Delphi Const?

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

    3)带指针的类型 . DWORD = Delphi Dword . 好 . DWORD * = Delphi PDWROD(或所有标记的* = Delphi指针类型)?

    默认情况下,Delphi继承了Pascal的概念"every pointer is typeless" . 对我来说,破坏Pascal的类型安全性,在我的项目中,我在选项中检查 Typed Pointers - 或者将pragma {$T+} 放入源代码中 .

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

  • 4

    声明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 .

相关问题