Using Unmanage Code in C#

Environment: C#, VC++, .NET

The first thing you should understanind is why you would want to use unmanaged code. There are possibly two reasons to call unmanaged code:

  1. You want to reuse your code which is already written in an unmanaged environment e.g. VC 6.0
  2. You want to perform some low level work. ( i.e. need in line assembly in your program)

How to call unmanaged code

The first time I saw this topic was in Tom Archer's "Inside C#" which explains how to call an unmanaged DLL from C#:

// Sample program to call unmanaged code
using System;
using System.Runtime.InteropServices;

class PInvoke1App
{
    [DllImport("user32.dll")]
    static extern int MessageBoxA(int hWnd, 
                                  string strMsg, 
                                  string strCaption,
                                  int iType);

    public static void Main() 
    {
        MessageBoxA(0, 
                    "Hello, World!", 
                    "This is called from a C# app!",
                    0);
    }
}

I then tried to make my own DLL and call that DLL from my Application:

// Dll1.cpp
// Written by Zeeshan Amjad

#include 

BOOL __stdcall DllMain(HINSTANCE hInst, 
                       DWORD dwReason, 
                       LPVOID lpReserved) {
   return TRUE;
}

__declspec(dllexport) void __stdcall Message(char* p_szMessage)
{
   MessageBox(NULL, p_szMessage, "Message from DLL", MB_OK);
}

The following is my C# program:

// Native2.cs
// Written by Zeeshan Amjad

using System;
using System.Runtime.InteropServices;

class MainClass {
   [DllImport("Dll1.dll")]
   static extern void Message(string msg);

   public static void Main() {
      Message("Hello world");
   }
};

Now I run my program which crash because it can not find DLL1.dll in either the current directory or in the path. This causes a DllNotFoundException exception to be thrown. To handle this I have to catch this exception too. So I change my program a little bit as shown:

// Native3.cs
// Written by Zeeshan Amjad

using System;
using System.Runtime.InteropServices;

class MainClass {
   [DllImport("Dll1.dll")]
   static extern void Message(string msg);
 
   public static void Main() {
      try {
         Message("Hello world");
      }
      catch(DllNotFoundException e) {
         Console.WriteLine(e.ToString());
      }
   }
};

I also copy the DLL1.dll in the current folder to avoid this exception

Now again my program crash when I try to run it. This time it throws EntryPointNotFoundException. I should also catch this and display the error message of this exception rather than allowing the program to crash. Following is the new version of the program:

// Native4.cs
// Written by Zeeshan Amjad

using System;
using System.Runtime.InteropServices;

class MainClass {
   [DllImport("Dll1.dll")]
   static extern void Message(string msg);

   public static void Main() {
      try {
         Message("Hello world");
      }
      catch(DllNotFoundException e) {
         Console.WriteLine(e.ToString());
      }
      catch(EntryPointNotFoundException e) {
         Console.WriteLine(e.ToString());
      }
   }
};

This program now give the following error message:

System.EntryPointNotFoundException: Unable to find an 
entry point named Message in DLL Dll1.dll.
   at MainClass.Message(String msg)
   at MainClass.Main()

The problem is not in the C# program. In fact when you write the function in C++, the C++ compiler decorates the function name to get function overloading. The function which exports by DLL is not Message. To get the exact name type dumpbin -exports dll1.dll at the command prompt. Part of output of this utility is

    ordinal hint RVA      name
          1    0 00001005 ?Message@@YGXPAD@Z

There isn't any standard way of decorating the function name. So you need to tell the C++ compiler to not decorate function name. Following is the revised version of DLL code:

// Dll1.cpp
// Written by Zeeshan Amjad

#include <windows.h>

BOOL __stdcall DllMain(HINSTANCE hInst, 
                       DWORD dwReason, 
                       LPVOID lpReserved) {
   return TRUE;
}

extern "C" __declspec(dllexport) void __stdcall Message(char* p_szMessage)
{
   MessageBox(NULL, p_szMessage, "Message from DLL", MB_OK);
}

extern "C" is used to tell the compiler not to decorate the function name. Now when you see the function name from the output of the dumpbin utility, it will look like this:

    ordinal hint RVA      name
          1    0 0000100A _Message@4

Here @ shows that the function uses standard calling conventions and 4 shows the number of bytes push on the stack for parameters. In 32 bit environment like windows 9x and NT/2000 the address is stored in 32 bit i.e. 4 bytes. It means there is only one parameter in the stack. In other words this function take only one parameter.

Now the above C# program works fine without any change and display a message box with the text "Hello world" and the caption of "Message from DLL"

Let's do experiment with inline assembly in a DLL. I cannot call assembly language from C# but I know I can call an unmanaged DLL from C#. I made a DLL that calculates the speed of the CPU as well as determines the vendor name, Family, Model and Stepping of CPU using inline assembly language.

