本机结构中包含的 PInvoke 方法
PInvoke method contained in a native structure
我正在尝试在 C# 中重新创建一些 C++ 示例 API 使用代码。
看起来我可能需要创建一个 C++/CLI 包装器以使 API 函数可供托管世界访问,但我想尽可能避免这种情况。 API 库本身只有一个导出函数:data_api_func_tab
.
这是 C++ API 用法的样子:
//
// .h file
//
typedef struct _DATA_API_FUNC_TAB {
short (*api_init)();
// ... lots of other methods ...
} DATA_API_FUNC_TAB
extern "C" typedef short (* MAPIINIT)(short);
// ... lots of other methods ...
#undef EXTERN
#ifdef _MAIN
#define EXTERN
#else
#define EXTERN extern
#endif
EXTERN MAPIINIT ncm_api_init;
// ... lots of other methods ...
public:
UCHAR SomeVariable;
void SomeMethod( arguments );
//
// .cpp file
//
/// Constructor
CWidgetDataApi::CWidgetDataApi()
{
SomeVariable = 0;
m_hInstHdl = ::LoadLibrary(_T(".\NATIVEAPI.dll"));
if( NULL != m_hInstHdl )
{
DATA_API_FUNC_TAB* p_data_api_func_tab =
(DATA_API_FUNC_TAB*) ::GetProcAddress(m_hInstHdl, "data_api_func_tab");
SomeVariable = 1;
if( p_data_api_func_tab == NULL )
{
::FreeLibrary(m_hInstHdl);
m_hInstHdl = NULL;
SomeVariable = 0;
}
else
{
api_init = (MAPINIT) p_data_api_func_tab->api_init;
// ... lots of other methods ...
short Ret = api_init(index);
}
}
}
/// Method
void CWidgetDataApi::SomeMethod( arguments )
{
// ... Makes use of the various other methods ...
}
//
// Usage in another class
//
DataAPI = new CWidgetDataApi;
if( DataAPI->SomeVariable == 1 )
{
DataAPI->SomeMethod( arguments );
...
}
由于我不能在原生库上使用反射(更何况它会很慢),PInvoke 似乎是唯一的途径。
我已经在 C# 中重新创建了适当的结构并尝试了以下 PInvoke 签名,
[DllImport("NATIVEAPI.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern struct data_api_func_tab { };
[DllImport("NATIVEAPI.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern short api_init([In, Out] ref _DATA_API_FUNC_TAB data_api_func_tab);
但它们会产生异常
Unable to find an entry point named '... whatever I try...' in NATIVEAPI.dll
我四处寻找执行此操作的方法,但似乎只找到非托管的 C++/CLI 解决方案。我正在尝试做的事情是否可能(假定结构中包含的各个方法未导出)?
这太过分了API。该库不导出函数,而是导出包含函数指针的结构的地址。请注意 C++ 代码如何调用 GetProcAddress
,然后将结果解释为指向结构的指针,而不是更常见的解释为指向函数的指针。
来自 GetProcAddress
的文档,我强调:
Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL).
您不能使用 DllImport
访问此库的导出,因为 DllImport
用于函数而不是变量。
这是您必须执行的操作:
- 使用
LoadLibrary
加载DLL并获取模块句柄作为IntPtr
。
- 调用
GetProcAddress
获取结构地址。
- 使用
Marshal.PtrToStructure
获取包含函数指针的托管结构。
- 为结构的每个成员使用
Marshal.GetDelegateForFunctionPointer
以获得一个您可以调用的委托。
- 完成库后,调用
FreeLibrary
将其卸载。如果您愿意等到进程终止并且系统自动卸载,则可以省略此步骤。
假设您可以获得 LoadLibrary
和朋友的 p/invoke 签名,代码如下所示:
// declare the structure of function pointers
struct DATA_API_FUNC_TAB
{
IntPtr api_init;
// more function pointers here
}
....
// load the DLL, and obtain the structure of function pointers
IntPtr lib = LoadLibrary(@"full\path\to\dll");
if (lib == IntPtr.Zero)
throw new Win32Exception();
IntPtr funcTabPtr = GetProcAddress(lib, "data_api_func_tab");
if (funcTabPtr == IntPtr.Zero)
throw new Win32Exception();
DATA_API_FUNC_TAB funcTab = (DATA_API_FUNC_TAB)Marshal.PtrToStructure(funcTabPtr, typeof(DATA_API_FUNC_TAB));
....
// declare the delegate types, note the calling convention
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate short api_init_delegate();
....
// obtain a delegate instance
api_init_delegate api_init = (api_init_delegate)Marshal.GetDelegateForFunctionPointer(funcTab.api_init, typeof(api_init_delegate));
....
// finally we can call the function
short retval = api_init();
编组程序有能力为您创建委托,这似乎是合理的。在这种情况下,结构将是:
struct DATA_API_FUNC_TAB
{
api_init_delegate api_init;
// more delegates here
}
在这种情况下,显然不需要 Marshal.GetDelegateForFunctionPointer
步骤,编组器已经代表您执行了它。
我没有测试任何这段代码,只是将它输入浏览器。毫无疑问,这里有一些问题,但这段代码更像是一个指南,而不是您可以直接使用的代码。
我正在尝试在 C# 中重新创建一些 C++ 示例 API 使用代码。
看起来我可能需要创建一个 C++/CLI 包装器以使 API 函数可供托管世界访问,但我想尽可能避免这种情况。 API 库本身只有一个导出函数:data_api_func_tab
.
这是 C++ API 用法的样子:
//
// .h file
//
typedef struct _DATA_API_FUNC_TAB {
short (*api_init)();
// ... lots of other methods ...
} DATA_API_FUNC_TAB
extern "C" typedef short (* MAPIINIT)(short);
// ... lots of other methods ...
#undef EXTERN
#ifdef _MAIN
#define EXTERN
#else
#define EXTERN extern
#endif
EXTERN MAPIINIT ncm_api_init;
// ... lots of other methods ...
public:
UCHAR SomeVariable;
void SomeMethod( arguments );
//
// .cpp file
//
/// Constructor
CWidgetDataApi::CWidgetDataApi()
{
SomeVariable = 0;
m_hInstHdl = ::LoadLibrary(_T(".\NATIVEAPI.dll"));
if( NULL != m_hInstHdl )
{
DATA_API_FUNC_TAB* p_data_api_func_tab =
(DATA_API_FUNC_TAB*) ::GetProcAddress(m_hInstHdl, "data_api_func_tab");
SomeVariable = 1;
if( p_data_api_func_tab == NULL )
{
::FreeLibrary(m_hInstHdl);
m_hInstHdl = NULL;
SomeVariable = 0;
}
else
{
api_init = (MAPINIT) p_data_api_func_tab->api_init;
// ... lots of other methods ...
short Ret = api_init(index);
}
}
}
/// Method
void CWidgetDataApi::SomeMethod( arguments )
{
// ... Makes use of the various other methods ...
}
//
// Usage in another class
//
DataAPI = new CWidgetDataApi;
if( DataAPI->SomeVariable == 1 )
{
DataAPI->SomeMethod( arguments );
...
}
由于我不能在原生库上使用反射(更何况它会很慢),PInvoke 似乎是唯一的途径。
我已经在 C# 中重新创建了适当的结构并尝试了以下 PInvoke 签名,
[DllImport("NATIVEAPI.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern struct data_api_func_tab { };
[DllImport("NATIVEAPI.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern short api_init([In, Out] ref _DATA_API_FUNC_TAB data_api_func_tab);
但它们会产生异常
Unable to find an entry point named '... whatever I try...' in NATIVEAPI.dll
我四处寻找执行此操作的方法,但似乎只找到非托管的 C++/CLI 解决方案。我正在尝试做的事情是否可能(假定结构中包含的各个方法未导出)?
这太过分了API。该库不导出函数,而是导出包含函数指针的结构的地址。请注意 C++ 代码如何调用 GetProcAddress
,然后将结果解释为指向结构的指针,而不是更常见的解释为指向函数的指针。
来自 GetProcAddress
的文档,我强调:
Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL).
您不能使用 DllImport
访问此库的导出,因为 DllImport
用于函数而不是变量。
这是您必须执行的操作:
- 使用
LoadLibrary
加载DLL并获取模块句柄作为IntPtr
。 - 调用
GetProcAddress
获取结构地址。 - 使用
Marshal.PtrToStructure
获取包含函数指针的托管结构。 - 为结构的每个成员使用
Marshal.GetDelegateForFunctionPointer
以获得一个您可以调用的委托。 - 完成库后,调用
FreeLibrary
将其卸载。如果您愿意等到进程终止并且系统自动卸载,则可以省略此步骤。
假设您可以获得 LoadLibrary
和朋友的 p/invoke 签名,代码如下所示:
// declare the structure of function pointers
struct DATA_API_FUNC_TAB
{
IntPtr api_init;
// more function pointers here
}
....
// load the DLL, and obtain the structure of function pointers
IntPtr lib = LoadLibrary(@"full\path\to\dll");
if (lib == IntPtr.Zero)
throw new Win32Exception();
IntPtr funcTabPtr = GetProcAddress(lib, "data_api_func_tab");
if (funcTabPtr == IntPtr.Zero)
throw new Win32Exception();
DATA_API_FUNC_TAB funcTab = (DATA_API_FUNC_TAB)Marshal.PtrToStructure(funcTabPtr, typeof(DATA_API_FUNC_TAB));
....
// declare the delegate types, note the calling convention
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate short api_init_delegate();
....
// obtain a delegate instance
api_init_delegate api_init = (api_init_delegate)Marshal.GetDelegateForFunctionPointer(funcTab.api_init, typeof(api_init_delegate));
....
// finally we can call the function
short retval = api_init();
编组程序有能力为您创建委托,这似乎是合理的。在这种情况下,结构将是:
struct DATA_API_FUNC_TAB
{
api_init_delegate api_init;
// more delegates here
}
在这种情况下,显然不需要 Marshal.GetDelegateForFunctionPointer
步骤,编组器已经代表您执行了它。
我没有测试任何这段代码,只是将它输入浏览器。毫无疑问,这里有一些问题,但这段代码更像是一个指南,而不是您可以直接使用的代码。