Was this page helpful?
Your feedback about this content is important. Let us know what you think.
Additional feedback?
1500 characters remaining
Export (0) Print
Expand All

Managed Extensions for C++ Frequently Asked Questions

Visual Studio .NET 2003

Click the text for the option you are interested in; the node will expand to show the choices in that category.

See managed, unmanaged.

See Managed Extensions for C++ Migration Guide.

If you defined an operator on a gc class using pointers, there's no way to call it other than through an explicit function call, because gc classes cannot be passed by value. However, gc references can offer a solution:

// mcppfaq_opoverload.cpp
// compile with: /c /clr
#using < mscorlib.dll >
using namespace System;

__gc class A
{
public:
   A(Int32 i){}
   static A& op_Addition(A& a, A& b);
};

int main()
{
   A* p1 = new A(1);
   A* p2 = new A(2);
   A* p3 = &(*p1 + *p2);
}

The default is that a pointer to an unmanaged type is a __nogc pointer and a pointer to a managed type is a __gc pointer.

System::Int32* == System::Int32 __gc*   // implicit __gc pionter
int* == int __nogc*   // implicit __nogc pointer
System::Int32**...* == System::Int32 __gc* __nogc* __nogc*...
System::String**...* == System::String __gc* __gc* __nogc* ... __nogc*

gcroot overloads operator->, allowing you to use it directly without casting. It abstracts the details of calling IntPtr::ToInt32 or IntPtr::ToInt64, depending on which platform the executable was compiled for (Win32 or Win64).

// mcppfaq_gcroot.cpp
// compile with: /clr
#using < mscorlib.dll >
#include < vcclr.h >

using namespace System;
using namespace System::Runtime::InteropServices;

#pragma managed
class StringWrapper
{
   gcroot< String* > m_handle;
   // int m_handle;
public:
   StringWrapper()
   {
      m_handle = new String(S"ManagedString");
   }

   ~StringWrapper() {}
   // more code here...
   void PrintString()
   {
      Console::WriteLine(S"StringWrapper::m_handle == {0}", m_handle);
   }
};

#pragma unmanaged
int main()
{
   StringWrapper s; 
   s.PrintString();
}

Output

StringWrapper::m_handle == ManagedString

Take the address of the first element and assign it to and an interior/whole gc pointer.

System::Byte bArr[] = {1, 2, 3};
System::Byte* pbArr = &bArr[0]; // implicit interior gc pointer

In managed applications, pointers can be of two types:

  • Unmanaged pointers   The traditional C++ pointer. This is a pointer to an unmanaged block of memory from the standard C++ heap.
  • __gc pointers   A new type of pointer available to managed applications. This is a pointer to a managed block of memory from the common language runtime heap. Automatic garbage collection is performed on this heap.

Because of the many features of __gc pointers, there are important differences between the two pointer types in a managed application. For details, see 7 __gc Pointers. For an example, see PinningPtrs Sample.

// mcppfaq_gcclasswitharray.cpp
// compile with: /LD
#include < stdio.h >
#include < windows.h >
struct S
{
   wchar_t* str;
   int intarr[2];
};

extern "C"
{
   __declspec(dllexport) int b(S* param)
   {
      printf("str: %S\n", param -> str);
      printf("In native: intarr: %d, %d\n", param -> intarr[0], param -> intarr[1]);
      fflush(stdout);   // for correct ordering when redirect to file
      param -> intarr[0] = 300;
      param -> intarr[1] = 400;
      return 0;
   }
};

and then,

// mcppfaq_gcclasswitharray2.cpp
// compile with: /clr
#using < mscorlib.dll >
using namespace System;
using namespace System::Runtime::InteropServices;
[StructLayout(LayoutKind::Sequential, CharSet = CharSet::Unicode)]
__gc class S
{
public:
   [MarshalAsAttribute(UnmanagedType::LPWStr)] String* str;
   [MarshalAsAttribute(UnmanagedType::ByValArray,
      ArraySubType = UnmanagedType::I4, SizeConst=2)] Int32 intarr[];
};

[DllImport("mcppfaq_gcclasswitharray.dll", CharSet=CharSet::Unicode)]
int b([In][Out] S* param);

