Click here to Skip to main content
11,712,792 members (88,738 online)
Click here to Skip to main content

Using Unmanaged code and assembler in C#

, 22 Sep 2001 CPOL 379.5K 3K 131
Rate this:
Please Sign up or sign in to vote.
This article describes how to call unmanaged code and assembler in C# using DLLs
<!-- Download Links --> <!-- Main HTML starts here -->

Introduction

The first question is why we call unmanaged code before we discuss how to call unmanaged code.

There are possibly two reasons to call unmanaged code

  1. You want to reuse your code which is already written in 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 in Tom Archer's "Inside C#" which explain how to call unmanaged DLL from the C#

Program 1

// 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);
    }
}
Then I tried to make my own DLL and call that DLL from my Application

Program 2

// Dll1.cpp
// Written by Zeeshan Amjad

#include <windows.h>

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);
}
And my C# Program is:

Program 3

// 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 ran program and my program crashed because it can not find DLL1.dll - either in current directory nor in the path. It threw a DllNotFoundException. To handle this I had to catch this exception. So I change my program little bit to:

Program 4

// 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());
		}
	}
};
And also copied the DLL1.dll to the current folder to avoid this exception.

Now again my program crashed when I tried to run it. This time it threw a EntryPointNotFoundException. To handle this more elegantly I should also catch this and display an error message for this exception rather than crash the program. This is new version of the program:

Program 5

// 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 this 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 this C# program. In fact when you write a function in C++ the compiler decorates the function name to enable function overloading. The function which exports by DLL is not Message. To get the exact name type 

dumpbin -exports dll1.dll  

at command prompt. The 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 function names, so we should tell the compiler not to decorate function name.

This is revised version of DLL code.

Program 6

// 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 if we see the function name from the dumpbin utility its output looks like this.

ordinal hint RVA      name

      1    0 0000100A _Message@4
Here @ shows the function uses the standard calling convention and 4 shows the number of bytes pushed on the stack for parameters. In a 32 bit environment like windows 9x and NT/2000 the address is stored in 32 bits i.e. 4 bytes. It means there is only one parameter in the stack  - in other words this function takes only one parameter.

Now the above C# Program works fine without any change and displays a message box with Text "Hello world" with the caption "Message from DLL"

How to call assembly in C#

Let's do an experiment with inline assembly in a DLL. I can not call assembly language from C# but I know I can call unmanaged DLLs from C#. I'll make a DLL which calculates the speed of CPU, vendor name, Family, Model and Stepping of CPU using in line assembly language.

Program 7

// 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 of this.

Program 8

// 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 I m going to write the same client in C#.

Program 9

// 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 write GUI for this program. The image below shows the output.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Zeeshan Amjad
Software Developer (Senior) Bloomberg LP
United States United States
Working as a Sr C++ Developer at Bloomberg LP

You may also be interested in...

