You are on page 1of 16

Software Engineering in Robotics

Interfacing to external functions

Henrik I. Christensen hic@cc.gatech.edu

Introduction

Motivation take home messages


What is managed code?
Interfacing to managed code

Direct DLL interfacing


Accessing external libraries

Summary

C# Process Model

The MSIL provides facilities for interoperability


across languages
Code that is compiled to run through the
Common Language Runtime is considered
managed
CLR support heap management, memory,
security,
The main limitation is lack of access to
pointers,

Managed vs Unmanaged Code

Managed code provide security and a harness


to handling exception handling etc.
This is achieved through strong typing and
type checking
The CLR manages the heap, and does full
checking
This can be at the cost of temporal jitter and
inability to manage memory directly

Sometimes it make sense to do direct


management or to control timing explicitly. I.e. the
programmer takes over responsibility for security,
memory management
Such code is term unsafe or unmanaged

Why use unmanaged code?

Sometimes valuable libraries are available


that you want to leverage without a complete
rewrite.
Sometimes you want direct access to a lowlevel API
Sometimes you need to write a driver with
direct management of low level details
Example is for example the OpenCV C++
library
Or access to a hardware interface for a robot
controller

Accessing DLL functions

Exposing DLL functions to C# runtime

using System;
using System.Runtime.InteropServices;
class VirtualMemoryManager {
[DllImport(kernel32.dll,EntryPoint=GetCurrentProcess)
]
internal static extern IntPtr
GetCurrentProcessHandle();
}

The method must be static


The Win32 API has inconsistent error handling, so be
aware

Guidelines to code design

Made sure the API is not already exposed


Define API external methods as private or
internal
Provide public wrappers around external code
to handle data conversion and error handling
Use enums and consts to make API values
Wrap resources such as handles into classes
that derive from
System.Runtime.InteropServices.SafeHandle
to ensure handling of pointers
Map input/outputs to ref parameters to avoid
relying on pointers

Doing unsafe code

Sometimes you need unsafe code (say drivers)


You can designate a code block to be unsafe

class Program {
unsafe static int Main( string[] args ){
//
}
}
Or
class Program {
static int Main( string[] args ){
//
unsafe{
//
}
//
}
}

Need to compile with the switch /unsafe i.e. csc.exe /unsafe ex.cs

Pointers

You can then do regular pointer management


as in C++, C,

You can only generate pointers to unmanaged


types

byte* pData;

I.e. you cannot do


string* pMessage

Memory can move around in a dynamic


environment

Fixed keyword

One can fixdata for a certain amount of


period

byte[] bytes = new byte[12];


fixed ( byte* pData = &bytes[0] ) {
// process your data with worry of moving data
// .
} // and now it may moved around again

Alternatively you can allocate data on the


stack

byte* bytes = stackalloc byte[42];

Think about performance

Switch to COM code 50 cycle overhead


Switch to unmanaged code 10 cycle
overhead
Guidelines for code

Made interface chunky and not chatty


Avoid ANSI/Unicode conversions
For high-performance scenarios, declaring
parameters and fields as IntPtr can boost
performance
Use SetLastError=false on platform invoke
signatures only if you will call
Marshal.GetLastWin32Error afterwards
If, and only if, unmanaged calls are exposed in a
non-exploitable fashion, use

Example for interface to Matlab

using
using
using
using
using

System;
System.Collections.Generic;
System.Windows.Forms;
EngMATLib;
System.Runtime.InteropServices;

namespace Mat_Envelope {
static class Program {
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern ushort GlobalAddAtom(string lpString);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern ushort GlobalFindAtom(string lpString);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern ushort GlobalDeleteAtom(ushort atom);
[STAThread]
static void Main() {
string atomStr = Application.ProductName + Application.ProductVersion;
ushort Atom = GlobalFindAtom(atomStr);
if (Atom > 0) {
MessageBox.Show("You are trying to open second instance of the App!");
} else {
// The Atom does not exist. Now we need to add it:
Atom = GlobalAddAtom(atomStr);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MatEnvelope());
GlobalDeleteAtom(Atom);
}
}
}
}
You can get the interface from MATHWORKS File sharing for running matlab from a C# program

Another example

// PInvokeTest.cs (use puts from msvcrt for output)


using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest {
[DllImport("msvcrt.dll")]
public static extern int puts(string c);
[DllImport("msvcrt.dll")]
internal static extern int _flushall();
public static void Main() {
puts("Test");
_flushall();
}
}

Same example with marshalling

// Marshal.cs
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest {
[DllImport("msvcrt.dll")]
public static extern int puts(
[MarshalAs(UnmanagedType.LPStr)] string m);
[DllImport("msvcrt.dll")]
internal static extern int_flushall();
public static void Main() {
puts("Hello World!");
_flushall();
}
}

Summary

Consider use of external libraries.


Do you need to make the switch to mixed
models?
There are a few different models
Provide good wrappers to isolate risk
Think about recommendations to maintain
performance
Doing unmanaged code is frequent in robotics
as you need to interface to providers code

A good chunky interface will allow you to do this


efficiently

Acknowledgement

This series of lectures has been developed


with generous support from the Microsoft
Corporation as part of the project Software
Engineering in Robotics Contract # 113873.
The support is gratefully acknowledged.

You might also like