February 28, 2021
Hot Topics:

Head-Spinning Continued: P/Invoke

  • By Kate Gregory
  • Send Email »
  • More Articles »

Using the DLL — The Old Way

I created an ordinary unmanaged console application to use the Add() function. The single file of code looks like this:

#include "stdafx.h"
using namespace std;

extern "C" __declspec(dllimport) double Add(double num1,
                                            double num2);

int _tmain(int argc, _TCHAR* argv[])
  cout << "1 + 2 is " << Add(1,2) << endl;
  return 0;

To avoid any "DLL Hell" issues, I prefer to make local copies of any DLLs I use, so that changes to the DLL don't affect my code unless I choose to copy the changed DLL. I copied legacy.dll into the project folder for the console application. When you build a DLL project in Visual C++, you get a companion .LIB file called the import library; I copied that file to the same place. Then it's just a matter of linking the import library into the project: in Solution Explorer right-click the project and choose Properties, expand the Linker section, select Input, and add legacy.lib to the Additional Dependencies properties.

That's all it takes to use the DLL. It's not very different from linking with a static library, as far as your coding effort is concerned. The real issue is that when the DLL changes, your code uses the new code. That's a double-edged sword, of course. Keeping a private copy can spare you that worry, while still enabling your old unmanaged code and your new managed code to use the same library, as you're about to see.

Using the DLL — The New Way

How does managed code use a DLL? It's all over the documentation: Platform Invoke, also known as P/Invoke. Most examples show you how to access a Windows DLL, in case the Base Class Libraries don't offer some particular functionality you need. It's no different to access your own DLLs though.

In a console application, to use the Add function, you need to declare it with a DllImport attribute, like this:

using namespace System::Runtime::InteropServices;
extern "C" {
double Add(double num1, double num2);

The parameter to this attribute, legacy, names the dll. Notice that you omit the extension: in some other .NET languages you must include the extension, so don't let sample code from another language confuse you here. You don't need to add any references to use the DllImport attribute, but it is in a namespace of its own, making a using statement convenient.

Copy the DLL into the project folder, and then you can just call the function:

System::Console::Write(S"1 + 2 is ");
System::Console::WriteLine(__box( Add(1,2)));

As in my COM Interop column, you need to box the double that's returned from the legacy function in order to pass it to WriteLine(), which doesn't know how to handle double variables, or any other unmanaged type. No matter how you settle your interop issues, you're going to need to convert between managed and unmanaged data as part of your solution. If you're lucky, boxing alone will take care of what you need.

The real power of PInvoke lies outside this example. It provides access to the DLL, but it can layer extra capabilities on top of that. For example, if the DLL function takes a char* string, you can declare the .NET version of the function as taking a System::String*, and the framework will handle the conversion for you automatically. You can add a variety of attributes to control things like string marshaling, structure layout, and more. You can even write your own marshaling code to convert between a managed and unmanaged data type. I'll be returning to more advanced PInvoke topics in a later column.

Is This The Way For You?

Does it make sense to wrap your existing code into a DLL, change your existing unmanaged code to use the DLL, and then have your new managed code use the DLL with PInvoke? Well, it's a higher-performance solution than the COM Interop approach I presented in an earlier column. However, if the default marshaling works for you, it's a bit of a waste to use PInvoke. Next time I'll show you how to access that same DLL without the DllImport attribute — from C++ only.

About the Author

Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET.

# # #

Page 2 of 2

This article was originally published on August 26, 2003

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

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