int main()
{
   S* param = new S;
   param -> str = S"Hello";
   param -> intarr = new Int32[2];
   param -> intarr[0] = 100;
   param -> intarr[1] = 200;
   b(param);   // Call to native function
   Console::WriteLine(S"In managed: intarr: {0}, {1}", __box(param -> intarr[0]), __box(param -> intarr[1]));
}

Output

str: Hello
In native: intarr: 100, 200
In managed: intarr: 300, 400

Using Managed Extensions for C++, a __gc array can be declared by using the __gc keyword or by containing a managed type. Because __gc arrays are allocated from the managed heap, these arrays have additional criteria and features for declaration and usage when compared to standard (unmanaged) arrays. For more information, see 4.5 __gc Arrays.

The following example declares and instantiates a simple __gc array of integers:

// gc_arrays.cpp
// compile with: /clr
#using <mscorlib.dll>

int main ()
{
   int MyIntArray __gc[]= new int __gc[100];
   return 0;
}

A __gc array has many features that are new to the C++ developer and others that are familiar.

  • Automatic initialization of the array

    A __gc array is automatically initialized when first created based on the typeof the array members. For details, see 4.5.1 Automatic Array Initialization.

  • Multidimensional arrays

    Like standard C++ arrays, __gc arrays can be of varying dimensions. However, there are some slight differences in syntax and usage. For details, see 4.5.5 Multidimensional Arrays.

  • Covariance of arrays

    Array covariance means that, given two types (A and B), an array value of type A can be treated as a reference to an array value of type B, provided there is an implicit reference conversion from B to A. With Managed Extensions, this implicit conversion is available if one class is a direct or indirect base class of the other. For details, see 4.5.6 Array Covariance.

    As part of the support for array covariance, assignments to elements of reference type arrays include a run-time check that ensures that the value being assigned to the array element is actually of a permitted type. If the type is not permitted, an exception System::ArrayTypeMismatchException is thrown.

Example

// gc_arrays2.cpp
// compile with: /clr
#using < mscorlib.dll >

using namespace System;

// This returns an array from a function.
Byte Function() []
{
   return new Byte[12];
}

String* Function2() []
{
   return new String*[12];
}

// This passes an 'IN' array argument to a function.
void Function3( Byte theArray[] )
{
}

void Function4( String* theArray[] )
{
}

// This passes a 'REF' array argument to a function.
typedef Byte ByteArray[];

void Function5( /*REF*/ ByteArray& theArray  )
{
   theArray = new Byte[ 123 ];
} 

void Function6( Byte (&theArray) [] )
{
   theArray = new Byte[ 123 ];
}

// This makes the function compliant with the common language
// specification (usable in C# and Visual Basic).
void Function7( Byte (*theArray) [] )
{
   *theArray = new Byte[ 123 ];
}

typedef String* StringArray[];
void Function8( /*REF*/ StringArray& theArray )
{
}

// This makes the function compliant with the common language
// specification (usable in C# and Visual Basic).
void Function9( String*(*theArray) [] )
{
   *theArray = new String*[ 123 ];
}

int main()
{
   // This declares an array variable.
   Byte myArray[] = new Byte[ 12 ];   // Byte is a value type
   Byte myArray2[] = { 12, 23, 32 };

   String* myArray3[] = new String*[12];
   String* myArray4[] = { S"onut", S"onutaou" };
}

Managed Extensions implements a type of abstract class that simulates the behavior of C++ function pointers. In addition, delegates provide support for events in managed applications. For more information and sample code, see 9 Delegates and __delegate.

You can implement a property (see 13 Properties for details) for a managed class using the __property keyword. Managed Extensions supports two kinds of properties:

  • Scalar properties   The common implementation of a property with a Get and Set function for access to a single property value.
  • Indexed properties   A different implementation of a property where the user can access the property like an array.

The C# params keyword declares a variable list of arguments for a function.

// mcppfaq_params_equiv.cpp
// compile with: /clr
#using < mscorlib.dll >

using namespace System;

public __gc class MyClass
{
public:
   void Trace( String* format, [ParamArray]Object* args[] )
   {
   }
};


int main()
{
   MyClass* myClass = new MyClass();
   Object* arguments[] = { S"123", S"234" };
   myClass->Trace( S"Some Format", arguments );
}

Yes, but it will have .dll extension instead of .netmodule if you don't specify the output filename explicitly.

Use of the ParamArray construct depends in part on also having implicit boxing. Because Managed Extensions does not support implicit boxing, there is no support for ParamArrays because they could not be used to pass value types.