// SysInfo.cpp
// written by Zeeshan Amjad

#include "SysInfo.h"

BOOL __stdcall DllMain(HINSTANCE hInst, 
                       DWORD dwReason, 
                       LPVOID lpReserved) {
  return  TRUE;
}

extern "C" __declspec(dllexport) int __stdcall getCPUSpeed() {
  LARGE_INTEGER ulFreq, ulTicks, ulValue, 
                ulStartCounter, ulEAX_EDX, ulResult;

  // it is number of ticks per seconds
  QueryPerformanceFrequency(&ulFreq);

  // current valueofthe performance counter
  QueryPerformanceCounter(&ulTicks);

  // calculate one second interval
  ulValue.QuadPart = ulTicks.QuadPart + ulFreq.QuadPart;

  // read time stamp counter
  // this asm instruction load the highorder 32 bit of the register into EDX
  // and the lower order 32 bits into EAX
  _asm {
     rdtsc
     mov ulEAX_EDX.LowPart, EAX
     mov ulEAX_EDX.HighPart, EDX
  }

  // start no of ticks
  ulStartCounter.QuadPart = ulEAX_EDX.QuadPart;

  // loop for 1 second
  do {
     QueryPerformanceCounter(&ulTicks);
  } while (ulTicks.QuadPart <= ulValue.QuadPart);

  // get the actual no of ticks
  _asm {
     rdtsc
     mov ulEAX_EDX.LowPart, EAX
     mov ulEAX_EDX.HighPart, EDX
  }

  // calculate result
  ulResult.QuadPart = ulEAX_EDX.QuadPart - ulStartCounter.QuadPart;

  return (int)ulResult.QuadPart / 1000000;
}

extern "C" __declspec(dllexport) char* __stdcall getCPUType() {
  static char pszCPUType[13];
  memset(pszCPUType, 0, 13);

  _asm {
     mov eax, 0
     cpuid

     // getting information from EBX
     mov pszCPUType[0], bl
     mov pszCPUType[1], bh

     ror  ebx, 16
     mov pszCPUType[2], bl
     mov pszCPUType[3], bh

     // getting information from EDX
     mov pszCPUType[4], dl
     mov pszCPUType[5], dh

     ror  edx, 16
     mov pszCPUType[6], dl
     mov pszCPUType[7], dh

     // getting information from ECX
     mov pszCPUType[8], cl
     mov pszCPUType[9], ch

     ror  ecx, 16
     mov pszCPUType[10], cl
     mov pszCPUType[11], ch
  }

  pszCPUType[12] = '\0';

  return pszCPUType;
}

extern "C" __declspec(dllexport) int __stdcall getCPUFamily() {
  int retVal;

  _asm {
     mov eax, 1
     cpuid
     mov retVal, eax
  }

  return (retVal >> 8);
}

extern "C" __declspec(dllexport) int __stdcall getCPUModel() {
  int retVal;

  _asm {
     mov eax, 1
     cpuid
     mov retVal, eax
  }

  return ((retVal >> 4 ) & 0x0000000f);
}

extern "C" __declspec(dllexport) int __stdcall getCPUStepping() {
  int retVal;

  _asm {
     mov eax, 1
     cpuid
     mov retVal, eax
  }

  return (retVal & 0x0000000f);
}

Here is a simple client of this DLL which is written in VC to check the functionality:

// Client1.cpp
// Written by Zeeshan Amjad

#include <iostream.h>
#include "SysInfo.h"

#pragma comment(lib, "SysInfo.lib")

int main() {
  cout << "CPU Speed = " << getCPUSpeed() << endl;
  cout << "CPU Type = " << getCPUType() << endl;
  cout << "CPU Family = " << getCPUFamily() << endl;
  cout << "CPU Model = " << getCPUModel() << endl;
  cout << "CPU Stepping = " << getCPUStepping() << endl;
  return 0;
}

Now the same client in C#:

// Native5.cs
// Written by Zeeshan Amjad

using System;
using System.Runtime.InteropServices;

class MainClass {
  [DllImport("SysInfo.dll")]
  static extern int getCPUSpeed();

  [DllImport("SysInfo.dll")]
  static extern string getCPUType();
  
  [DllImport("SysInfo.dll")]
  static extern int getCPUFamily();
  
  [DllImport("SysInfo.dll")]
  static extern int getCPUModel();

  [DllImport("SysInfo.dll")]
  static extern int getCPUStepping();
  
