2013-04-10 60 views
5

我是一位C++(MSVC)編寫者,VB新手試圖幫助一位尚未完成此任務的專家VB.net編寫者。在VB中傳遞指向extern C函數的指針

我們希望開發C/C++和VB應用程序,以使用用C++編寫的帶有C語言API函數的DLL。 C++程序工作得很好。這是我們遇到困難的VB。

DLL提供一個extern C功能:

RegisterCallback(void* cbFuncPtr, void* dataPtr); 

注1:請參見下面的設計變化,我們把它的原因我的筆記。

注2:作爲下面的答案添加了附加更新。

在回調函數HAVS這款C的typedef:

typedef (void)(* CALL_NACK)(void*); 

cbFuncPtr有望成爲一個函數指針一些VB函數將被調用爲CALL_BACK。所述dataPtr是一個指向一個數據結構,其具有該C定義:

typedef struct 
{ 
    int  retCode; 
    void* a_C_ptr; 
    char  message[500]; 
} cbResponse_t; 

其中a_C_ptr is an internal pointer in the DLL that the VB can cast to long`。它唯一標識DLL中回調的位置,並允許VB功能識別來自相同/不同位置的呼叫。

我們能夠從VB訪問和運行RegisterCallback()功能就好了。記錄顯示我們到達那裏並且數據被傳入。這是實際的數據,似乎是問題所在。

在閱讀大約一百萬個論壇條目時,我們瞭解到VB不知道指針是什麼,VB結構不僅僅是有組織的內存。我們很確定VB結構的「地址」不是C認爲的地址。我們已經看到了對「編組」和「託管數據」的重複引用,但缺乏足夠的理解知道這些信息告訴我們什麼。

我們應該如何編寫VB代碼給DLL的回調函數的執行地址,以及如何編寫一個VB構造,使DLL可以像C++一樣填充?

我們是否需要一個DLL函數,其中調用應用程序可以說「C」或「VB」,並且DLL具有不同的結構指針處理方式?如果是這樣,那麼如何編寫C來填充VB結構?

+1

這[MS文章,瞭解如何馬歇爾數據到一個DLL(http://msdn.microsoft.com/en-us/library/fzhhdwae.aspx)至少看起來像它可能適用。它被[這篇關於從VB調用C++ DLL的文章]鏈接到(http://social.msdn.microsoft.com/forums/en-US/Vsexpressvb/thread/4c486d10-fe8b-49da-a5f1-8054b82251ff/)。我不是一個VB或.NET程序員,所以我可能會脫穎而出。 – 2013-04-10 19:59:50

+0

@DaveNewman這次討論的最後一篇文章已經很有幫助。作爲一名C程序員,數據可以隨時隨地移動,並且指向它的指針只在查詢它們的語句時才被信任,是異端。猜測我將不得不在DLL側創建結構,並告訴應用程序在哪裏放置數據。這違背了讓應用程序擁有數據的想法,以便儘管DLL可能會這樣做,它仍然存在。 – 2013-04-10 20:41:47

+1

據我所知,在vb.net中,選擇指針的IntPtr結構是非常有用的。但是如果你已經有了一個指針定義,不確定是否真的需要。指針也可在vb.net上找到,只是有點難以找到 – Amegon 2013-04-10 20:59:11

回答

1

這是一個有點過大和深,只是一個編輯到原來的帖子...

從張貼@DaveNewman的link,我摘錄了這種寶石:


這裏有一點關於緊湊框架,但它在 成年人框架中是一樣的:

.NET Compact Framework支持結構的自動編組 和包含簡單類型的類。所有字段按順序排列在內存中 ,順序與 結構或類定義中顯示的順序相同。這兩個類和結構都以 本機代碼的形式出現在指向C/C++結構體的指針中。

垃圾回收器在託管堆中的對象可隨時在內存中移動 ,因此它們的物理地址可能會在沒有通知的情況下更改 。 P/Invoke自動針對每個方法調用的持續時間通過引用傳遞的管理對象 。這意味着 傳遞給非託管代碼的指針將對該一次調用有效。 請注意,在後續調用中,不保證該對象不會被移動到不同的內存地址。

http://msdn.microsoft.com/en-us/library/aa446538.aspx#netcfmarshallingtypes_topic6


這是一個RegisterCallback(fcnPtr, dataPtr)功能的主要障礙。在註冊時傳入的指針可能隨時改變,RegisterCallback()不是當前的聲明。發帖作者以這種方式總結出來

您不需要做任何事情,因爲結構會在通話期間自動固定。

暗示當然不會在通話之外固定下來。

由於這個原因,我們決定進行設計更改,以便將內置的響應結構,即DLL的C/C++世界,而不是VB的空間。這樣它會保持。實際的回調函數的簽名將保持不變,以便VB程序可以知道DLL放置響應的位置。這也允許DLL中的響應者爲不同需求分配不同的響應結構。

+0

更新。基於@DaveNewman的發佈以及我的同事的出色工作,我們正在添加[從非託管代碼調用託管代碼,反之亦然]中提出的託管非託管類(http://www.codeproject.com/Articles/) 9903 /調用,託管代碼從 - 非託管代碼和副#xx3493475xx)。 – 2013-04-12 16:23:32

0

我的更新再次過大,僅僅只是一個評論!

更新2013年4月18日:

好,使用代碼Calling Managed Code from Unmanaged Code上面提到的嘗試是一個半身像。我們最終不得不向DLL添加/ clr,將DLL轉換爲託管代碼,這使得它無法從C應用程序中使用。

我們現在測試的例子是Callback Sample,我能夠展示一個可以同時使用VB和C++的DLL。你需要有PinvokeLib.dll Source才能完成這項工作。

這是C++(C真正)測試程序的代碼。編譯爲MSVC項目。


注意:注意在此行中__cdecl:

typedef bool (__cdecl *FPtr)(BOOL_FP_INT fp, int i); 

這是祕密,我不得不找。 DLL和這個應用程序使用__cdecl鏈接進行編譯,而不是__stdcall。它們是VC++中的默認值,我只是使用了默認值。我嘗試將所有內容更改爲__stdcall,但沒有奏效。必須是__cdecl。


// PinvokeTester.cpp : Defines the entry point for the console application. 
// 


#include <stdio.h> 
#include <cstdio> 
#include <stdlib.h> 
#include <cstdlib> 
#include <string.h> 
#include <cstring> 
#include <sstream> 
#include <iostream> 
#include <algorithm> 
#include <Windows.h> 

#define PINVOKELIB_API __declspec(dllimport) 

HINSTANCE  hLib;       // Windows DLL handle 

bool  CALLBACK VBCallBack(int value); 
bool  AttachLibrary(void); 
void * GetFuncAddress(HINSTANCE hLib, const char* procname); 

int main(int argc, char* argv[]) 
{ 
    if (!AttachLibrary()) 
    { 
     printf("Lib did not attach.\n"); 
     exit(1); 
    } 

    typedef bool (CALLBACK *BOOL_FP_INT)(int i); 

    typedef bool (__cdecl *FPtr)(BOOL_FP_INT fp, int i); 

    FPtr TestCallBack = (FPtr)GetFuncAddress(hLib, "TestCallBack"); 

    TestCallBack((BOOL_FP_INT)VBCallBack, 255); 

    return 0; 
} 

bool CALLBACK VBCallBack(int value) 
{ 
    printf("\nCallback called with param: %d", value); 
    return true; 
} 


bool  AttachLibrary(void) 
{ 
    // Get a var for the IPC-dll library. 
    std::string dllName; 

    /*--- First, link to the IPC-dll library or report failure to do so. ---*/ 
    dllName = ".\\PinvokeLib"; 
    if (NULL == (hLib = LoadLibraryA(dllName.c_str()))) 
    { 
     printf("\nERROR: Library \"%s\" Not Found or Failed to Load. \n\n", dllName.c_str()); 
     printf("\"%s\"\n", GetLastError()); 
     return false; 
    } 

    return true; 
} 

//===================================================================== 
void * GetFuncAddress(HINSTANCE hLib, const char* procname) 
{ 
    void * procAddr = NULL; 

    procAddr = (void *)GetProcAddress(hLib, procname); 

    // If the symbol wasn't found, handle error --------------------- 
    if (NULL == procAddr) 
    { 
     std::cout << "ERROR: Could not get an address for the \"" 
       << procname << "\" function. : " 
       << GetLastError() << std::endl; 
     exit(7); 
     procAddr = (void*)NULL; 
    } 

    return procAddr; 
} 
相關問題