The equivalent to operator[] in the managed world is an indexed property. For more information, see 13.2 Indexed Properties.

There isn't one. Floating point literals are Double by default. If you want you explicitly say it is Single, the format specifier is F (Single s; s = 1.23123F;).

// mcppfaq_is_and_as.cpp
// compile with: /clr
#using <mscorlib.dll>
__gc __interface I
{
public:
   void F( void );
};

__gc class C : public I
{
public:
   void F( void ) { }
};
 
int main()
{
   C* c = new C();
   I* i = __try_cast< I* >(c);   // is (maps to castclass in IL)
   I* ii = dynamic_cast< I* >(c);   // as (maps to isinst in IL)
}

You can also do something like this to simulate as:

// mcppfaq_is_and_as2.cpp
// compile with: /clr
#using <mscorlib.dll>
using namespace System;

template < class T, class U > 
System::Boolean isinst(U u)
{
   return dynamic_cast< T >(u) != 0;
}

int main()
{
   Object* o = S"test";
   if ( isinst<Object*>(o) )
      Console::WriteLine(S"o is a string");
}

Output

o is a string

C# uses a special attribute to indicate the name of the indexer for a class. If the indexer name is other than "Item", it's called named indexer. With MC++, you can have as many index properties as you want, but you have to choose a default name that C# can use. If you have multiple indexed properties, you have to call the rest via get_ and set_ functions in C#.

// mcppfaq_indexer.cpp
// compile with: /clr /LD
#using < mscorlib.dll >
using namespace System;
using namespace System::Reflection;

[DefaultMember(S"Item")]
public __gc class CPPIndexerTest
{
   Int32 arr[];
public: 
   CPPIndexerTest(): arr(new Int32[10])
   {
   }
   __property void set_Item(Int32 nIndex, Int32 Value)
   {
      arr[nIndex] = Value;
   } 
   
   __property int get_Item(Int32 nIndex)
   {
      return arr[nIndex]; 
   }

   __property void set_MyItem(Int32 nIndex, Int32 Value)
   {
      arr[nIndex] = Value;
   }

   __property int get_MyItem(Int32 nIndex){
      return arr[nIndex];
   }
};

and then,

// mcppfaq_indexer2.cs
// compile with: /reference:mcppfaq_indexer.dll
// a C# program

using System;
public class A
{
   public static void Main()
   {
      CPPIndexerTest t = new CPPIndexerTest();
      t[1] = 100;
      Console.WriteLine(t[1]);
      t.set_MyItem(1, 100);
      Console.WriteLine(t.get_MyItem(1));
   }
}

Yes, however System::UInt32* is not equivalent to unsigned int*. System::UInt32* is System::UInt32 __gc* whereas unsigned int* is unsigned int __nogc*.

C# function:

public void SomeFunc(out int x) { x = 2; }

MC++ equivalent:

public: void SomeFunc([System::Runtime::InteropServices::Out] Int32* x) { *x = 2; }

C# function:

public void SomeFunc(ref int x) { x = 2; }

MC++ equivalent:

public: void SomeFunc(Int32* x) { *x = 2; }

This requires definite assignment of the parameter before the call to SomeFunc.

Moreover, there's no problem using a C++ reference to express this. In metadata, a C++ reference is the same as a C++ pointer, except for modopt:

[Microsoft.VisualC]Microsoft.VisualC.IsCXXReferenceModifier.

C# does not distinguish between the two:

// mcppfaq_cpp_byref.cpp
// compile with: /clr /LD
#using < mscorlib.dll >
using namespace System;

public __gc struct S
{
   static void f( [System::Runtime::InteropServices::Out] Int32 & i)
   {
      i = 20; 
   }
};

and then,

// cpp-byref-csc.cs
// compile with: /reference:mcppfaq_cpp_byref.dll
// a C# program
using System;
public class F
{
   public static void Main()
   {
      int i;
      S.f(out i);
      Console.WriteLine(i);
   }
   public void b(out int i)
   {
      i = 10;
   }
}

From a managed component you can call a native function with function pointer parameters where the native function then can call the member function of the managed component's delegate.

// mcppfaq_delnative.cpp
// compile with: /LD
#include <windows.h>
#include <stdio.h>
extern "C"
{
   __declspec(dllexport)
   void nativeFunction(void (CALLBACK *managedFunction)(const char* str))
   {
      printf("in nativeFunction()");
   }
}

