Calling Unmanaged Code: Part 1 - simple DLLImport
The managed world is beautiful. I have all classes I could want in FrameWork. What happens if I want to call some unmanaged code--such as existing C++ code? For instance, I have DLL written in C++, and want use it from C#.
Let's look some code. I have a DLL that exports a function in CDecl convention, that sums two integers:
extern "C" __declspec(dllexport) __cdecl
int sum(int a,int b);
And, of course, I want to reuse this code in C#. There is no "direct" way to call unmanaged code, so you must inform the compiler about it. More specificially, you must tell the compiler about what you want to call, how you want it called, and where its needed code is located:
[DllImport("TestDll.dll", EntryPoint="sum",
ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]
static extern int sum(int a,int b);
Once you've done this, you can then call it like any other normal C# function:
x=5;
y=7;
z=sum(x,y); // x will receive 12
Here is full C# client code - tested for Beta2:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
namespace WindowsApplication6
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox textBox3;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after
// InitializeComponent call
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.textBox2 = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.textBox3 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(64, 192);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(144, 64);
this.button1.TabIndex = 0;
this.button1.Text = "call sum";
this.button1.Click
+= new System.EventHandler(this.button1_Click);
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(40, 120);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(72, 22);
this.textBox1.TabIndex = 1;
this.textBox1.Text = "2";
//
// label1
//
this.label1.Location = new System.Drawing.Point(128, 128);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(16, 16);
this.label1.TabIndex = 2;
this.label1.Text = "+";
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(152, 120);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(56, 22);
this.textBox2.TabIndex = 3;
this.textBox2.Text = "3";
//
// label2
//
this.label2.Location = new System.Drawing.Point(224, 120);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(24, 23);
this.label2.TabIndex = 4;
this.label2.Text = "=";
//
// textBox3
//
this.textBox3.Location = new System.Drawing.Point(248, 120);
this.textBox3.Name = "textBox3";
this.textBox3.Size = new System.Drawing.Size(112, 22);
this.textBox3.TabIndex = 5;
this.textBox3.Text = "5";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
this.ClientSize = new System.Drawing.Size(576, 322);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.textBox3,
this.label2,
this.textBox2,
this.label1,
this.textBox1,
this.button1});
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
#region My Code
#region Dll Imports
[DllImport("TestDll.dll", EntryPoint="sum",
ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]
static extern int sum(int a,int b);
#endregion
#region Button Click Events
private void button1_Click(object sender, System.EventArgs e)
{
textBox3.Text=(int.Parse(textBox1.Text)+
int.Parse(textBox2.Text)).ToString();
}
#endregion
#endregion
}
}
I defined 3 textboxes, where textBox1 and textBox2 are the two operands, and where textBox3 is result. The button1 calls sum() and updates the result. The sum() method is defined as "static extern", which means it is an extern linked functions. It can't be put outside of class, becouse in C# there are no "alone" functions--everyone must belong to a class.
The calling convention is CDecl, because the C++ function is compiled with the __cdecl attribute. ExactSpelling=false tells the compiler to try "decore" the function name with "W" for Unicode or "A" for ANSI.
The button1 Click event parses two arguments from string to int, call to sum() and puts result as string.
Pay attention, you can use normal C++ declaration, without <extern "C">:
/*extern "C"*/ __declspec(dllexport)
__cdecl int sum(int a,int b);
But in such case you must inform compiler about the true, or "decorated" function name.
This can be done by using the EntryPoint field of DllImport attribute:
[DllImport("TestDll.dll", EntryPoint="sum",
ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]
static extern int sum(int a,int b);
It sounds very simple, becouse "int" is an isomorphic type, which means an int in C# and an int C++ are identical. What do you do when you want to operate on non-isomorhic types, such as String? Recall, that .NET string is a Class, while a C++ string is a char*, wchar_t*, or BSTR. String may be embedded in a structure, or pointed by pointer, or even something more exotic. Let's call some string function.
[DllImport("Advapi32.dll",
EntryPoint="GetUserName",
ExactSpelling=false,
SetLastError=true)]
static extern bool GetUserName(
[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,
[MarshalAs(UnmanagedType.LPArray)] Int32[] nSize );
This function receives two parameters: char* and int*. Because you must allocate char* buffer and receive a string by pointer, you can't use the UnmanagedType.LPStr attribute. So, you pass the ANSI string as a byte array. An int* is simpler--it's 1-element Int32 array. Let's call it:
private void button2_Click(object sender,
System.EventArgs e)
{
byte[] str=new byte[20];
Int32[] len=new Int32[1];
len[0]=20;
GetUserName(str,len);
MessageBox.Show(System.Text.Encoding.ASCII.GetString(str));
}
This allocates 20 bytes for receiving the ANSI string, one element in Int32 array, set 20 as max string length and call it. For receiving the string from the byte array I used Text.Encoding.ASCII class.
That's enough for the first part. The second part will speak about more complex interop.
Downloads
None

Comments
Passing ptr and structure
Posted by Legacy on 08/19/2003 12:00amOriginally posted by: chhabi
how to pass pointers and structure to the C++ dll from c#
ReplyCan Unmanaged class from an DLL be exported?
Posted by Legacy on 07/10/2003 12:00amOriginally posted by: dnguyen
What if i have an extension DLL created with a many exported
Replyclasses and i want to be able to use this dll in my c# application. Is it possible? Some hints will be appreciated?
Thanks
can we pass CString reference to an unmanaged C++ dll function ?
Posted by Legacy on 06/18/2003 12:00amOriginally posted by: svkr
Hi,
Is it possible to pass a reference of CString object to
a native C++ dll ?
Please clarify.
ReplyHow to use functions from a C++ lib
Posted by Legacy on 01/09/2003 12:00amOriginally posted by: OS
This article is very nice and helpful... but what if I have lib files from VC++ 6.0. Any ideas??
Thanks in Advance
-OS
-
Replycalling vc++6.0 dll in C# its generated error....
Posted by keyur4you on 03/27/2007 05:34amThis article is very useful.. but when i have calli my vc++6.0 dll generate under given error?? An unhandled exception of type 'System.AccessViolationException' occurred in your application exe. Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. u have any ideas to solve this error??? Thanks regareds keyur
ReplyUsing a com component in a C# aplication
Posted by Legacy on 08/29/2002 12:00amOriginally posted by: Manoj Sharma
Hello,
supposing this is a com dll method on some interface(in mathcom.tlb):
[id(1), helpstring("method Add")]
HRESULT Add([in] int a, [in]int b, [out]int *c);
Now I want to use it in a C# application.
Steps are:
1. Make Metadata by executing the folowing line on the dos prompt:
tlbimp mathcom.tlb /out:mathcommetadata.dll
2. create a C# console application in MS .Net and do as follows:
using System;
using System.Reflection ;
using System.Resources ;
using MATHCOMib ;
...Main(...)
{
CMath obj = new CMath();
int a = 100, b = 200, c = 0;
c = obj.Add(a, b);
Console.WriteLine(a + ":" + b + ":" + c);
}
That is it. But it does not work and cribs saying that MATHCOMLib is an unknown namespace. Pl suggest solutions.
Regards,
MKS
ReplyExample with GetUserName is wrong!!
Posted by Legacy on 01/23/2002 12:00amOriginally posted by: Bert Weber
ReplyCalling C++ DLL functions from VB.Net
Posted by Legacy on 01/07/2002 12:00amOriginally posted by: Jay
Replywhere is the part2 ?
Posted by Legacy on 10/29/2001 12:00amOriginally posted by: ashish ranjan
where is the part2 ?
ReplyWhy write a DLL function to add two integers?
Posted by Legacy on 09/24/2001 12:00amOriginally posted by: Synn
Why write a DLL function to add two integers? Use the + operator for this!
j/k :)
Replyobject access from c#
Posted by Legacy on 09/05/2001 12:00amOriginally posted by: stephen barrett
thank you a concise article - this style is preferable to the drawn out discussions I usually come across.
A quick question: Can one instantiate unmanaged objects from c#? I beleive the only way is via managed c++ but it would be useful if you could confirm or persuade to the contrary?
regards,
Stephen.
ReplyLoading, Please Wait ...