1

假设我像这样导入一个 DLL 函数:

typedef int(__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);

HINSTANCE hDLL = LoadLibraryW(L"foo.dll");
pRandomNumber RandomNumber = (pRandomNumber)GetProcAddress(hDLL, "RandomNumber");

现在,RandomNumberDLL 代码中的这个函数用 SAL 注释进行了注释_Check_return_。我还希望在调用它的代码中强制执行此注释。我该怎么做?

如果我将注释放在typedef

_Check_return_
typedef int(__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);

pRandomNumber稍后当我初始化 a并在未检查返回值的情况下调用它时,分析无法报告警告。

如果我将注释放在一个实例上:

typedef int(__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);

_Check_return_
pRandomNumber RandomNumber;

当我调用它而不检查返回值时,分析仍然无法报告。

有什么办法可以强制执行吗?一种方法是编写一个带有注释的存根内联函数,但这对我来说很难看。

4

1 回答 1

2

而不是声明变量

int (__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);

您可以使用__declspec(dllimport)属性和使用[[nodiscard]]和/或_Must_inspect_result_在此声明上声明函数

EXTERN_C
_NODISCARD
DECLSPEC_IMPORT
_Must_inspect_result_
int 
WINAPI 
RandomNumber(
    _In_ size_t cb, 
    _Out_writes_bytes_(cb) unsigned char *pb
    );

在内部,当你用__declspec(dllimport)属性声明 api 时,编译器用关键字声明 变量;extern

extern "C" { 
    extern PVOID __imp_<function_name>;
}

in place <function_name> __FUNCDNAME__used - 函数的修饰名称

所以可能说和

extern PVOID __imp_##__FUNCDNAME__;

当然,因为extern,这只是声明。如果你不添加定义 - 你得到了

error LNK2001: unresolved external symbol __imp_RandomNumber

如果你尝试打电话RandomNumber。所以你需要添加定义。因为_AMD64_这很简单,因为extern "C"符号没有装饰。

所以简单地写:

EXTERN_C_START
    PVOID __imp_RandomNumber;
EXTERN_C_END

反而

EXTERN_C_START
    int (__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);
EXTERN_C_END

在这两种情况下,您都需要声明指针大小变量,它将保存函数指针-仅在名称上不同-您选择pRandomNumber名称(并且可以选择任何名称)-在我的情况下-您必须选择__imp_RandomNumber__imp_<function_name>)-为修饰的函数名称添加__imp_前缀.

但如果_X86_存在问题 - 对于__stdcall- @符号将在修饰函数名称中。你需要写

EXTERN_C_START
    PVOID __imp__RandomNumber@8;
EXTERN_C_END

因为装饰名称将是_RandomNumber@8. 但__imp__RandomNumber@8不是有效的c/c++名称。您可以在 asm 代码中定义这样的名称,但不能在c/c++中定义。但您可以使用/alternatename链接器选项。

__pragma(comment(linker, "/alternatename:__imp__RandomNumber@8=___imp_RandomNumber"))

EXTERN_C_START
    PVOID __imp_RandomNumber;
EXTERN_C_END

如果未找到链接器,请__imp__RandomNumber@8尝试使用___imp_RandomNumber(如果存在)。并将__imp_RandomNumber被装饰___imp_RandomNumber_X86_

可能写下一个宏

#ifdef _X86_
#define ALT_NAME(name, n) __pragma(comment(linker, _CRT_STRINGIZE(/alternatename:__imp__##name##@##n####=___imp_##name)))
#else
#define ALT_NAME(name, n)
#endif

#define IMP_FUNC(name, n) EXTERN_C_START PVOID __imp_##name; EXTERN_C_END ALT_NAME(name, n)

有了这个你的代码将是

// global declaration

EXTERN_C
_NODISCARD
DECLSPEC_IMPORT
_Must_inspect_result_
int 
WINAPI 
RandomNumber(
    _In_ size_t cb, 
    _Out_writes_bytes_(cb) unsigned char *pb
    );

IMP_FUNC(RandomNumber, 8);

// initialization
__imp_RandomNumber = GetProcAddress(GetModuleHandle(L"foo"), "RandomNumber");

//usage
if (__imp_RandomNumber )
{
    UCHAR test[16];
    RandomNumber(sizeof(test), test);
}

你得到了

warning C4834: discarding return value of function with 'nodiscard' attribute

如果不使用返回值RandomNumber

于 2021-06-04T17:55:48.000 回答