and then,

// mcppfaq_delmgd.cpp
// compile with: /clr
#using <mscorlib.dll>
using namespace System;
using namespace System::Runtime::InteropServices;

__delegate void Del(String __gc* s);

public __gc class A
{
public:
   void delMember(String __gc* s)
   {
      Console::WriteLine(s);
   }
};

[DllImportAttribute("mcppfaq_delnative.dll", CharSet=CharSet::Ansi)]
extern "C" void nativeFunction(Del __gc* d);

int main()
{
   A __gc* a = new A;
   Del __gc* d = new Del(a, &A::delMember);
   nativeFunction(d); // Call to native function
}

Output

in nativeFunction()

Yes, if it's part of a multi-file, managed assembly. The assembly can be just a wrapper for the unmanaged DLL.

To see if an application was compiled as managed (/clr), see if _MANAGED or _M_CEE macro is set to 1.

#if (_MANAGED == 1) || (_M_CEE == 1)
// Managed code
#else
// Unmanaged code
#endif

For more information, see Predefined Macros.

// mcppfaq_isManaged.cpp
// compile with: /clr
// arguments: mcppfaq_delnative.dll
#using <mscorlib.dll>
using namespace System;
using namespace System::IO;
static bool isManaged(String __gc* sFilename)
{
   try
   {
      Byte Data __gc[] = new Byte __gc[4096];
      FileInfo __gc* file = new FileInfo(sFilename);
      Stream __gc* fin = file -> Open(FileMode::Open, FileAccess::Read);
      Int32 iRead = fin -> Read(Data, 0, 4096);
      fin -> Close();
      // Verify this is a executable/dll
      if ((Data[1] << 8 | Data[0]) != 0x5a4d)
         return false;
      // This will get the address for the WinNT header
      Int32 iWinNTHdr = Data[63]<<24 | Data[62]<<16 | Data[61] << 8 | Data[60];
      // Verify this is an NT address
      if ((Data[iWinNTHdr+3] << 24 | Data[iWinNTHdr+2] << 16 
                                   | Data[iWinNTHdr+1] << 8 
                                   | Data[iWinNTHdr]) != 0x00004550)
         return false;
      Int32 iLightningAddr = iWinNTHdr + 24 + 208;
      Int32 iSum=0;
      Int32 iTop = iLightningAddr + 8;
      for (int i = iLightningAddr; i < iTop; i++)
         iSum|=Data[i];
      if (iSum == 0)
         return false;
      else
         return true;
   }

   catch(Exception __gc* e)
   {
      throw (e);
   }
}

int main(int argc, char * argv[])
{
   System::Console::WriteLine(isManaged(argv[1]));
}

CSharpFunc()
{
   String s = "";   // Must initialize here
   S.ProvideString(ref s);
   System::Console::WriteLine(t);
}

You can avoid the initialization in the C# code by using an out parameter instead.

CSharpFunc()
{
   String s;   // default initialized to ""
   S.ProvideString(out s);
   System::Console::WriteLine(t);
}

The MC++ function can be written as follows

public __value struct S
{
   static void ProvideString([Out] String ** s) 
   {
      *s = S"a string";
   }
};

The argument of ProvideString needs to be a ByRef. The Out attribute parameter is a member of the System::Runtime::InteropServices namespace.

System::Runtime::InteropServices::GCHandle lets you hold a managed object reference in unmanaged memory. You use the GCHandle::Alloc method to create an opaque handle to a managed object and GCHandle::Free to release it. Also, the GCHandle::Target method allows you to obtain the object reference back from the handle in managed code.

// mcppfaq_gchandle.cpp
// compile with: /clr
#using < mscorlib.dll >
using namespace System;
using namespace System::Runtime::InteropServices;

#pragma managed
class StringWrapper
{
   int m_handle;
public:
   StringWrapper()
   {
      String* str = new String(S"ManagedString");
      m_handle = (GCHandle::op_Explicit(GCHandle::Alloc(str))).ToInt32();
   }

   ~StringWrapper()
   {
      (GCHandle::op_Explicit(m_handle)).Free();
   }

   // more code here...
   void PrintString()
   {
      String* targetStr = __try_cast< String* >((GCHandle::op_Explicit((IntPtr)m_handle)).Target);
      Console::WriteLine(S"StringWrapper::m_handle == {0}", targetStr);
   }
};

