Search This Blog

Sunday, July 14, 2013

Interoperatablity between Managed and Unmanaged Code


1. Abstract:

            Code that executes under the control of the runtime is called managed code. Conversely, code that runs outside the runtime is called unmanaged code. COM components, ActiveX interfaces, and Win32 API functions are examples of unmanaged code.

2. The background of the post:

            I got an assignment to interface with a .net c# dll from my c++ unmanaged static library. Here I have to handle data from both managed and unmanaged processes and which inspired me to come up with a blog which unveils under the hood of Managed C++ programming or C++\CLI.

 3. Managed Vs. Unmanaged

            In this blog I will cover following topics in detail:

            1. What is C++\CLI ? 
            2. Managed C++ Programming
            3. PInvoke
            4. Marshaling 
            5. Interoperability between COM Streams and Managed Streams 

3.1 What is C++\CLI?

            CLI stands for Common Language Infrastructure. C++/CLI is the means to program .NET in C++, similarly like C# or VB.NET are used.

3.1.1 Using .Net types in Managed C++ 

 

     3.1.1.1 Using Pointers and references

            As you know, in C++, we have the "*" to denote a pointer, and in C++/CLI, we have the ^ to denote a handle (reference/pointer). Now, a "*" designates a native pointer which resides on the CRT heap, whereas the handles designate "safe pointers" and reside on the managed heap. The handles can be thought of as references, and unlike native pointers, they won't cause memory leaks if they are not properly deleted, since the GC will take care of that.

            System::Object ^ x = gcnew System::Object();

In addition to the punctuator "^" symbol, we have the percent "%" which stands for a tracking reference;
                     N* pn = new N; // allocate on native heap
                    N& rn = *pn; // bind ordinary reference to native object
                   R^ hr = gcnew R; // allocate on CLI heap
                 R% rr = *hr; // bind tracking reference to gc-lvalue

In general, the punctuator % is to ^ as the punctuator & is to *.

3.1.1.2 Arrays 

             Array in managed heap is as below:settings

             cli::array<int> ^a = gcnew cli::array<int> {1, 2, 3}; 

3.1.1.3. Managed Classes

             To indicate that a class is managed you have to prefix the declaration with the "__gc" modifier. This modifier can be used with classes, struts and variables that are explicitly marked to be managed by the garbage collector.

#using <mscorlib.dll> // to use Console::WriteLine
#include <stdio.h> // to printf()
using namespace System; 
// Mark unmanaged code
#pragma unmanaged
void print(char *msg)
{
    printf("%s\n", msg);
} 
// Switch back to managed code
#pragma managed
int main()
{
// Write to the console through managed call
Console::WriteLine(S"Hello world from managed method");
// use stdio to write to console
print("hello world from unmanaged method");
return 0;
}


3.1.1.4 Pinning Pointers

           Managed pointers are managed and tracked by the garbage collector. When you want to pass a gc pointer to a nongc method, you need a way to tell the garbage collector not to move or discard that object. In all cases you cannot pass a gc pointer to a non gc method directly.When you pin a managed object you may pass the pinned pointer to the unmanaged method.Pinned objects will increment the object reference count and will inform the garbage collector so that it doesn't move the object in memory.

#using <mscorlib.dll>
#include <stdio.h>

using namespace System;
// just a simple unmanged method
#pragma unmanaged
void print_msg(char *s)
{ printf("printf() -> %s\n", s); }
#pragma managed
int main()
{
// Construct a byte array
Byte arr[] = new Byte[20];
// fill the array with: Hello
arr[0] = 'h'; arr[1] = 'e'; arr[2] = 'l'; arr[3] = 'l'; arr[4] = 'o'; arr[5] = 0;
// Pin the pointer to the first element
unsigned char __pin *str = &arr[0]; // Call the managed method print_msg((char *)str);
return 0;
} 

3.2 Marshaling

           Interop marshaling governs how data is passed in method arguments and return values between managed and unmanaged memory during calls. Interop marshaling is a run-time activity performed by the common language runtime's marshaling service.

Marshal Class Provides a collection of methods for allocating unmanaged memory, copying unmanaged memory blocks, and converting managed to unmanaged types, as well as other miscellaneous methods used when interacting with unmanaged code.

3.3 Platform Invoke (PInvoke)

           PInvoke is a short name for Platform Invoke; it enables managed code to call C-style functions in native DLLs.

This is an important and unique feature of Managed Extensions for C++ is that you can use unmanaged APIs directly. If you do not require customized data marshaling, you do not need to use PInvoke.

Platform invoke relies on metadata to locate exported functions and marshal their arguments at run time. The following illustration shows this process.

A platform invoke call to an unmanaged DLL function




When platform invoke calls an unmanaged function, it performs the following sequence of actions:
Locates the DLL containing the function.
Loads the DLL into memory.
Locates the address of the function in memory and pushes its arguments onto the stack, marshaling data as required.

Locating and loading the DLL, and locating the address of the function in memory occur only on the first call to the function.

Transfers control to the unmanaged function.

