[英]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);
但它們會產生異常
在NATIVEAPI.dll中找不到名為“ ...無論我嘗試...”的入口點
我一直在尋找實現此目的的方法,但似乎只發現對C ++ / CLI解決方案不受管理。 我嘗試做的事情是否可能(假設不導出結構中包含的各個方法)?
這是一個相當遙遠的API。 庫不導出函數,而是導出包含函數指針的結構的地址。 請注意,C ++代碼如何調用GetProcAddress
,然后將結果解釋為指向結構的指針,而不是更常見的解釋為函數的指針。
從GetProcAddress
的文檔中,重點是:
從指定的動態鏈接庫(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
步驟顯然不是必需的,因為Marshaller已經代表您執行了此步驟。
我沒有測試任何代碼,只是將其輸入瀏覽器。 毫無疑問,這里有一些皺紋,但是此代碼的目的更多是作為指導,而不是直接可以使用的代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.