What are the steps to expose C++ functions to C and .Net? I want to use the same function names in C++, C and .Net for 32-bit and 64-bit builds.
I'm posting this question and answer because I haven't found these techniques documented anywhere.
The steps are:
Expose C++ functions as a C static library (.lib).
Use #pragma statements to project the lib's functions into a DLL.
Use .Net's DllImport attribute to expose the DLL's functions.
For an example, see this github project.
Step 1. Create a static library project. Create a C++ file that exposes each function as a C function.
extern "C" {
void vfunc(void) { return; }
int ifunc(int i) { return i; }
}
Step 2. Create a DLL project. Specify the static library as an "Additional Dependency". Create a C file and put #pragma statements to project each static lib function into the .DLL. You'll need to define two #pragmas, one for 32-bit and another for 64-bit. The difference being that 32-bit requires a leading underscore before the function name. No code necessary, just #pragmas.
#ifdef _M_X64
#define Alias(func) comment(linker, "/export:" func "=" func)
#else
#define Alias(func) comment(linker, "/export:" func "=_" func)
#endif
#pragma Alias("vfunc")
#pragma Alias("ifunc")
Step 3. Create a .Net project. Use DllImport attribute to expose each function.
class Program
{
[DllImport("c-dll.dll")]
extern static void vfunc();
[DllImport("c-dll.dll")]
extern static void ifunc();
static void Main(string[] args)
{
vfunc();
int i = ifunc(1);
}
}
While coding is straightforward using these techniques, you'll need to do quite a bit of editing of solution and project files.
Set the solution's correct build order. The DLL will depend on the static library. The .Net projects will depend on the DLL.
Manually edit the DLL project file to build the 32-bit and 64-bit DLLs into separate directories (e.g. Win32\Debug, x64\Debug).
Manually edit the .Net project files to create sections for combinations of x86/x64 and Debug/Release (4 total). DO NOT USE AnyCPU. DllImport WILL NOT WORK WITH AnyCPU. USE ONLY x86/x64.
Manually edit the .Net project file output path to be the same Win32/x64 directories as above. This will allow DllImport to find the DLL since DLL/EXE share the same directory. Optionally you can put the DLL into a discoverable directory or put a path in DllImport -- but that's problematic when you are using two DLLs (32/64).
Related
I've always found it a bit of a pain that you have to use LoadLibrary() and define function pointers when wanting to make use of a DLL, it just feels a bit archaic when so much is automated now days. So when I stumbled across a forum post explaining that the VS auto-generated lib file that always (in my experience) gets compiled alonside your DLL can be used to save yourself the hassle, as a sort of auto-import mechanism, I figured I'd put it to the test.
I created two very minimal projects to test this, but it doesn't appear to be working.
The code in its entrirety for the DLL:
extern "C"
{
__declspec(dllexport) int get10()
{
return 10;
}
}
And for the exe (an otherwise empty Win32 console app):
#include <Windows.h>
#pragma comment(lib, "Kernel32.lib")
#pragma comment(lib, "dll1.lib")
__declspec(dllimport) int get10();
int main(int, char**)
{
int x = 0;
if (LoadLibrary(L"dll1.dll") != NULL)
{
x = get10();
}
return 0;
}
dll1.dll and dll1.lib are local and visible to the project, but when I go to link it get10() is unresolved. If I remove the call to this it builds and runs and the LoadLibrary() call succeeds.
I also tried copying across the dll1.exp, just clutching at straws really, but that didn't help either.
Is what I'm trying to do actually valid?
By the way I appreciate you'd usually just use a lib file proper, it just grabbed my curiosity for circumstances where you might have these dll and "reduced" lib files but not have the source to compile a regular lib file.
You either link as static, or as dynamic.
So, in essence, you need __declspec(dllexport) only if you're loading the function at runtime by name. Read some of this, for example.
As mentioned here, regarding __declspec(dllexport):
This convenience is most apparent when trying to export decorated C++ function names. Because there is no standard specification for name decoration, the name of an exported function might change between compiler versions. If you use __declspec(dllexport), recompiling the DLL and dependent .exe files is necessary only to account for any naming convention changes.
I need to create a dll which contains stuff I have in my executable project in visual studio 2010. I realized instead of creating a dll project, I can just change the project configuration in project properties >> General >> 'configuration type' to 'dll' and it builds fine. It creates the dll. I added an additional .h/.cpp files which contains the export functions I want in the dll.
My first concern is that is this a legit dll? I am trying to load it using LoadLibrary() but I get error code 126 (The specified module could not be found) although the dll is in the project directory (same as executable). I am just wondering if it has to do with the fact it may not be a fully qualified dll for any reason? My exe project is MFC project.
** Update **
Thanks to the comments, I can now load the dll successfully - it was dependencies issue. However GetProcAddress() doesn't return valid pointer for the export function. The dumpbin /exports utility shows the dll has no export functions!
So I have added just .h/cpp files to the original project which has a simple dummy function for export right now.
__declspec(dllexport) int MakeDouble(int value);
I also included the header file in the app class just in case. I am wondering why does this function does't appear as an export? What do I have to do?
First:
__declspec(dllexport) int MakeDouble(int value);
Function declaration should have the same signature than the definition and, of course, the function must have a definition (at simple return 0; should work }
Second:
The exported function name is decorated with beautiful weird characters, you should use extern "C" (or the MS specific stdcall + the .def file).:
//.h
extern "C" __declspec(dllexport) int MakeDouble(int value);
//.cpp
extern "C" __declspec(dllexport) int MakeDouble(int value) {
return 0;
}
You should also check in project properties the option:
Configuration Properties -> C/C++ -> Code Generation -> Runtime Library
Make sure the value contains the word DLL.
As the title says, although I guess what I really mean is "And using them later."
The Setup
I have followed this answer:
https://stackoverflow.com/a/13219631/696407
which creates a very simple dll
#include <stdio.h>
extern "C"
{
__declspec(dllexport) void DisplayHelloFromMyDLL()
{
printf ("Hello DLL.\n");
}
}
and I now have a dll compiled for release:
DllTest.dll
DllTest.exp
DllTest.lib
DllTest.pdb
When I run DllTest.dll through dumpbin, I find this line:
1 0 00001000 DisplayHelloFromMyDLL = _DisplayHelloFromMyDLL
USING THE DLL
To use that function in a new solution, I believe I must
Start a project in a new solution
Add the location of the DLL to the project under
Properties
Configuration Properties
Linker
General
Additional Library Directories
Add the .lib file under
Properties
Configuration Properties
Linker
Input
Additional Dependencies
and, having added the .lib there, the next step is... hvæt?
My code right now:
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
while(1)
{
DisplayHelloFromMyDLL();
}
return 0;
}
but that doesn't work.
EDIT: I guess "doesn't work" is vague. The function gets Error: identifier "DisplayHelloFromMyDLL" is undefined
(Side question: Is my function called DisplayHelloFromMyDLL(); or _DisplayHelloFromMyDLL();?)
You need .h for compiler (use with #include, and add the folder to .h file as relative path to Configuration Properties > C/C++ > General > Additional Include Directories). Aside from .lib for linker you also need .dll to actually run the test application.
EDIT: There are two types of DLL's that you can make. First are C-like DLL's, with functions that have signatures as if they are written in C instead of in C++. All Windows DLL's (user32.dll, shell32.dll, version.dll) are built as such. The other are C++ DLL's, with functions that are part of the class. MFC and Standard C++ Libraries are such.
If you want to make a C++ DLL then you have to declare all classes that are part of interface as __declspec(dllexport) in your DLL project and __declspec(dllimport) in all projects that would use DLL. Usually the same file is used for this, but with a macro that is defined accordingly to one or the other. If you create a DLL from Visual Studio project template you would see this code.
Your case is actually the simpler case, as you want C-like DLL. You don't have to fiddle with this __declspec rubbish, but you need one additional .def file in DLL project. This should be the content of the .def file:
LIBRARY MyApi
EXPORTS
DisplayHelloFromMyDLL
Your header file (.h file) should look like this:
#pragma once
#ifndef HELLO_DLL_INCLUDED
#define HELLO_DLL_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
void DisplayHelloFromMyDLL();
#ifdef __cplusplus
};
#endif
#endif // HELLO_DLL_INCLUDED
__declspec(dllimport) tells the compiler that this function (or class) is defined somewhere else, and that linker will find it and link it. __declspec(dllexport) tells the compiler (and linker) that this function (or class) should be exported and be part of DLL interface. If class has neither of those then it's just a class that should be defined in the same project.
To consume your .dll you need two things, a header file and a .lib.
The header file is so that the compiler knows there is a function somewhere with the name DisplayHelloFromMyDLL(). At this point it doesn't matter where it is, just that you've told the compiler it's somewhere. The linker will take care of the where bit.
The .lib file is for the linker. It tells the linker that DisplayHelloFromMyDLL() lives in a .dll, and that (in your case) the name of the dll is DllTest.dll. When your program starts up the Windows loader will use this information to load the .dll into your process and will perform any address fixups to make sure that calling DisplayHelloFromMyDLL() in your application calls the function in your .dll.
You don't actually need the .dll in order to build your executable, only to run it.
I currently have a console application written in unmanaged C++, the source code consists of an entry-point main and some other functions. I need to create a DLL from this code so that I can use it from other projects, specifically from managed C++. (Another question: would I have to write a wrapper class for this purpose?)
As I know next to nothing of managed/unmanaged C++ and creating DLLs, I followed this tutorial and managed to get a simple Hello World DLL up and running by using only VS2010 (no CMake).
However, this project of mine has a lot of dependencies (e.g. Point Cloud Library), and so I normally use CMake to generate the Visual Studio 2010 solution that then builds into an executable, as described in the PCL Tutorial. How can I use CMake to build a VS2010 project that will build into a DLL?
To summarise my problem:
I have a project of unmanaged C++ code that needs a lot of dependencies.
I want to create a DLL from this code that can be called from managed C++.
Extra information:
Windows 7, Visual Studio 2010 Ultimate, CMake 2.8.10.2
EDIT:
I used CMake with your line changed, and it worked as expected. This is what I have since added to my header file, am I on the right track?
MyCode.h
#ifdef MyLib_EXPORTS
#define API_DECL __declspec( dllexport )
#else
#define API_DECL __declspec( dllimport )
#include <iostream>
#include <pcl/...>
etc...
API_DECL void myFirstFunction();
API_DECL void mySecondFunction();
#endif
MyCode.cpp: I have not made any changes to the source file, should I make any?
Unfortunately, I cannot help you with the managed code part, but this is how you create a DLL in CMake:
First of all, instead of using
`ADD_EXECUTABLE( YourLib SHARED yourclass.cpp yourclass.h )`
in your CMakeLists.txt, use
`ADD_LIBRARY( YourLib SHARED yourclass.cpp yourclass.h )`
This will configure the solution to create a DLL rather than an executable.
However, to be able to use this DLL with your projects, you need to export the symbols you want to use. To do this, you need to add __declspec( dllexport ) to your class and/or function declarations. Building the library will then yield two files, a .dll and a .lib. The latter one is the so-called import library that you need when you want to use this library in your other projects. The .dll will be required at runtime.
However: When you want to use your library, you need to use __declspec( dllimport) (rather than dllexport). To avoid using two header files, the usual way to do this is to use the preprocessor. CMake actually helps you by providing a YourLibrary_EXPORTS define in your library project.
To summarize:
#ifndef YOUR_CLASS_H
#define YOUR_CLASS_H
#ifdef YourLib_EXPORTS
#define API_DECL __declspec( dllexport )
#else
#define API_DECL __declspec( dllimport )
#endif
class APIDECL YourClass {
void foo();
void bar();
};
#endif // YOUR_CLASS_H
EDIT:
If you want to be able to use those functions from C (and languages that are able to use C functions) you should wrap your declarations with extern "C" {
extern "C" {
API_DECL void myFirstFunction();
API_DECL void mySecondFunction();
}
I have a application and several plugins in DLL files. The plugins use symbols from the
application via a export library. The application links in several static libraries and this is where most of the symbols come from. This works fine as long as the application uses a symbol. If the symbol is not used there, I get linker errors when compiling the DLL.
How can I force the export of the symbols only used in the plugins?
In order to trigger the export I've tried something like this:
class MyClassExporter
{
MyClass mInstance;
public:
MyClassExporter() {}
};
static MyClassExporter TheMyClassExporter;
in one of the static libs the application is made of to force the export, which didn't work.
In response to Greg (thanks for the answer) and to clarify: The class I want to force the export for is MyClass (which has __declspec(...) defined, depending on wether I want to export or import). MyClassExport was my attempt to force the inclusion of unused (in terms of the application) symbols into the app. I want to 'touch' the symbols so that the linker recognizes them as used and includes them into the application so that it can in turn export these to my plugins. Linking the static libs into the plugins is not an option, since they contain singletons which would be duplicated (app and DLLs each have their own copy of static variables).
The /INCLUDE directive can be used to force the MSVC linker to include a symbol. Alternatively, /OPT:NOREF can be used to disable removal of unused symbols in general.
A common approach is to create a single unused function that references all objects exported for your plugins. Then you only need a single /INCLUDE directive for that function.
You probably want to look at __declspec(export/import)
#ifdef DLL_EXPORTING
#define WHDLL __declspec(dllexport)
#else
#define WHDLL __declspec(dllimport)
#endif
When linking static module into a dll it will only bring in the code that is used. I've never imported stuff from a static lib to simply re export it.
Perhaps you just need to mark it as exportable in the dll when compiling the static lib.
But that reminds me of putting std containers into exported classes and using some trickery in msvc to export the 'instance' of the specialised container. the template code is similar to your static code (in my thinking)
for instance without the template you get warnings the template code is not exported to support the class - this is MSVC specific from my understanding
template class DLL_EXPORTING std::auto_ptr<wxCursor>;
class DLL_EXPORTING imageButton : public wxWindow
{
std::auto_ptr<wxCursor> m_Cursor;
};
What I tried out to solve this was this:
build a static library with function void afunction( int ).
build a dll, linked to static lib, exporting afunction.
build an exe, using the afunction symbol.
How? Since the linker can be told to export functions using the __declspec(dllexport) directive, a dll needs no more than declare a to-be-exported symbol thusly.
The lib has a header "afunction.h" and an accompanying cpp file containing the function body:
// stat/afunction.h
namespace static_lib { void afunction(int); }
// stat/afunction.cpp
#include "afunction.h"
namespace static_lib { void afunction(int){ } }
The dll has an include file "indirect.h", containing the declaration of the function to be exported. The dll has a link-time dependency to the static lib. (Linker options: Input/Additional Dependencies: "static_library.lib")
// dll/indirect.h
namespace static_lib {
__declspec( dllexport ) void afunction(int);
}
The executable has only the indirectly included file:
#include <dll/indirect.h>
int main() { static_lib::afunction(1); }
And guess what? It compiles, links and even runs!
The "Use Library Dependency Inputs" option does the trick in VS2005!
This option can be found under Configuration Properties -> Linker -> General -> Use Library Dependency Inputs. Set to "true" to force linking in ALL symbols & code declared in every LIB specified as input to the project.
You can do the following to get the symbol to export from the DLL: define LIB_EXPORTS in the library project and nothing in either the DLL project or the DLL client project.
#ifdef LIB_EXPORTS
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI __declspec(dllimport)
#endif
Turns out there is no need #include any headers from the LIB project when compiling the DLL project; just specify the LIB as a linker input. However, if you need to make use of the LIB code from within the DLL, you will need to #define DLLAPI as an empty macro; setting the symbol(s) to either dllexport or dllimport will generate an error or a warning, respectively.
There is some discussion of this problem on MSDN which was pretty useful. As it turns out, /OPT:NOREF is not particularly helpful in this case. /INCLUDE can work but it can be hard to automatically figure out what needs to be /INCLUDEd. There's unfortunately no silver bullet.
http://social.msdn.microsoft.com/forums/en-US/vclanguage/thread/2aa2e1b7-6677-4986-99cc-62f463c94ef3