2010-04-09 37 views
7

Vì vậy, những gì tôi có là API C++ chứa trong * .dll và tôi muốn sử dụng ứng dụng C# để gọi các phương thức trong API.Hiển thị API C++ cho C#

Cho đến nay tôi đã tạo ra một dự án C++/CLR bao gồm các bản ngữ C++ API và quản lý để tạo ra một lớp "cầu nối" mà trông hơi giống như sau:

// ManagedBridge.h 
#include <CoreAPI.h> 
using namespace __CORE_API; 

namespace ManagedAPIWrapper 
{ 
    public ref class Bridge 
    { 
     public: 
      int    bridge_test(void); 
      int    bridge_test2(api_struct* temp); 
    } 
} 

.

// ManagedBridge.cpp 
#include <ManagedBridge.h> 

int Bridge::bridge_test(void) 
{ 
    return test(); 
} 

int Bridge::bridge_test2(api_struct* temp) 
{ 
    return test2(temp); 
} 

Tôi cũng có ứng dụng C# có tham chiếu đến C++/CLR "Bridge.dll" và sau đó sử dụng các phương pháp chứa bên trong. Tôi có một số vấn đề với điều này:

  1. tôi không thể tìm ra cách để gọi bridge_test2 trong chương trình C#, vì nó không có kiến ​​thức về những gì một api_struct thực sự là. Tôi biết rằng tôi cần phải sắp xếp đối tượng ở đâu đó, nhưng tôi có làm điều đó trong chương trình C# hoặc cầu C++/CLR không?
  2. Điều này có vẻ như một số dài nhất để lộ tất cả các phương pháp trong API, không phải là cách dễ dàng hơn mà tôi bỏ lỡ? (Điều đó không sử dụng P/Invoke!)

EDIT: Ok, vì vậy tôi đã có những điều cơ bản làm việc bây giờ nhờ vào phản ứng dưới đây, tuy nhiên struct của tôi (gọi nó là "api_struct2" ví dụ này) có cả nội dung gốc và công đoàn trong mã gốc C++, như sau:

typedef struct 
{ 
    enum_type1 eEnumExample; 
    union 
    { 
      long  lData; 
      int  iData; 
      unsigned char ucArray[128]; 
      char  *cString; 
      void  *pvoid; 
    } uData; 
} api_struct2; 

Tôi nghĩ rằng tôi đã tìm ra cách để có được enum làm việc; Tôi đã khai báo lại nó trong mã được quản lý và đang thực hiện một "native_enum test = static_cast (eEnumExample)" để chuyển đổi phiên bản được quản lý thành native.

Tuy nhiên, công đoàn đã khiến tôi bối rối, tôi không thực sự chắc chắn làm thế nào để tấn công nó .. Ý tưởng bất cứ ai?

Trả lời

3

Có, bạn đang chuyển một cấu trúc không được quản lý theo tham chiếu. Đó là một vấn đề đối với một chương trình C#, con trỏ khá không tương thích với bộ sưu tập rác.Không kể thực tế rằng nó có lẽ không có tuyên bố cho cấu trúc.

Bạn có thể giải quyết nó bằng cách tuyên bố một phiên bản quản lý của cấu trúc:

public value struct managed_api_struct { 
    // Members... 
}; 

Bây giờ bạn có thể khai báo phương pháp này như

int bridge_test2(managed_api_struct temp); // pass by value 

hoặc

int bridge_test2(managed_api_struct% temp); // pass by reference 

Pick sau này nếu cấu trúc có nhiều hơn 4 trường (~ 16 byte). Phương thức này cần sao chép các thành viên cấu trúc, từng cái một, thành một api_struct không được quản lý và gọi phương thức lớp không được quản lý. Điều này không may là cần thiết vì bố cục bộ nhớ của cấu trúc được quản lý không thể dự đoán được.