Comments and Discussions

 
QuestionIs there a way to go from a C# dll to a C dll? Pin
blipton28-Mar-15 19:34
memberblipton28-Mar-15 19:34 
Questionhow do you import a method from unmanaged Dll class Pin
teshman24-Apr-14 19:02
memberteshman24-Apr-14 19:02 
Generalgarbled character strings (chinese characters) [modified] Pin
Krischu13-Aug-10 0:39
memberKrischu13-Aug-10 0:39 
GeneralVS2008 get BadImageFormatException when loading Dll1.dll [modified] Pin
Krischu12-Aug-10 22:49
memberKrischu12-Aug-10 22:49 
Generalassembly in c# Pin
metyouba30-Jun-10 13:34
groupmetyouba30-Jun-10 13:34 
Generaldll file Pin
metyouba24-Jun-10 6:29
groupmetyouba24-Jun-10 6:29 
GeneralRe: dll file Pin
Zeeshan Amjad24-Jun-10 8:50
memberZeeshan Amjad24-Jun-10 8:50 
Generalunmanaged code with classes and interfaces Pin
BatulKaleem15-Jun-10 5:00
memberBatulKaleem15-Jun-10 5:00 
QuestionIs there any way to access exported variables? Pin
Bug Me Not13-Oct-09 20:59
memberBug Me Not13-Oct-09 20:59 
AnswerRe: Is there any way to access exported variables? Pin
pk_fox7-Mar-10 22:19
memberpk_fox7-Mar-10 22:19 
QuestionMethod With Cstring Pin
rajeev.abes17-Sep-09 20:49
memberrajeev.abes17-Sep-09 20:49 
GeneralNice Article Pin
santhosh55516-Sep-09 7:45
membersanthosh55516-Sep-09 7:45 
GeneralMarshalling: Using native DLLs in .NET Pin
meukjeboer10-Sep-08 5:16
membermeukjeboer10-Sep-08 5:16 
GeneralI have the same error, DllNotFoundException but is not cured after i tried try and catch. Pin
sharp_k20-May-08 15:08
membersharp_k20-May-08 15:08 
GeneralRe: I have the same error, DllNotFoundException but is not cured after i tried try and catch. Pin
eduardna13-Jun-08 4:31
membereduardna13-Jun-08 4:31 
GeneralRe: I have the same error, DllNotFoundException but is not cured after i tried try and catch. Pin
sharp_k13-Jun-08 7:39
membersharp_k13-Jun-08 7:39 
GeneralRe: I have the same error, DllNotFoundException but is not cured after i tried try and catch. Pin
eduardna18-Jun-08 4:06
membereduardna18-Jun-08 4:06 
GeneralRe: I have the same error, DllNotFoundException but is not cured after i tried try and catch. Pin
sharp_k18-Jun-08 4:49
membersharp_k18-Jun-08 4:49 
GeneralGet CPU temperature with assembly and/or C++ Pin
Javalang31-Jul-07 5:48
memberJavalang31-Jul-07 5:48 
GeneralC lib on C# Pin
Bandoleiro11-Dec-06 4:37
memberBandoleiro11-Dec-06 4:37 
GeneralRe: C lib on C# Pin
markus.md5-Jan-09 7:23
membermarkus.md5-Jan-09 7:23 
GeneralExcellent Piece of Example Pin
bilalabbas29-May-06 21:11
memberbilalabbas29-May-06 21:11 
GeneralCreating Managed wrappers in C++ for unmanged C++ dll Pin
srinin00810-Feb-06 1:31
membersrinin00810-Feb-06 1:31 
GeneralStopping dll Pin
toticow29-Dec-04 18:13
membertoticow29-Dec-04 18:13 
GeneralRe: Stopping dll Pin
Peregrine Falcon20-Jul-05 4:26
memberPeregrine Falcon20-Jul-05 4:26 
GeneralRe: Stopping dll Pin
eduardna13-Jun-08 4:34
membereduardna13-Jun-08 4:34 
GeneralInterruptions in VC6 Pin
Cmaker16-Oct-04 7:02
memberCmaker16-Oct-04 7:02 
Generalconverting an unmanaged C++ DLL in C# Pin
Julien Racle2-Jun-04 22:01
sussJulien Racle2-Jun-04 22:01 
GeneralRe: converting an unmanaged C++ DLL in C# Pin
Peregrine Falcon20-Jul-05 4:24
memberPeregrine Falcon20-Jul-05 4:24 
QuestionIs it possible to load MFC Extension DLL? Pin
temp555614-Sep-03 6:28
membertemp555614-Sep-03 6:28 
Questionhow to use unmanged lib in C# Pin
Anonymous9-Jun-03 3:40
sussAnonymous9-Jun-03 3:40 
AnswerRe: how to use unmanged lib in C# Pin
Yinon Ehrlich5-Sep-04 0:03
memberYinon Ehrlich5-Sep-04 0:03 
GeneralRe: how to use unmanged lib in C# Pin
Anonymous2-Aug-05 10:31
sussAnonymous2-Aug-05 10:31 
Questionhow to find serial no. of hdd using c# Pin
rohitsarna31-Mar-03 18:07
memberrohitsarna31-Mar-03 18:07 
AnswerRe: how to find serial no. of hdd using c# Pin
Anonymous13-Oct-03 1:05
sussAnonymous13-Oct-03 1:05 
AnswerRe: how to find serial no. of hdd using c# Pin
Marcello Cantelmo6-Mar-05 13:04
sussMarcello Cantelmo6-Mar-05 13:04 
GeneralConvert a part of code from c to c# Pin
shab226-Sep-02 4:16
membershab226-Sep-02 4:16 
Generalwire ListViewNotify function to C#. Pin
kasturirawat22-Jan-02 8:55
memberkasturirawat22-Jan-02 8:55 
GeneralRe: wire ListViewNotify function to C#. Pin
SeveredCross8-Aug-05 16:10
memberSeveredCross8-Aug-05 16:10 
GeneralRe: wire ListViewNotify function to C#. Pin
The_Mega_ZZTer6-Oct-05 10:52
memberThe_Mega_ZZTer6-Oct-05 10:52 
QuestionWhy not use unmaned C++? Pin
Ramon Smits11-Jan-02 7:11
memberRamon Smits11-Jan-02 7:11 
QuestionSome questions about unmanaged code? Pin
Stormwind24-Oct-01 16:55
memberStormwind24-Oct-01 16:55 
AnswerRe: Some questions about unmanaged code? Pin
Ianier16-Jul-02 2:53
sussIanier16-Jul-02 2:53 
Generalassembly rules! Pin
Anonymous26-Sep-01 2:16
memberAnonymous26-Sep-01 2:16 
GeneralRe: assembly rules! Pin
tuannahtc17-Feb-06 22:16
membertuannahtc17-Feb-06 22:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150819.1 | Last Updated 23 Sep 2001
Article Copyright 2001 by Zeeshan Amjad
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid