Calling Unmanaged Code: Part 1 - simple DLLImport

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

Environment: [.NET]

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

  • System.AccessViolationException : Invoke C dll method from C#

    Posted by Kanika Maheshwari on 02/02/2017 11:35am

    Thanks for this article. But I am facing some issue in my application - unhandled exception of type 'System.AccessViolationException' occurred in your application exe. Additional information: Attempted to read or write protected memory.. trying to run C Dll method from C#. Any ideas how can it be resolved or what could be the reason?

    Reply
  • Passing ptr and structure

    Posted by Legacy on 08/19/2003 12:00am

    Originally posted by: chhabi

    how to pass pointers and structure to the C++ dll from c#

    Reply
  • Can Unmanaged class from an DLL be exported?

    Posted by Legacy on 07/10/2003 12:00am

    Originally posted by: dnguyen

    What if i have an extension DLL created with a many exported
    classes and i want to be able to use this dll in my c# application. Is it possible? Some hints will be appreciated?
    Thanks

    Reply
  • can we pass CString reference to an unmanaged C++ dll function ?

    Posted by Legacy on 06/18/2003 12:00am

    Originally posted by: svkr

    Hi,

    Is it possible to pass a reference of CString object to
    a native C++ dll ?

    Please clarify.

    Reply
  • How to use functions from a C++ lib

    Posted by Legacy on 01/09/2003 12:00am

    Originally 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

    • calling vc++6.0 dll in C# its generated error....

      Posted by keyur4you on 03/27/2007 05:34am

      This 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

      Reply
    Reply
  • Using a com component in a C# aplication

    Posted by Legacy on 08/29/2002 12:00am

    Originally 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

    Reply
  • Example with GetUserName is wrong!!

    Posted by Legacy on 01/23/2002 12:00am

    Originally posted by: Bert Weber

    Hi,
    
    

    to call a function with a string as a out parameter and int pointer is wrong or better to complicated, there are better ways.

    [DllImport("advapi32.dll", EntryPoint="GetUserName", SetLastError=true)]
    public static extern bool GetUserName(StringBuilder szStrBuffer, ref int iLen);


    Usage:

    int iLen = 100;
    StringBuilder szStrBuffer = new StringBuilder(iLen);

    GetUserName(szStrBuffer, ref iLen);

    string szUser = szStrBuffer.ToString();


    For integer pointers which are used only es return parameters simply use the out keyword. It automaticly adds one level of indirection (int -> int*). There is no need to handle this as an array.

    Bert Weber

    Reply
  • Calling C++ DLL functions from VB.Net

    Posted by Legacy on 01/07/2002 12:00am

    Originally posted by: Jay

    I imagine it would also be relatively easy to call
    
    unmanaged code from a VB.Net application (the first of
    which I have yet to write). With respect to your
    Testdll.Dll, how and where would I rewrite:

    [DllImport("TestDll.dll", EntryPoint="sum",
    ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]
    static extern int sum(int a,int b);

    Thanks,
    Jay

    Reply
  • where is the part2 ?

    Posted by Legacy on 10/29/2001 12:00am

    Originally posted by: ashish ranjan

    where is the part2 ?

    Reply
  • Why write a DLL function to add two integers?

    Posted by Legacy on 09/24/2001 12:00am

    Originally posted by: Synn

    Why write a DLL function to add two integers? Use the + operator for this!

    j/k :)

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date