📜 ⬆️ ⬇️

C ++ / CLI - sticky tongue

In this topic I will talk about C ++ / CLI - an intermediate language for pasting code in C / C ++ and .NET

This is a fairly common task, because tons of time-tested high-performance code written in C / C ++ cannot be rewritten into controlled languages.

Our task is to provide a .NET interface to these libraries. But how to do it if they are written in C / C ++?
')
Microsoft offers two solutions to the problem.


P / Invoke


The first is the Platform Invoke mechanism, with which you can use libraries with C ABI . It looks something like this:

[DllImport ("user32.dll")] static extern bool MessageBeep (System.UInt32 type); 


P / Invoke provides marshalling (translation) of all simple data types, as well as strings, structures with fields, and even callback functions (delegates).


C ++ / CLI


But what if the library does not have a C-interface, or P / Invoke’s capabilities are missing? C ++ / CLI comes to the rescue.

This is the ideal language for generating glue code between managed and unmanaged execution environments, because it allows you to generate code for both environments + generates a transition code, eliminating the need to glue something manually.

CLI suffix - indicates that the language implements the Common Language Infrastructure specification, i.e. is a full member of the .NET platform language family

So, we will need Visual C ++ Express 2008. Click on “New Project”, select the project type - CLR. This will create a project with the default / clr (Use Common Language Runtime) option set. This means that the compiler will generate the correct MSIL build and will let us use the new syntax - which we will now consider.


Crash course


Allocation of managed / unmanaged-blocks


By default, the compiler considers all C ++ project code to be compiled into MSIL. Most likely, it will not work (it will not compile), and if it compiles, then this is most likely not what you would like.

Special preprocessor commands allow you to specify which part of the code should be compiled in x86, and which part in MSIL.

 /* ...   ... */ #pragma unmanaged /* ...   ++ ... */ #pragma managed /* ...    ... */ 


Most likely, if you connect a library, you will first have to compile it into a static .lib and remember to wrap its headers in the #pragma unmanaged block. Or build a library into one large .c file (translation unit) - as in SQLite amalgamation .

Connecting MSIL assemblies


Again preprocessor:

 #using <System.dll> #using "..\MyLocalAssembly.dll"> 


Namespaces


Here, just like in regular C ++:

 using namespace System::Collections::Generic; 


Type-value declaration


This is what in C # is called “struct”.

 public value class Vector { public: int X; int Y; Vector (int x, int y) : X (x), Y (y) {} }; 


Reference type declaration, methods, properties



 public ref class Resource { public: void PublicMethod () { ... } property int SomeProperty { int get () { return ... } void set (int value) { ... } }; }; 


By the way, interestingly, C ++ / CLI supports some CLR features that are not implemented in C #. For example, property indexers - you can define indexers (operator []) for individual properties, and not just for the entire class. Here only such code cannot be caused from C # :)

Interface type declaration


What is called interface in C #:

 public interface class IApplicationListener { void OnStart (); void OnWait (); void OnEnd (); }; 


Enum's



 public enum struct RenderMode { Normal = FT_RENDER_MODE_NORMAL, Light = FT_RENDER_MODE_LIGHT, Mono = FT_RENDER_MODE_MONO, LCD = FT_RENDER_MODE_LCD }; 


Life inside the method - basic syntax



 /*   GC-, nullptr —  null  C# */ System::String ^ string = nullptr; /*  , gcnew —  new  C#,      */ throw gcnew System::Exception (L"   "); 


Generics, type constraints, arrays



Here the generic keyword is used similarly to template in C ++:

 generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /*  array<T> — CLR-,  T[]  C# */ } 


Making a wrapper for unmanaged resource


Very frequent task. We use the IDisposable pattern.

Pay particular attention that C ++ - the destructor in the ref class is automatically translated into the Dispose () method. For finalizers, a different syntax is used.

 public ref class Tessellator : System::IDisposable { internal: //       Unmanaged::Tessellator * tess; public: Tessellator (int numSteps) { tess = new Unmanaged::Tessellator (numSteps); } ~Tessellator () // IDisposable::Dispose () { delete tess; } }; 


We receive the crude pointer on GC-object


This operation in the CLR architecture is called “pinning”. When “nailing” the object is prohibited to move in the heap during garbage collection and compaction of the heap. This allows unmanaged code to use the address of the object and write / read something at that address.

Be careful, as nailing objects interferes with the garbage collector and strongly fragments the pile. This operation is intended only for short-term manipulations with heap objects.

"Pinned" pointers are implemented in C ++ / CLI as a template RAII -type
 pin_ptr.    std::auto_ptr ( ,  ).    pin_ptr   , GC-  . 

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!
pin_ptr. std::auto_ptr ( , ). pin_ptr , GC- .

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!

pin_ptr. std::auto_ptr ( , ). pin_ptr , GC- .

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!

pin_ptr. std::auto_ptr ( , ). pin_ptr , GC- .

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!
 pin_ptr.    std::auto_ptr ( ,  ).    pin_ptr   , GC-  . 

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!
pin_ptr. std::auto_ptr ( , ). pin_ptr , GC- .

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!
 pin_ptr.    std::auto_ptr ( ,  ).    pin_ptr   , GC-  . 

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!
pin_ptr. std::auto_ptr ( , ). pin_ptr , GC- .

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!
 pin_ptr.    std::auto_ptr ( ,  ).    pin_ptr   , GC-  . 

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!
pin_ptr. std::auto_ptr ( , ). pin_ptr , GC- .

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!

pin_ptr. std::auto_ptr ( , ). pin_ptr , GC- .

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!

pin_ptr. std::auto_ptr ( , ). pin_ptr , GC- .

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!
 pin_ptr.    std::auto_ptr ( ,  ).    pin_ptr   , GC-  . 

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!
pin_ptr. std::auto_ptr ( , ). pin_ptr , GC- .

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!

pin_ptr. std::auto_ptr ( , ). pin_ptr , GC- .

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!

pin_ptr. std::auto_ptr ( , ). pin_ptr , GC- .

generic<typename T> where T : value class Buffer ^ CreateVertexBuffer (array<T> ^ elements) { /* */ pin_ptr<T> p = &(elements[0]); /* , * */ void * address = p; }




System::String wide char (wchar_t *):

#include <vcclr.h> System::String ^ path = ... /* "" String */ pin_ptr<const wchar_t> pathChars = PtrToStringChars (path);

System::String ANSI C (RAII-):

struct StringToANSI { private: const char * p; public: StringToANSI (String ^ s) : p ((const char*) ((Marshal::StringToHGlobalAnsi (s)).ToPointer ())) { } ~StringToANSI() { Marshal::FreeHGlobal (IntPtr ((void *) p)); } operator const char * () { return p; } };

ANSI- System::String:

const char * ptr = "ANSI string"; System::String ^ str = gcnew System::String (ptr);

GC- unmanaged-
- . . C++/CLI . , gcroot:

#include <msclr/auto_gcroot.h> #pragma unmanaged class UnmanagedWindowCounterpart { private: /* */ gcroot<IInputEventListener ^> MouseEventListener; ... };




, . MSDN.

Happy coding!

Source: https://habr.com/ru/post/47732/


All Articles