#pragma unmanaged
int main()
{
   StringWrapper s; 
   s.PrintString();
}

Output

StringWrapper::m_handle == ManagedString

gcroot overloads operator->, allowing you to use it directly without casting. It abstracts the details of calling IntPtr::ToInt32 or IntPtr::ToInt64, depending on which platform the executable was compiled for (Win32 or Win64).

// mcppfaq_gcroot2.cpp
// compile with: /clr
#using < mscorlib.dll >
#include < vcclr.h >

using namespace System;
using namespace System::Runtime::InteropServices;

#pragma managed
class StringWrapper
{
   gcroot< String* > m_handle;
   // int m_handle;
public:
   StringWrapper()
   {
      m_handle = new String(S"ManagedString");
   }

   ~StringWrapper() {}
   // more code here...
   void PrintString()
   {
      Console::WriteLine(S"StringWrapper::m_handle == {0}", m_handle);
   }
};

#pragma unmanaged
int main()
{
   StringWrapper s; 
   s.PrintString();
}

Output

StringWrapper::m_handle == ManagedString

Given an unmanaged C++ class:

// mcppfaq_wrapunmgclass.cpp
// compile with: /LD
#include < windows.h >
class UnmanagedClass
{
public:
   LPCWSTR GetPropertyA();
   void MethodB( LPCWSTR );
};

You can wrap this with Managed C++ as follows:

// mcppfaq_wrapunmgclass2.cpp
// compile with: /clr /LD
#include < windows.h >
#using < mscorlib.dll >
#using < System.dll >
#include < vcclr.h >

using namespace System;

class UnmanagedClass
{
public:
   LPCWSTR GetPropertyA(){return 0;}
   void MethodB( LPCWSTR ){}
};

public __gc class ManagedClass
{
public:
   ManagedClass() : m_Impl( new UnmanagedClass ) {}
   ~ManagedClass()
   {
      delete m_Impl;
   }

   __property String*  get_PropertyA()
   {
      return new String( m_Impl->GetPropertyA());
   }

   void MethodB( String* theString )
   {
      const WCHAR __pin*  str = PtrToStringChars( theString );
      m_Impl->MethodB( str );
   }
private:
   UnmanagedClass* m_Impl;
};

Use the System::Runtime::InteropServices::Marshal class.

// mcppfaq_convertnativechar.cpp
// compile with: /clr
#using < mscorlib.dll >
#include < string.h >
using namespace System;
using namespace System::Runtime::InteropServices;

int main()
{
   char buf[] = "Native String";
   int len = strlen(buf);
   
   Byte byteArray[] = new Byte[len + 2];

   // convert any native ptr to System::IntPtr by doing C-Style cast
   Marshal::Copy((IntPtr)buf,byteArray, 0, len);
}

Use the Marshal.GetObjectForNativeVariant method.

The following sample shows how to declare and use a __nogc array of native type inside __gc type:

// mcppfaq_nogc_array_in_gc_type.cpp
// compile with: /clr
#using <mscorlib.dll>
using namespace System;

class NativeClass
{
public:
   int i;
};

public __gc class ManagedClass
{
public:
   __property void set_arrayProp(int index, NativeClass nc)
   {
      NativeClass __pin* pnc = &(narray[index]);
      (*pnc) = nc;
   }
   __property NativeClass get_arrayProp(int index)
   {
      NativeClass __pin* pnc = &(narray[index]);
      return *pnc;
   }

private:
   NativeClass narray __nogc[10];
};

int main()
{
   NativeClass ncpp;
   ncpp.i = 100;
   ManagedClass* mcpp = new ManagedClass;
   mcpp->arrayProp[0] = ncpp;
 
  if( mcpp->arrayProp[0].i == 100 )
      Console::WriteLine(S"Pass");
   else
      Console::WriteLine(S"Fail");
}

Output

Pass

Use an array of strings.

String * arr[] = {S"Hit", S" the", S" pause", S" button", 
S" on", S" a", S" DVR", S" and", S" the", S" picture", 
S" freezes."};
Console::WriteLine(String::Concat(arr));

Use PtrToStringChars in vcclr.h.

// mcppfaq_string_to_wchart.cpp
// compile with: /clr
#include < stdio.h >
#include < stdlib.h >
#include < vcclr.h >
#using < mscorlib.dll >
using namespace System;

