2013-04-10 30 views
5

Tôi là một nhà văn C++ (MSVC), VB newbie đang cố gắng hỗ trợ một nhà văn VB.net chuyên gia, người đã không thực hiện nhiệm vụ này trước đây.truyền con trỏ tới hàm extern C trong một DLL từ VB

Chúng tôi muốn phát triển cả ứng dụng C/C++ và VB để sử dụng một DLL được viết bằng C++ với các hàm API extern-ed C. Chương trình C++ đang hoạt động tốt. Đó là VB, nơi chúng ta đang gặp khó khăn.

Các DLL cung cấp một chức năng extern C:

RegisterCallback(void* cbFuncPtr, void* dataPtr); 

LƯU Ý 1: Xem phần ghi chú của tôi dưới đây cho một sự thay đổi thiết kế và lý do chúng tôi đã làm cho nó.

CHÚ Ý 2: Cập nhật bổ sung được thêm dưới dạng câu trả lời bên dưới.

nơi hàm callback havs C typedef này:

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

Các cbFuncPtr được kỳ vọng sẽ trở thành một con trỏ hàm để một số chức năng VB mà sẽ được gọi là CALL_BACK. Các dataPtr là một con trỏ đến một cấu trúc dữ liệu có định nghĩa C này:

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

nơi a_C_ptr is an internal pointer in the DLL that the VB can cast to long`. Nó xác định duy nhất nơi trong DLL gọi lại đã được thực hiện và cho phép chức năng VB nhận ra các cuộc gọi từ cùng một vị trí/khác nhau.

Chúng tôi có thể truy cập và chạy hàm RegisterCallback() từ VB. Đăng nhập cho thấy chúng ta đạt được điều đó và dữ liệu đó được truyền vào. Đó là dữ liệu thực tế dường như là vấn đề.

Khi đọc khoảng một triệu mục diễn đàn, chúng tôi đã học được rằng VB không biết con trỏ là gì và cấu trúc VB không chỉ là bộ nhớ có tổ chức. Chúng tôi khá chắc chắn rằng "địa chỉ" của một cấu trúc VB không phải là những gì mà C nghĩ là một địa chỉ. Chúng tôi đã thấy các tham chiếu lặp đi lặp lại để "marshaling" và "managed data", nhưng thiếu hiểu biết đủ để biết điều gì đang cho chúng tôi biết.

Làm thế nào chúng ta nên mã VB để cung cấp cho DLL địa chỉ thực thi của hàm gọi lại của nó và làm thế nào để chúng tôi mã lên một cấu trúc VB mà DLL có thể điền vào giống như nó cho C++?

Có thể chúng ta cần một hàm DLL trong đó ứng dụng gọi điện có thể nói "C" hoặc "VB" và có phải xử lý các con trỏ sturcture khác nhau không? Nếu vậy, làm thế nào một mã C lên để điền vào cấu trúc VB?

+1

này [MS bài viết về làm thế nào để dữ liệu marshall đến một DLL] (http://msdn.microsoft.com/en-us/library/fzhhdwae.aspx) ít nhất có vẻ như nó có thể áp dụng .Nó được liên kết với [bài viết này về việc gọi C++ DLL từ VB] (http://social.msdn.microsoft.com/forums/en-US/Vsexpressvb/thread/4c486d10-fe8b-49da-a5f1-8054b82251ff/). Tôi không phải là một lập trình viên VB hoặc .NET, vì vậy tôi có thể bị mất điểm. –

+0

@DaveNewman Bài đăng cuối cùng cho cuộc thảo luận đó đã là một trợ giúp lớn. Là một lập trình viên C ý tưởng rằng dữ liệu có thể chỉ lên và di chuyển xung quanh và các con trỏ đến nó chỉ được tin cậy trong khoảng chừng nào câu lệnh truy vấn chúng, là dị giáo. Đoán tôi sẽ phải tạo cấu trúc ở phía bên DLL nói cho ứng dụng biết nơi tôi đặt dữ liệu. Điều đó đánh bại ý tưởng cho phép ứng dụng sở hữu dữ liệu sao cho nó sẽ ở đó bất chấp việc DLL có thể làm gì. –

+1

Theo như tôi biết, trong vb.net nó rất hữu ích để thích cấu trúc IntPtr cho con trỏ. Nhưng nếu bạn đã có một định nghĩa con trỏ, không chắc chắn nếu thực sự cần thiết. Con trỏ cũng có sẵn trong vb.net, chỉ cần một chút khó khăn hơn để tìm thấy – Amegon

Trả lời

1

Đây là một chút quá lớn và sâu sắc đối với chỉ là một chỉnh sửa để bài đăng ban đầu ...

Từ link đăng bởi @DaveNewman, tôi chiết xuất đá quý này:


Dưới đây là một chút về khung nhỏ gọn, nhưng cũng giống như vậy trong khung công tác đã xây dựng :

.NET Compact Framework hỗ trợ tự động đầm lầy cấu trúc và các lớp có chứa các loại đơn giản. Tất cả các trường được đặt ra tuần tự trong bộ nhớ theo thứ tự giống như chúng xuất hiện trong cấu trúc hoặc định nghĩa lớp.Cả hai lớp và cấu trúc đều xuất hiện trong mã gốc làm con trỏ tới cấu trúc C/C++.

Đối tượng trong vùng được quản lý có thể được di chuyển xung quanh bộ nhớ bất kỳ lúc nào bởi bộ thu gom rác, vì vậy địa chỉ thực của chúng có thể thay đổi mà không cần thông báo. P/Gọi tự động ghim xuống các đối tượng được quản lý được chuyển qua tham chiếu trong khoảng thời gian của mỗi cuộc gọi phương thức. Điều này có nghĩa là con trỏ được chuyển tới mã không được quản lý sẽ hợp lệ cho một cuộc gọi đó. Lưu ý rằng không có gì đảm bảo rằng đối tượng sẽ không được chuyển đến một địa chỉ bộ nhớ khác trên các cuộc gọi tiếp theo.

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


Đây là trở ngại lớn đối với một hàm RegisterCallback(fcnPtr, dataPtr). Con trỏ được chuyển vào lúc đăng ký có thể thay đổi bất cứ lúc nào RegisterCallback() không phải là câu lệnh hiện tại. Tác giả đăng bài tổng hợp nó theo cách này

Bạn không cần phải làm bất cứ điều gì vì cấu trúc được tự động ghim trong suốt thời gian của cuộc gọi.

ngụ ý, tất nhiên, không được ghim bên ngoài cuộc gọi. Vì lý do này, chúng tôi quyết định thay đổi thiết kế để có cấu trúc phản hồi tích hợp, vì vậy để nói, thế giới C/C++ của DLL, không phải trong không gian của VB. Bằng cách đó nó sẽ ở lại. Chữ ký của hàm gọi lại thực tế sẽ không thay đổi để chương trình VB có thể biết vị trí của DLL đặt câu trả lời. Điều này cũng cho phép người trả lời trong DLL phân bổ các cấu trúc phản hồi riêng biệt cho các nhu cầu riêng biệt.

+0

Bản cập nhật. Dựa trên bài đăng @DaveNewman và công việc tuyệt vời của đồng nghiệp của tôi, chúng tôi đang thêm vào các lớp được quản lý không được quản lý được đề xuất trong [Gọi mã được quản lý từ mã không được quản lý và ngược lại] (http://www.codeproject.com/Articles/ 9903/Calling-Managed-Code-from-Không được quản lý-Mã-và-phó # xx3493475xx). –

0

Một lần nữa bản cập nhật của tôi quá lớn để có một nhận xét đơn giản!

Cập nhật ngày 18 Tháng 4 năm 2013:

Vâng, cố gắng sử dụng mã từ Calling Managed Code from Unmanaged Code trích dẫn ở trên là một bức tượng bán thân. Chúng tôi đã phải thêm/clr vào DLL để biến DLL thành mã được quản lý, khiến nó không thể sử dụng được từ ứng dụng C.

Chúng tôi hiện đang thử nghiệm ví dụ tại Callback Sample mà tôi đã có thể hiển thị đã tạo một DLL hoạt động với cả VB và C++. Bạn cần có PinvokeLib.dll Source để thực hiện công việc này.

Đây là mã cho trình kiểm tra C++ (C thực sự). Biên dịch thành dự án MSVC.


Chú ý: Thông báo __cdecl trong dòng này:

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

Đó là bí mật tôi đã phải tìm. Các DLL và ứng dụng này được biên dịch với liên kết __cdecl, không __stdcall. Chúng là mặc định trong VC++ và tôi chỉ sử dụng các giá trị mặc định. Tôi đã thử thay đổi mọi thứ thành __stdcall nhưng điều đó không hiệu quả. Phải là __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; 
} 
Các vấn đề liên quan