[
Advertise | Submit Code | About us | Contact us | Link us
]
Go!
Membership Services
Login
Register

Home
C# General

General

C# Language

Design & Architecture

Algorithms

Database

Security

Active Directory

COM Interop

Remoting
C# Windows Forms

General

Combo and List boxes

Miscellaneous Controls

Button Controls

Edit Controls
Cutting Edge

ASP.NET 2.0

Visual Studio 2005

Windows Longhorn

SQL Server 2005
C# Multimedia and GDI+

General

DirectX

GDI+

Audio
Internet & Web

General

Images and multimedia

Database

Utilities

Security

ASP.NET Controls

Design and Architecture

Webservices
.NET

General

Design & Architecture

Algorithms

Database

Security

Active Directory

COM Interop

Remoting

ADO.NET

XML.NET

Tools

Enterprise

IDE
Visual Basic .NET

VB.NET General

VB.NET Controls
General Reading

.NET Books Review

Product Showcase

Book Chapters

Business Design & Strategy
Community

Discuss

Job Board

Discussion

CodeXchange
DeveloperLand

Advertise

Submit Code

About us

Contact us

Link us
Miscellaneous

Favorite Links

Downloads

Programming Sites

Top Stories
Regular Expressions

E-Mail

Date/Time
Home > C# General > General
Using Unmanaged Code in C#
Posted by on Saturday, September 18, 2004 (EST)

This article shows you how to make unmanaged API calls from C#

This article has been viewed: 9,558 times
Technology: General.

Unmanage.zip (4.06 KB)

Contents

Introduction

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)

Top Go to Table of Contents

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 
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 
#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());
     }
  }
};

Top Go to Table of Contents

About Zeeshan Amjad

Click here if you want to know more about .

Other articles that may interest you

  • Write a Word Add-In – Part 0
  • Write a Word Add-In – Part I
  • Lengthy Operations on Single Thread in .NET Application
  • Learning Draughts
  • Exceptions and Performance
  • Average Rating :

    Discussion Forums
    Got a programming related question? Hopefully someone has the answer... Want to help out other developers? Visit our discussion forums.

    Sponsored by:

    New Articles

  • Exceptions and Performance
    Almost every time exceptions are mentioned in mailing lists and newsgroups, people say they're really expensive.Let's examine that claim, shall we?

  • Creating multilingual websites - Part 1
    Extend the existing globalization capabilities of .NET to create flexible and powerful multilingual web sites. First, create a custom ResourceManager, and then create custom localized-capable server controls to easily deploy multilingual functionality.

  • Parameter passing in C#
    Many people have become fairly confused about how parameters are passed in C#, particularly with regard to reference types. This page should help to clear up some of that confusion

  • Most Popular Articles

  • LDAP, IIS and WinNT Directory Services
    This article explains how to use .NET Directory Services to retrieve and search directory objects, create new directory objects and edit or delete existing directory objects. Describes Active Directory Application Mode (ADAM) and how to use the IIS, WinNT and LDAP directory (ADSI) provider.

  • An in-depth look at WMI and instrumentation, Part II
    WMI stands for Windows Management Instrumentation and, as the name indicates, is about managing your IT infrastructure this article is the second part of a two-part series.

  • An in-depth look at WMI and instrumentation, Part I
    WMI stands for Windows Management Instrumentation and, as the name indicates, is about managing your IT infrastructure this article provides an in-depth look at WMI and MOM 2005

  • New Books

  • Murach's ASP.NET 2.0 Upgrader's Guide: VB Edition
    What’s new and how to use it! That’s what this book delivers if you’re a VB developer who’s interested in upgrading from ASP.NET 1.x to ASP.NET 2.0.

  • C# in easy steps
    Learn to program with Microsoft’s premier programming language. No previous programming knowledge is assumed. With numerous easy-to-follow examples, this title explains the essentials of object-oriented programming with C#.

  • Murach's ASP.NET web programming with VB.NET
    Murach's ASP.NET web programming with VB.NET by Doug Lowe and Anne Prince is a in depth training and reference book for ASP.NET programming using VB.NET. The book builds upon Murach's previous books and covers more advanced concepts for programming ASP.NET pages.

  • Got Code?

    if you have any article , source code , or anything else you'd like to share with this community that you think others might find useful, please submit it here and we will gladly make it available on this site. submit@developerland.com.
    Partners

    All articles are copyrighted by their individual authors unless otherwise specified , everything else Copyright ©2004-2006 DeveloperLand