int main()
{
   String __gc* str = S"Hello";

   // Conversion to wchar_t* :
   const wchar_t __pin* p = PtrToStringChars(str);
   printf("%S\n", p);

   // Conversion to char* :
   // Convert wchar_t* to char* using a conversion functionssuch as: 
   // WideCharToMultiByte()
   // wcstombs()
   char* ch = (char *)malloc((str -> Length + 1) * 2);
   wcstombs(ch, p, (str -> Length + 1) * 2);
   printf("%s\n", ch);
}

Output

Hello
Hello

// mcppfaq_convertstr.cpp
// compile with: /clr /LD /EHsc
#using <mscorlib.dll>
#include <iostream>

void MarshalString ( System::String* s, std::string& os )
{
   using namespace System::Runtime::InteropServices;
   const char* chars = 
      (const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();
   os = chars;
   Marshal::FreeHGlobal(System::IntPtr((void*)chars));
}

void MarshalString ( System::String* s, std::wstring& os )
{
   using namespace System::Runtime::InteropServices;
   const wchar_t* chars = 
      (const wchar_t*)(Marshal::StringToHGlobalUni(s)).ToPointer();
   os = chars;
   Marshal::FreeHGlobal(System::IntPtr((void*)chars));
}

Use DllImportAttribute and MarshalAsAttribute.

// mcppfaq_nativelib.cpp
// compile with: /LD
#include < stdio.h >
#include < windows.h >

extern "C"
{
   __declspec(dllexport)
   int f(wchar_t* buff[])
   {
      printf("in f: %S\n", buff[0]);
      buff[0] = L"changed";
      return wcslen(buff[0]);
   }
};

and then,

// mcppfaq_usenativelib.cpp
// compile with: /clr
#using < mscorlib.dll >
using namespace System;
using namespace System::Runtime::InteropServices;

[DllImport("mcppfaq_nativelib.dll", CharSet=CharSet::Unicode)]
int f([In][Out][MarshalAsAttribute(UnmanagedType::LPArray, ArraySubType=UnmanagedType::LPWStr, SizeConst=2)] String* str[]);

int main()
{
   String* myarr[] = new String*[2];
   myarr[0] = S"one";
   myarr[1] = S"two";
   f(myarr);   // Call to imported native function.
   Console::WriteLine(S"in managed: {0}", myarr[0]);
}

In standard C++ applications, string literals are composed of ASCII characters. However, managed types and applications cannot handle standard ASCII strings. Managed Extensions for C++ solve this problem by allowing string literals to be assigned to a variable of type System::String without casting.

String *s1 = "a string literal";
String *s2 = L"a wide string literal";
Note   Regular C++ wide-character string literals (prefixed by L) and managed string literals (prefixed by S) can be used interchangeably where String types are expected. However, the reverse is not true; managed string literals cannot be used where C++ string types are expected.

Managed Extensions support a new type of string literal (prefixed by S) that has type String* and better performance than a C++ string literal.

String *s3 = S"a wide string literal";

The following example demonstrates creating and using a two-dimensional string array using Managed Extensions for C++. Although the example uses a two-dimensional string array, the information can also be applied to a single- or multidimensional string array.

Example

// gc_string_arrays.cpp
// compile with: /clr
// Creating and using a two-dimensional string array
#using <mscorlib.dll>
#include <tchar.h>
using namespace System;
int _tmain(void)
{
   Int32 nRows, nColumns;
   nRows = 10;
   nColumns = 10;
// Create an instance of an Array* class and set it to __typeof(String)
   Array* myStringArray1 = Array::CreateInstance(__typeof(String), 
                                                 nRows, nColumns);
// Initialize a new instance of a two-dimensional __gc 
// array with elements of a pointer to the String class
   String* myStringArray2 __gc[,]= new String* __gc[nRows,nColumns];
   String* myString = S"This is a test";
   myStringArray1->SetValue(myString, 0, 0);
   myStringArray2->SetValue(myString, 0, 0);
   Console::WriteLine(myStringArray1->GetValue(0,0));
   Console::WriteLine(myStringArray2->GetValue(0,0));
   return 0;
}

Output

This is a test
This is a test

For more information on this type, see 11.2 Runtime String Literals.

No, there is no notion of "friend" assemblies. Depending on your needs one alternative you might look at doing is a multimodule assembly using .netmodules (/clr:noAssembly).

You need to generate a strong name, for example, sn.exe -k keyfilename.snk. You can use the Visual Studio .NET Command Prompt to do this. It is in the Visual Studio .NET Tools program group.

Add the strong name key to your assembly by adding the following attribute on a class declaration in your source:

[assembly:AssemblyKeyFileAttribute("location\\keyfilename.snk")];

You can install the assembly you build into the Global Assembly Cache via the command gacutil -I myassembly. Running gacutil can be done automatically as a post-build step. You should then be able to add your component to the Toolbox.

See Producing Verifiable Components with Managed Extensions for C++.

No.

This shows that the preprocessor simply performs textual substitutions and disregards that MessageBox is part of the System::Windows::Forms namespace. The .NET Framework does not include a class called MessageBoxA and so the compiler generates an error. To resolve this, you can use #undef to prevent name conflicts, as shown below.

#include < windows.h >
#using < mscorlib.dll >
#using < System.Windows.Forms.dll >

#ifdef MessageBox
#undef MessageBox
#endif

int main()
{
   System::Windows::Forms::MessageBox::Show("Hello, World!");
}

// mcppfaq_convertguid.cpp
// compile with: /clr
#using <mscorlib.dll>
#include <windows.h>
#include <stdio.h>

using namespace System;

Guid FromGUID( _GUID& guid )
{
   return System::Guid( 
      guid.Data1, guid.Data2, guid.Data3, 
      guid.Data4[0],guid.Data4[1],guid.Data4[2],
      guid.Data4[3],guid.Data4[4],guid.Data4[5],
      guid.Data4[6],guid.Data4[7] );
}

_GUID ToGUID( System::Guid& guid )
{
   Byte guidData[] = guid.ToByteArray();
   Byte __pin* data = &(guidData[0]);
   return *(_GUID*)data;
}

int main()
{
   _GUID ng = {0x11111111, 0x2222, 0x3333, 0x44, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
   Guid mg;
   Console::WriteLine((mg = FromGUID(ng)).ToString());
   _GUID ng2 = ToGUID(mg);
   printf("%x-%x-%x-", ng2.Data1, ng2.Data2, ng2.Data3);
   for ( int i=0; i < 8; i++ )
   {
      if ( i == 2) printf("-");
      printf("%x", ng2.Data4[i]);
   }
}

Output

11111111-2222-3333-4455-555555555555
11111111-2222-3333-4455-555555555555

The Win32 GetLastError function returns unpredictable results when compiled with the /clr compiler option. Between calls of a Win32 function and a call to GetLastError, any number of Win32 API calls can be performed by the common language runtime. There is no way to guarantee that the value returned from GetLastError applies to the previously called Win32 function.

For example, in the following code, GetLastError is not likely to return the correct error code.

#include "wininet.h"
int _tmain(int argc, _TCHAR* argv[])
{
   GROUPID groupID;
   HANDLE hGroup = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, 
                                          0, 0, &groupID, 0);
   DWORD dwError = GetLastError();
   printf ("Error = %X\n", dwError);
}

This problem can be addressed by declaring the Win32 function with the DllImport attribute and explicitly setting its SetLastError parameter to true.

In the following code, the EntryPoint parameter of DllImport attribute is used to rename the Win32 function FindFirstUrlCacheGroup to MyWin32Function to avoid a compilation error, because FindFirstUrlCacheGroup is already declared in the wininet.h header file.

#include "wininet.h"
#using <mscorlib.dll>
using namespace System::Runtime::InteropServices;

[DllImport("wininet.dll", SetLastError=true, 
EntryPoint="FindFirstUrlCacheGroup")]
HANDLE MyWin32Function(DWORD dwFlags,
    DWORD dwFilter,
    LPVOID lpSearchCondition,
    DWORD dwSearchCondition,
    GROUPID* lpGroupId,
    LPVOID lpReserved
);

int _tmain(int argc, _TCHAR* argv[])
{
   GROUPID groupID;
   HANDLE hGroup = MyWin32Function(0, CACHEGROUP_SEARCH_ALL, 
0, 0, &groupID, 0);
   DWORD dwError = Marshal::GetLastWin32Error();
   printf ("Error = %X\n", dwError);
}

See Also

Managed Extensions for C++ Programming | Visual C++ .NET 2003 Frequently Asked Questions

Show:
© 2015 Microsoft