  // main program
  public static void Main() {

     // get CPU Speed
     try {
        int iCPUSpeed = getCPUSpeed();
        Console.WriteLine("CPU Speed = {0}", iCPUSpeed.ToString());
     }
     catch (DllNotFoundException e) {
        Console.WriteLine(e.ToString());
     }
     catch (EntryPointNotFoundException e) {
        Console.WriteLine(e.ToString());
     }
     
     // get CPU Type
     try {
        string strType = getCPUType();
        Console.WriteLine("CPU Type = {0}", strType);
     }
     catch (DllNotFoundException e) {
        Console.WriteLine(e.ToString());
     }
     catch (EntryPointNotFoundException e) {
        Console.WriteLine(e.ToString());
     }
 
     // get CPU Family
     try {
        int iFamily = getCPUFamily();
        Console.WriteLine("CPU Family = {0}", iFamily.ToString());
     }
     catch (DllNotFoundException e) {
        Console.WriteLine(e.ToString());
     }
     catch (EntryPointNotFoundException e) {
        Console.WriteLine(e.ToString());
     }
     
     // get CPU Model
     try {
        int iModel = getCPUModel();
        Console.WriteLine("CPU Model = {0}", iModel.ToString());
     }
     catch (DllNotFoundException e) {
        Console.WriteLine(e.ToString());
     }
     catch (EntryPointNotFoundException e) {
        Console.WriteLine(e.ToString());
     }

     // get CPU Stepping
     try {
        int iStepping = getCPUStepping();
        Console.WriteLine("CPU Stepping = {0}", 
                          iStepping.ToString());
     }
     catch (DllNotFoundException e) {
        Console.WriteLine(e.ToString());
     }
     catch (EntryPointNotFoundException e) {
        Console.WriteLine(e.ToString());
     }
  }
};

I created a GUI for this program. Its output is:

Downloads

Download source - 5 Kb


About the Author

Zeeshan Amjad

C++ Developer at Bechtel Corporation. zamjad.wordpress.com

Comments

  • Interesting

    Posted by snareenactina on 12/30/2012 12:53pm

    But that doesn't have to be you. jailhouse These sites provide links to other sites with a range of economic data. summerearly familial theoriginal wute aroung

    Reply
  • Great work

    Posted by pavan on 06/12/2012 07:33am

    Tks for very clear explanation.

    Reply
  • Using def file does same things as extern "C"

    Posted by voronov on 07/16/2009 03:13pm

    I found that creating def file for DLL entries also allows normal names to be exported. Very useful article, thank you!

    Reply
  • Multi-cpu / Multi-core?

    Posted by Your_Persona on 11/27/2006 01:36pm

    How would you add support to dertmine the processor and core count, and the frequency of each? Also could you add total/available system memory?

    Reply
  • How to pass the string reference from C# to unmanaged code

    Posted by Legacy on 01/20/2004 12:00am

    Originally posted by: JC Lee

    Great work! very clear! I had problem is how to pass the reference of a string to the unmanaged code. I need the string back from the unmanaged code. Thanks in advanced.
    

    Reply
  • Calling a Native/UnManaged DLL from C#/Managed code

    Posted by Legacy on 12/05/2003 12:00am

    Originally posted by: Satya Dev Bulusu

    Thanks for the informative one. I really appreciate you for presenting tech. problems and solutions onto Internet for sharing. Keep doing it. My best wishes to you.
    
    

    Satya

    Reply
  • How do we specify reference paths(additional directory paths)?

    Posted by Legacy on 04/15/2003 12:00am

    Originally posted by: sameer sudame

    Hello,

    Many thanks for extern "C" funda. I am not able to call unmanaged API from my dll. But i still have to copy it to C# project directory.

    Is there any way of addition c dll path to c# project to look for dll there?

    Reply
  • Unable to find an Entry point of DLL (vb.net)

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

    Originally posted by: herv�

    I have the same issue (Unable to find an entry point <function name>) but i operate in vb.net not c# like you.

    Any idea ?

    Reply
  • How can we do this in Internet Explorer

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

    Originally posted by: Bharat


    How can we do this in Internet Explorer.
    I tried to do the same in IE using Usercontrol. But C++.dll is not getting at the client side. Other dll whicc created in C# using the UserContrl is getting in the CLient side. But on clicking Button to acces this dll(SysInfo.dll) to get the functionality shows ERROR -"dll not found exception etc".
    So the question is how can we access a C++.dll to the client side.

    Need all Steps to do.

    bharat

    • i'd like to know that too

      Posted by subtile on 05/10/2004 09:49am

      ????

      Reply
    Reply
  • How to call C++ dll functions (having parameters of user-defined datatypes) from C#

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

    Originally posted by: Bhaskar Kalle

    This is a good article. But, I have a question as how to call from C#, functions written in C++ which take user-defined datatype parameters.

    Reply
  • Loading, Please Wait ...

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

Top White Papers and Webcasts

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

  • A modern mobile IT strategy is no longer an option, it is an absolute business necessity. Today's most productive employees are not tied to a desk, an office, or a location. They are mobile. And your company's IT strategy has to be ready to support them with easy, reliable, 24/7 access to the business information they need, from anywhere in the world, across a broad range of communication devices. Here's how some of the nation's most progressive corporations are meeting the many needs of their mobile workers …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds