Let's say I import a DLL function like so:
typedef int(__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);
HINSTANCE hDLL = LoadLibraryW(L"foo.dll");
pRandomNumber RandomNumber = (pRandomNumber)GetProcAddress(hDLL, "RandomNumber");
Now, this function RandomNumber
in the code of the DLL is annotated with the SAL annotation _Check_return_
. I also want to have this annotation enforced in code that calls it. How do I get that done?
If I put the annotation on the typedef
:
_Check_return_
typedef int(__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);
Analysis fails to report a warning later when I initialize a pRandomNumber
and call it without checking the return value.
If I put the annotation on an instance:
typedef int(__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);
_Check_return_
pRandomNumber RandomNumber;
Analysis still fails to report when I call it without checking the return value.
Is there any way I can have that enforced? One way is writing a stub inline function with the annotation on it, but that feels ugly to me.
instead of declare variable
int (__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);
you can declare function with __declspec(dllimport)
attribute and use [[nodiscard]]
and/or _Must_inspect_result_
on this declaration
EXTERN_C
_NODISCARD
DECLSPEC_IMPORT
_Must_inspect_result_
int
WINAPI
RandomNumber(
_In_ size_t cb,
_Out_writes_bytes_(cb) unsigned char *pb
);
internally, when you declare api with __declspec(dllimport)
attribute, compiler declare variable with extern
keyword;
extern "C" {
extern PVOID __imp_<function_name>;
}
in place <function_name> __FUNCDNAME__
used - the decorated name of the function
so possible say and
extern PVOID __imp_##__FUNCDNAME__;
of course, because extern, this is only declaration. and if you not add definition - you got
error LNK2001: unresolved external symbol __imp_RandomNumber
if you try call RandomNumber
. so you need add definition. for _AMD64_
this is very simply, because extern "C"
symbols not decorated.
so simply write:
EXTERN_C_START
PVOID __imp_RandomNumber;
EXTERN_C_END
instead
EXTERN_C_START
int (__stdcall *pRandomNumber)(size_t cb, unsigned char *pb);
EXTERN_C_END
you need in both case declare pointer size variable, which will be hold function pointer - different only in name - you select pRandomNumber
as name (and can select any name) - in my case - you must select __imp_RandomNumber
( __imp_<function_name>
) - add __imp_
prefix to decorated function name.
but in case _X86_
exist problem - for __stdcall
- the @ symbol will be in decorated function name. you need write
EXTERN_C_START
PVOID __imp__RandomNumber@8;
EXTERN_C_END
because decorated name will be _RandomNumber@8
. but __imp__RandomNumber@8
not valid c/c++ name. you can define such name in asm code but not in c/c++. but you can use /alternatename
linker option.
__pragma(comment(linker, "/alternatename:__imp__RandomNumber@8=___imp_RandomNumber"))
EXTERN_C_START
PVOID __imp_RandomNumber;
EXTERN_C_END
if linker not found __imp__RandomNumber@8
it try use ___imp_RandomNumber
if it exist. and __imp_RandomNumber
will be decorated to ___imp_RandomNumber
in _X86_
possible write next macro
#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)
with this you code will be
// 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);
}
and you got
warning C4834: discarding return value of function with 'nodiscard' attribute
if not use return value of RandomNumber