Visual C Concerns

team lib

Visual C++ Concerns

This section is going to look at interop considerations that apply only to managed C++ code.

Marshaling Argument Types

No marshaling is required between managed and unmanaged types that have the same representation, such as Int32 and int . Other types will need to be converted. Table 12-3 shows how types are represented in the Win32 API, in unmanaged C++, in managed C++, and in the .NET Framework.

Table 12-3: Type Conversion with Platform Invoke

Windows

C++

Managed C++

.NET Framework

HANDLE

void*

void*

IntPtr , UIntPtr

BYTE

unsigned char

unsigned char

Byte

SHORT

short

short

Int16

WORD

unsigned short

unsigned short

UInt16

INT

int

int

Int32

UINT

unsigned int

unsigned int

UInt32

LONG

long

long

Int32

ULONG

unsigned long

unsigned long

UInt32

DWORD

unsigned long

unsigned long

UInt32

FLOAT

float

float

Single

DOUBLE

double

double

Double

BOOL

long

bool

Boolean

CHAR

char

char

Char

LPSTR

char*

[in] String* , [in,out] , StringBuilder*

[in] String , [in,out] , StringBuilder

LPCSTR

const char*

String*

String

LPWSTR

wchar_t*

[in] String* , [in,out] , StringBuilder*

[in] String , [in,out] , StringBuilder

LPCWSTR

const wchar_t*

String*

String

Marshaling of structures and arrays is covered in Chapter 13, along with more details of string marshaling.

Pinning

The previous section introduced the concept of pinningobtaining a pointer to a managed object and telling the garbage collector not to move the object while the pointer is still in scope.

In managed C++, you use the __pin keyword to create a pinning pointer from a __gc pointer, and the action of creating a pinning pointer pins the object in memory. Listing 12-5 contains an example. You can find this sample in the Chapter12\CppPin folder of the books companion content.

Listing 12-5: CppPin.cpp
start example
 #using<mscorlib.dll> usingnamespaceSystem; #include<iostream> usingnamespacestd; //Amanagedclass __gcclassGcClass { public: intval; GcClass(intn):val(n){} }; #pragmaunmanaged voidUnmanagedFunction(int*pn) { cout<< "nis " <<*pn<<endl; } #pragmamanaged voidmain() { //Createamanagedinstance GcClass*pgc=newGcClass(3); //Createapinningpointer GcClass__pin*ppin=pgc; //Passamembertothefunction UnmanagedFunction(&ppin->val); //Zerooutthepinningpointer ppin=0; } 
end example
 

A managed class contains an integer member, whose address I want to pass to an unmanaged function. This means that the address of the managed instance must not change for the duration of the unmanaged function call. I create a pinning pointer, which locks the object in memory, and can then pass the address of a data member to the function. When the pinning pointer is zeroed outor goes out of scopethe instance will be free to move.

Calling Exported C++ Member Functions

Youll need to take special care when calling functions in DLLs that are class members . When a C++ class member function is compiled, the compiler generates a mangled name a name for the function that encodes its actual name, plus details of the class it belongs to, and its argument and return types. If you use Platform Invoke to call such a function, youll need to specify the mangled name and use the appropriate calling convention.

Here is an example that shows how to call a C++ member function. First lets look at the declaration of the class and the exported function:

 classMyClass { public: intSquare(inti){returni*i;} }; 

And here is the Platform Invoke prototype you can use to call it:

 [DllImport("MyDll.dll",EntryPoint="?Square@MyClass@@QAEHH@Z", CallingConvention=CallingConvention::ThisCall)] extern "C" intSquare(IntPtrpThis,inti); 
Tip 

You can find the exported name of a C++ member function by using the Dumpbin.exe utility with the /exports option to examine the DLL.

The EntryPoint parameter gives the exported name of the member function, but the programmer can call it using the more friendly name Square . Because the function is a C++ member function but is not static, it needs to be passed a pointer to an object of the appropriate type, which will act as the this pointer for the function. You can see the this pointer being passed as the first parameter. You also need to choose the correct calling convention: for C++ member functions, this will be the ThisCall convention, which assumes that the first parameter in the call will be the this pointer.

IJW (It Just Works)

Youve seen how Platform Invoke is used to call unmanaged functions from managed code, but many times in managed C++ code this appears to happen automatically. For instance, consider the following class definition:

 #include<iostream> usingnamespacestd; __gcclassGcClass { public: intval; GcClass(intn):val(n) { cout<< "GcClass::ctor" <<endl; } }; 

The constructor for the managed class uses unmanaged iostream functionality to display a message, without any need for coding Platform Invoke prototypes .

The It Just Works (or IJW) mechanism allows C++ coders to call unmanaged functions in DLLs simply by including the relevant header file and linking with a link library. Because the combination of a header file and link library contains all the information needed by Platform Invoke, in the simplest cases no further work is needed by the programmer.

Simple types will be marshaled automatically, as described in Table 12-3. Sometimes, however, youll need to marshal data manually. This is especially true where arguments consist of strings, arrays, and structures. Marshaling of these types is covered in Chapter 13.

IJW vs. P/Invoke

Both IJW and Platform Invoke use the same underlying mechanism, but there are some cases when you might prefer one to the other.

The IJW mechanism has several features that might provide an advantage over Platform Invoke in some situations:

  • There is no need for Platform Invoke prototypes.

  • IJW can be slightly faster than Platform Invoke because the developer will have done any pinning that is needed.

  • Marshaling is more explicit, and this can help developers decide on the most efficient marshaling method.

  • If youre calling a function more than once, it will be more efficient to marshal the data once and then call it multiple times.

IJW also has some disadvantages:

  • Marshaling needs to be specified in code rather than declared by attributes.

  • Marshaling code is inline, which might interfere with program logic.

  • Marshaling APIs return IntPtr for pointers, so extra ToPointer calls will be needed to extract the underlying pointer.

The advantages of Platform Invoke can be summarized as follows :

  • It works the same way in every .NET language.

  • There is no need to write marshaling code.

  • It is a simple declarative mechanism.

There are, however, some disadvantages to Platform Invoke:

  • Marshaling is performed for every call. This can be wasteful if the same call is being made several times.

  • Some functions are not suitable for calling via Platform Invoke. For example, consider an unmanaged function that dynamically allocates memory for a string and returns a char* . The return type will be mapped onto a String* , but the caller has no way of deallocating this memory after it has been used. In this case, IJW provides a better mechanism.

C++ is unique in the .NET world, in that it has two separate ways to invoke unmanaged functions. Which method you choose will depend on how you are proposing to use the unmanaged functions that you call.

 
team lib


COM Programming with Microsoft .NET
COM Programming with Microsoft .NET
ISBN: 0735618755
EAN: 2147483647
Year: 2006
Pages: 140

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net