Platform invoke throws exceptions generated by the unmanaged function to the managed caller.

To call functions exported from an unmanaged library, a .NET Framework application requires a function prototype in managed code that represents the unmanaged function. To create a prototype that enables platform invoke to marshal data correctly, you must do the following:
  • · Apply the DLLImportAttribute attribute to the static function or method in managed code.
  • · Substitute managed data types for unmanaged data types.
http://msdn.microsoft.com/en-us/library/ac7ay120.aspx - Lists the mapping between managed and unmanaged types.
                  a). MarshalAsAttribute to specify the marshaling for parameters passing during PInvoke specifically. 
                 b). Many unmanaged functions expect you to pass structure as a parameter to the function, in such cases we need to define structures in managed code and provide necessary marshaling(if needed). During PInvoke those structures will be automatically converted to unmanaged.

[C#] Use the DllImportAttribute to identify the DLL and function. Mark the method with the static and extern modifiers.
[C++] Use the DllImportAttribute to identify the DLL and function. Mark the wrapper method or function with extern "C".

C# using System.Runtime.InteropServices;

public class Win32 {
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr MessageBox(int hWnd, String text,
String caption, uint type);
}

public class HelloWorld {
public static void Main() {
Win32.MessageBox(0, "Hello World", "Platform Invoke Sample", 0);
}
}

using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public struct Point {
public int x;
public int y;
}

[StructLayout(LayoutKind.Explicit)]
public struct Rect {
[FieldOffset(0)] public int left;
[FieldOffset(4)] public int top;
[FieldOffset(8)] public int right;
[FieldOffset(12)] public int bottom;
}

class Win32API {
[DllImport("User32.dll")]
public static extern bool PtInRect(ref Rect r, Point p);
}

3.4. Example on handling Managed IO Stream in Unmanaged Code.

Below shows the code snippet in the managed c++ wrapper class to receive and convert the System.IO.Stream

1. //Copying Managed stream data of length 'cb' to managed byte array                            
      array<byte>^ managedBuffer;
      managedBuffer = gcnew array<byte>((int)cb);
      bytesRead = ioStream->Read(managedBuffer,0,(int)cb);
               
2. //Copies data from a managed array to an unmanaged memory pointer
     if( bytesRead > 0 )
     {  
         byte* pv = new byte[bytesRead];
                   Marshal::Copy(managedBuffer,0,(IntPtr)pv,(int)(bytesRead)); }
                  delete[] managedBuffer;
             }
3. Now you can use the above unmanaged byte array in your unmanaged code for further processing
           a) You can write to a file and can get the FileStream 
           b) You can get the COM IStream from the byte array using ( SHCreateMemStream or CreateStreamOnGlobal).

4. Conclusion

      1. If you want to access managed library in unmanaged process , then
          a). Create a managed c++ library to access the managed data
                      i. In project settings allow to use common language runtime
                      ii. We need to put: "#using <mscorlib.dll>" in order to start using the .NET Framework.
          b). Marshal to unmanaged memory space.

     2. If you want to access unmanaged apis in managed space, then make use of PInvoke to marshal and call unmanaged APIs directly.

5. References

Friday, March 29, 2013

Using Windows Performance Monitor

In this blog I will explain how we can use Performance monitor to assess the performance of a particular process in memory.
 
Windows Performance Monitor is a Microsoft Management Console (MMC) snap-in that provides tools for analyzing system performance. WPM performs performance data collection for specified processes and log them using data collector sets.
  • To launch Windows Performance Monitor

Click Start , click in the Start Search box, type perfmon , and press ENTER
  • Data Collector Set

A Data Collector Set is the building block of performance monitoring and reporting in Windows Performance Monitor. It organizes multiple data collection points into a single component that can be used to review or log performance.
Data Collector Sets can contain the following types of data collectors:
1. Performance counters
2. Event trace data
3. System configuration information (registry key values)
  • Steps to create data collector with Performance Counters:

Following screen shots explains how to create a data collector and assess performance. Create a new data collector set as below:





Click next and select the necessary performance counters to monitor


Select Process counter and it will list all the available processes in the below box. I have selected Notepad process to monitor. Also the select the counters from the process. Then click add to add the selected counters.


Now I have created data collector for Notepad process and which will collect all process counters and will log into a file for future reference.
Click start to start collecting performance data as below:

  • To view the data collector log file

Click on reports and which will list the log file created by above steps.
    • Click General tab to adjust sampling interval and the time range to show in the graph



Here the bottom pane will list the counters for which the data has been collected. The graph will display the values for the selected counters.
Also you can access the graph properties by right clicking the graph and then select properties.
    • Select source tab to select the data source. You can access from log file or live data by selecting ‘Current activity’
    • Select Data tab to add or remove counters and also to color each counter. Here you can adjust the scale for each counter display.
Eg: Your memory counter value is in the range 300000 to 400000…, then you can select the scale as 0.001 to show the values in the range 300 to 400.


    • Select Graph tab and update the vertical axes ranges as below: