Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C++/CLI: Attributes


DllImport

With the aid of the DllImport attribute, we can call unmanaged functions, provided they reside in a DLL. For example, the following C++/CLI program calls an unmanaged C function called Hypot that resides in a DLL called Tools.dll:

using namespace System;
using namespace System::Runtime::InteropServices;

[DllImport("Tools.dll", CallingConvention =
CallingConvention::StdCall)]
extern double Hypot(double side1, double side2);

The first argument one must pass to the DllImport constructor is the DLL's name, as shown. This attribute has a number of fields, whose values can be set during construction. The one used here is CallingConvention, which has been set to the value StdCall. (This also happens to be the default calling convention.)

int main()
{
        Console::WriteLine("Hypotenuse = {0}", Hypot(3, 4));
        Console::WriteLine("Hypotenuse = {0}", Hypot(5, 12));
        Console::WriteLine("Hypotenuse = {0}", Hypot(2.34, 6.78));
}

The output produced is:

Hypotenuse = 5
Hypotenuse = 13
Hypotenuse = 7.17244728108893

Here then is the unmanaged C source:

#include <math.h>
__declspec(dllexport) double __stdcall Hypot(double side1, double side2)
{
        return sqrt((side1 * side1) + (side2 * side2));
}

Since the default calling convention for C is cdecl, the keyword stdcall has been used. (Alternatively, the default could have been used and the calling convention changed in the C++/CLI code.)

Here are some more examples; these involve calling functions in the Win32 API library and the Visual C++ Standard C library:

using namespace System;
using namespace System::Text;
using namespace System::Runtime::InteropServices;

// BOOL SetCurrentDirectory(LPCTSTR pstrDirName);
[DllImport("Kernel32.dll", CharSet = CharSet::Unicode)]
extern bool SetCurrentDirectory(String^ pstrDirName);

// DWORD GetCurrentDirectory(DWORD dwLen, LPTSTR pstrDirName);
[DllImport("Kernel32.dll", CharSet = CharSet::Unicode)]
extern int GetCurrentDirectory(int length, StringBuilder^ pstrDirName);

Functions that expect a C-style string (that is, a null-terminated array of char or wchar_t), and that do not modify that string, have their corresponding argument declared as String^. If the Unicode (16-bit character) version is wanted, that is the character set selected; if the ANSI (8-bit character) version is wanted, that is selected.

Functions that expect a C-style string and that do modify that string have their corresponding argument declared as StringBuilder^.

Since all the functions in the Win32 API library use the StdCall calling convention, and that is the default for the DllImport attribute, the calling convention need not be stated.

// UINT GetSystemDirectory(LPTSTR lpBuffer, UINT uSize)
[DllImport("Kernel32.dll", CharSet = CharSet::Auto)]
extern unsigned int GetSystemDirectory(StringBuilder^ sysDirBuffer,
        unsigned int size);

// BOOL GetUserName(LPTSTR lpBuffer, LPDWORD nSize);
[DllImport("Advapi32.dll", CharSet = CharSet::Auto)]
extern bool GetUserName(StringBuilder^ userNameBuffer,
        unsigned int *size);

// int MessageBox(HWND hWnd, LPCTSTR
lpText, LPCTSTR lpCaption,
        UINT uType)
[DllImport("User32.dll")]
extern int MessageBox(IntPtr hWnd, String^ text, String^ caption,
        unsigned int type);

The managed type IntPtr is used to hold a generic address.

// int printf(const char *format [, argument]... );
[DllImport("msvcrt.dll", CharSet = CharSet::Ansi,
        CallingConvention = CallingConvention::Cdecl)]
extern int printf(String^ format, int i);

[DllImport("msvcrt.dll", CharSet = CharSet::Ansi,
        CallingConvention = CallingConvention::Cdecl)]
extern int printf(String^ format, double d);

Functions, like printf, having a variable number of arguments, can be imported via a number of overloads.

Since all the functions in the Standard C library use the Cdecl calling convention, and that is not the default for the DllImport attribute, the calling convention is specified.

// int wprintf(const wchar_t *format [, argument]... );
[DllImport("msvcrt.dll", CharSet = CharSet::Unicode,
        CallingConvention = CallingConvention::Cdecl)]
extern int wprintf(String^ format, int i);

[DllImport("msvcrt.dll", CharSet = CharSet::Unicode,
        CallingConvention = CallingConvention::Cdecl)]
extern int wprintf(String^ format, double d);

As defined, printf is intended to deal with single-byte characters, while wprintf is intended for wide characters; that is, Unicode.

// size_t strlen(const char *str);
[DllImport("msvcrt.dll", CharSet = CharSet::Ansi,
        CallingConvention = CallingConvention::Cdecl)]
extern unsigned int strlen(String^ str);



int main()
{
	StringBuilder^ curDirBuffer = gcnew StringBuilder(256);
	int result = GetCurrentDirectory(curDirBuffer->Capacity,
					curDirBuffer);
	Console::WriteLine("Current path is >{0}<",
curDirBuffer);

	StringBuilder^ sysDirBuffer = gcnew StringBuilder(256);
	GetSystemDirectory(sysDirBuffer,
					(unsigned int)sysDirBuffer->Capacity);
	Console::WriteLine("The system directory is >{0}<",
sysDirBuffer);

	StringBuilder^ userNameBuffer = gcnew StringBuilder(128);
	unsigned int size = (unsigned int)userNameBuffer->Capacity;
	GetUserName(userNameBuffer, &size);
	Console::WriteLine("The username is >{0}<",
userNameBuffer);

	result = MessageBox(IntPtr::Zero, "MyMessage",
"MyTitle", 1);"
	Console::WriteLine("Call result: {0}", result.ToString() );

	SetCurrentDirectory("e:\\temp");
	result = GetCurrentDirectory(curDirBuffer->Capacity, curDirBuffer);
	Console::WriteLine("Current path is >{0}<",
curDirBuffer);

	printf("printf: %d\n", 123);
	printf("printf: %f\n", 98.76);
	wprintf("wprintf: %d\n", 123);
	wprintf("wprintf: %f\n", 98.76);

	Console::WriteLine("String length = {0}",
					strlen("Managed C++").ToString());
}


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.