Đây là tất cả khá cơ học, bạn có thể nhận trợ giúp from SWIG. Đã không sử dụng nó bản thân mình, không chắc chắn nếu nó là đủ thông minh để đối phó với một cấu trúc thông qua.

Một cách tiếp cận hoàn toàn khác là làm cho lớp trình bao bọc sạch hơn bằng cách cho nó một hàm tạo và/hoặc thuộc tính cho phép bạn xây dựng nội dung của api_struct. Hoặc bạn có thể khai báo một lớp ref wrapper cho cấu trúc, giống như bạn làm trong mã được quản lý.

+0

Vì vậy, điều này có nghĩa là tôi tạo managed_api_struct trong C++/CLI * .dll hoặc trong chính mã C#? Ngoài ra, tôi nghĩ rằng bạn có thể chuyển một cấu trúc được quản lý đến mã gốc miễn là bạn sử dụng thuộc tính StructLayout? – Siyfion

+0

Nó không quan trọng, nhưng C++/CLI có ý nghĩa để tránh phụ thuộc vòng tròn. Có, [StructLayout] hoạt động nhưng bạn * phải * sử dụng lệnh gọi Marshal :: StructureToPtr(). Bố cục của cấu trúc được quản lý không thể dự đoán được. –

+0

Ok cảm ơn, tôi sẽ cho rằng một đi ngay bây giờ, xem nơi tôi nhận được quá. – Siyfion

2

vì nó không có kiến ​​thức về những gì một api_struct thực sự là

Bạn cần phải xác định một phiên bản quản lý trong một assembly .NET, sử dụng thuộc tính (như StructLayoutAttribute) để đảm bảo nó marshals một cách chính xác.

Điều này có vẻ giống như một rất dài dòng [...]

Cách tiếp cận khác là tạo ra một wrapper COM (ví dụ sử dụng ATL) xung quanh API của bạn. Đây có thể là nỗ lực nhiều hơn, nhưng ít nhất bạn tránh mã hóa kép của các định nghĩa cấu trúc và hàm cần thiết cho P/Invoke.

Correction: Bạn đã tạo một dự án C++/CLI: vì vậy chỉ cần thêm đúng '#pragma' để nói với trình biên dịch này là mã .NET, và sau đó đầu ra là một lắp ráp C# dự án chỉ có thể tham khảo .

+0

Điều gì sẽ thêm #pragma giúp tôi? Và có cách nào mà tôi có thể đặt tất cả các marshalling trong dự án C++/CLI, để mỗi dự án tham chiếu nó không phải khai báo lại các cấu trúc .NET tương đương? – Siyfion

+0

@Siyfion: '#pragma managed' cho phép chuyển đổi vào và ra khỏi mã được quản lý. Xem http://msdn.microsoft.com/en-us/library/0adb9zxe(VS.100).aspx – Richard

+0

Tôi hiểu rằng, nhưng nó hoạt động hiện tại, vì vậy tôi không thể nhìn thấy những gì thêm mà sẽ cho phép tôi làm! ? – Siyfion

2

Yuo đang cố gắng làm theo cách này phức tạp hơn thực sự. Những gì bạn muốn là hai các cấu trúc khác nhau. Một người quản lý và một người không được quản lý. Bạn hiển thị phiên bản được quản lý bên ngoài (với ứng dụng C# của bạn). Nó sẽ là tất cả ".Net-ish" không có khái niệm về công đoàn hay như vậy.

Trong cây cầu, bạn nhận được phiên bản được quản lý của cấu trúc, tạo thủ công cấu trúc không được quản lý và viết mã để di chuyển dữ liệu của bạn, theo trường qua cấu trúc không được quản lý. Sau đó gọi mã không được quản lý của bạn và cuối cùng chuyển dữ liệu trở lại cấu trúc được quản lý.

Điều tuyệt vời về C++/CLI là mã được quản lý cũng có thể hoạt động với mã không được quản lý và dữ liệu (và bao gồm các tệp .h không được quản lý).