簡體   English   中英

本機結構中包含的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);

但它們會產生異常

在NATIVEAPI.dll中找不到名為“ ...無論我嘗試...”的入口點

我一直在尋找實現此目的的方法,但似乎只發現對C ++ / CLI解決方案不受管理。 我嘗試做的事情是否可能(假設不導出結構中包含的各個方法)?

這是一個相當遙遠的API。 庫不導出函數,而是導出包含函數指針的結構的地址。 請注意,C ++代碼如何調用GetProcAddress ,然后將結果解釋為指向結構的指針,而不是更常見的解釋為函數的指針。

GetProcAddress的文檔中,重點是:

從指定的動態鏈接庫(DLL)檢索導出的函數或變量的地址。

您不能使用DllImport訪問此庫的導出,因為DllImport用於函數而不是變量。

這是您必須執行的操作:

  1. 使用LoadLibrary加載DLL並獲取模塊句柄作為IntPtr
  2. 調用GetProcAddress以獲取該結構的地址。
  3. 使用Marshal.PtrToStructure獲取包含函數指針的托管結構。
  4. 對結構的每個成員使用Marshal.GetDelegateForFunctionPointer以獲得可以隨后調用的委托。
  5. 完成庫操作后,通過調用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM