I'm trying to do a little test-plugin using NPAPI for firefox. This is my code so far:
/* File: npp_test.cpp
Copyright (c) 2012 by Niklas Rosenstein
Testing the NPAPI interface. */
// - Includes & Preprocessors --------------------------------------------------
// - -------- - ------------- --------------------------------------------------
#define DEBUG
#ifdef DEBUG
# include <iostream>
using namespace std;
# include <windows.h>
#endif // DEBUG
#include <stdint.h>
#include <npapi.h>
#include <npfunctions.h>
#include <npruntime.h>
#define DLLEXPORT extern __declspec(dllexport)
// - NPAPI Calls ---------------------------------------------------------------
// - ----- ----- ---------------------------------------------------------------
NPError NP_New(NPMIMEType pluginType, NPP npp, uint16_t mode, int16_t argc,
char* argn[], char* argv[], NPSavedData* saved);
// - Entrypoints ----------------------
// - ----------- ----------------------
NPError NP_GetEntryPoints(NPPluginFuncs* pFuncs) {
# ifdef DEBUG
cout << "NP_GetEntryPoints\n";
# endif // DEBUG
// Initialize plugin-functions
pFuncs->newp = NP_New;
return NPERR_NO_ERROR;
}
NPError NP_Initialize(NPNetscapeFuncs* npFuncs) {
# ifdef DEBUG
cout << "NP_Initialize\n";
MessageBox(NULL, "NP_Initialize", "Plugin-message", 0);
# endif // DEBUG
return NPERR_NO_ERROR;
}
NPError NP_Shutdown() {
# ifdef DEBUG
cout << "NP_Shutdown\n";
# endif // DEBUG
return NPERR_NO_ERROR;
}
// - Plugin Execution -----------------
// - ------ --------- -----------------
NPError NP_New(NPMIMEType pluginType,
NPP npp,
uint16_t mode,
int16_t argc,
char* argn[],
char* argv[],
NPSavedData* saved) {
# ifdef DEBUG
cout << "NP_New\n";
# endif
if (!npp)
return NPERR_INVALID_INSTANCE_ERROR;
return NPERR_NO_ERROR;
}
I'm compiling the code using g++ 4.4.1
g++ npp_test.cpp -I"D:\include\xulrunner" -shared -Wall -Wextra -o "npp_test.dll"
Compiles fine, but looking at the DLLs content using DLL Expat, the names are not as expected:
==================================================
Function Name : _get_output_format
Address : 0x6889c658
Relative Address : 0x0001c658
Ordinal : 5 (0x5)
Filename : npp_test.dll
Type : Exported Function
Full Path : C:\Users\niklas\Desktop\npp_test.dll
==================================================
==================================================
Function Name : _Z6NP_NewPcP4_NPPtsPS_S2_P12_NPSavedData
Address : 0x68881270
Relative Address : 0x00001270
Ordinal : 4 (0x4)
Filename : npp_test.dll
Type : Exported Function
Full Path : C:\Users\niklas\Desktop\npp_test.dll
==================================================
==================================================
Function Name : NP_GetEntryPoints#4
Address : 0x688811d8
Relative Address : 0x000011d8
Ordinal : 1 (0x1)
Filename : npp_test.dll
Type : Exported Function
Full Path : C:\Users\niklas\Desktop\npp_test.dll
==================================================
==================================================
Function Name : NP_Initialize#4
Address : 0x68881205
Relative Address : 0x00001205
Ordinal : 2 (0x2)
Filename : npp_test.dll
Type : Exported Function
Full Path : C:\Users\niklas\Desktop\npp_test.dll
==================================================
==================================================
Function Name : NP_Shutdown#0
Address : 0x6888124f
Relative Address : 0x0000124f
Ordinal : 3 (0x3)
Filename : npp_test.dll
Type : Exported Function
Full Path : C:\Users\niklas\Desktop\npp_test.dll
==================================================
Shouldn't they be called like in the source? When "expating" the java-dll for firefox, just for example, the names are fine. Using DLLEXPORT as
#define DLLEXPORT __declspec(dllexport)
doesn't work either. But this at least "removes" the functions _get_output_format and _Z6NP_NewPcP4_NPPtsPS_S2_P12_NPSavedData from being exported, whyever they are exported when not using DLLEXPORT.
Why are the exported functions names with an additional #4/#0 suffix ? I guess the number after the # specifies the number of bytes the function takes as arguments, but when exporting, this actually shouldn't be contained in the name, right ?
The #(stack_size_of_params) is a name decoration for stdcall extern "C" functions. I'm more familiar with Microsoft tools but my belief is that you will need to use a .def file to export undecorated names for functions that use stdcall.
Edit: Websearch suggests --kill-at command line option for the GNU tools could avoid need for irksome .def files.
This is C++ so names are mangled unless you declare them also with extern "C"
If you want control over exported function names, use an EXPORTS section in a linker definition file (*.def), not __declspec(dllexport).
With GCC NP_EXPORT already takes care of the symbol visibility, e.g.:
extern "C" NP_EXPORT(NPError) NP_GetEntryPoints(NPPluginFuncs* pFuncs);
That only works for Unixes though, so for GCC on Windows you'll have to set the visibilities yourself.
On Windows/VC++ you additionally have to specify the exports in a .def file as mentioned:
NP_GetEntryPoints #1
NP_Initialize #2
NP_Shutdown #3
Related
Problem
I have a C++ project and create a library A from it. If I now link another project B with this library A, I of course also have to provide an include path for A's headers, so I just use A's source folder. But A's headers contain symbols that aren't exported. I feel like this is not the correct way to do it, but don't know better. A specific thing that makes me feel like this is incorrect is that my IDE suggests the symbols that aren't exported.
I'd guess the solution would be to create an include folder besides the source folder where the same headers are in but only with the exported symbols. So at build-time, every symbol with PROJECTAPI should be automatically copied over to the corresponding headers in the include folder. But if I google, I don't find such a function for e.g. cmake.
So what would be the recommended way here? Is there a functionality to create such an include folder?
Example
example.cpp of project B
#include <A/main.hpp>
int main() {
ex::World w("Earth");
w.say_hello();
//IDE wouldn't see this as error: w.private();
}
main.cpp of project A
#include <iostream>
#include "main.hpp"
namespace ex {
void World::say_hello() {
std::cout << "Hello, World from " << m_name << std::endl;
}
World::World(std::string name)
: m_name(name)
{}
void World::hidden() {
std::cout << "Not exported" << std::endl;
}
}
main.hpp of project A
#include <string>
#ifndef PROJECTAPI
# ifdef example_EXPORTS
# define PROJECTAPI __declspec(dllexport)
# else
# define PROJECTAPI __declspec(dllimport)
# endif
#endif
namespace ex {
class World {
private:
std::string m_name;
public:
void PROJECTAPI say_hello();
PROJECTAPI World(std::string name);
void hidden();
};
}
Edit: private isn't a good method name
You are looking for the PIMPL idiom. "PIMPL" is short for "Pointer to IMPLementation". The idea is that, at the cost of a pointer indirection, you hide the implementation data and private methods in an inner class whose definition is opaque to the API consumer.
This approach is especially effective if you need to provide ABI stability.
Herb Sutter has a great GOTW on this here: https://herbsutter.com/gotw/_100/
Here is a full example that's close-ish to your code:
$ tree
.
├── CMakeLists.txt
├── include
│ └── world.h
├── main.cpp
└── src
├── world.cpp
└── world_priv.h
In ./include/world.h (the public header)
#ifndef WORLD_H
#define WORLD_H
#include <memory>
#include <string>
#include "world_export.h"
namespace ex {
class World {
public:
WORLD_EXPORT World(std::string name);
WORLD_EXPORT ~World() /* = default */;
void WORLD_EXPORT say_hello();
private:
class Impl;
std::unique_ptr<Impl> pImpl;
};
} // namespace ex
#endif
In ./src/world_priv.h:
#ifndef WORLD_PRIV_H
#define WORLD_PRIV_H
#include "world.h"
namespace ex {
class World::Impl {
public:
Impl(std::string name) : name(std::move(name)) {}
void say_hello();
void hidden();
private:
std::string name;
};
} // namespace ex
#endif
In ./src/world.cpp:
#include <iostream>
#include "world_priv.h"
namespace ex {
World::World(std::string name)
: pImpl(std::make_unique<Impl>(std::move(name))) {}
World::~World() = default;
void World::say_hello() { pImpl->say_hello(); }
void World::Impl::say_hello() {
std::cout << "Hello, World from " << name << "\n";
}
void World::Impl::hidden() { std::cout << "Not exported" << std::endl; }
} // namespace ex
In main.cpp:
#include <world.h>
int main() {
ex::World w("Earth");
w.say_hello();
}
Finally, here's the build:
cmake_minimum_required(VERSION 3.21)
project(pimpl_example)
option(BUILD_SHARED_LIBS "Build world as shared rather than static" ON)
# Library
include(GenerateExportHeader)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
add_library(world src/world.cpp src/world_priv.h include/world.h)
add_library(world::world ALIAS world)
target_include_directories(
world PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>"
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
)
generate_export_header(world EXPORT_FILE_NAME include/world_export.h)
target_compile_definitions(
world PUBLIC "$<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:WORLD_STATIC_DEFINE>")
target_include_directories(
world PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
# Application
add_executable(app main.cpp)
target_link_libraries(app PRIVATE world::world)
This doesn't include install rules or anything, but it's ready for those to be written.
Building it:
$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
...
$ cmake --build build
$ ./build/app
Hello, World from Earth
$ $ nm ./build/libworld.so | c++filt | grep ' T ' | uniq
0000000000001460 T ex::World::say_hello()
00000000000012e0 T ex::World::World(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
00000000000013c0 T ex::World::~World()
You can see that only the API of ex::World is exported by the library. All the private details are hidden both in the code and in the library itself.
On Windows:
>dumpbin /EXPORTS build\world.dll
Microsoft (R) COFF/PE Dumper Version 14.28.29915.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file build\world.dll
File Type: DLL
Section contains the following exports for world.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
3 number of functions
3 number of names
ordinal hint RVA name
1 0 00001390 ??0World#ex##QEAA#V?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std###Z
2 1 00001550 ??1World#ex##QEAA#XZ
3 2 000015D0 ?say_hello#World#ex##QEAAXXZ
Summary
1000 .data
1000 .pdata
2000 .rdata
1000 .reloc
1000 .rsrc
2000 .text
Normally, a simple way to tackle that would be to create separate public and private headers ahead of time, and only expose the public ones to the user.
Here's a simple project structure that would accomplish that:
- lib_a
- include
- main.hpp
- src
- main_private.hpp
- main.cpp
Now, obviously, that won't work for the code you posted, since the declarations you want to separate belong to the same class. But that's just a symptom of the fact that what you are trying to do is unfortunately not allowed.
From the standard basic.def.odr:
There can be more than one definition of a
(13.1) class type ([class]),
[...]
in a program provided that each definition appears in a different translation unit and the definitions satisfy the following requirements.
[...]
Each such definition shall consist of the same sequence of tokens [...]
In other words, if you put a class in a public header, it has to be identical to the one that was used when compiling the library.
As much as it would be convenient, putting "half-a-class" in a public header is just not allowed.
I wrote this code in c++:
extern "C" __declspec(dllexport) int __stdcall sumx(int a, int b)
{
int result;
result = a + b;
return result;
}
I also tried:
int __stdcall sumx(int a, int b)
{
int result;
result = a + b;
return result;
}
and build win32 dll. then copy it in PB directory.
I define it external function.
And I call it:
when I run it:
Why do error occurs?
tnx
After some tests here I think that your problem may result from a name decoration of your exported function. I.E: instead of being named sumx in the dll, it is named _sumx#8 by the compiler.
You can check that by invoking dumpbin /exports keyadll.dll. With my test dll, it shows:
C:\dev\powerbuilder\dlltest>dumpbin.exe /exports keyadll.dll
Microsoft (R) COFF/PE Dumper Version 8.00.50727.762
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file keyadll.dll
File Type: DLL
Section contains the following exports for keyadll.dll
00000000 characteristics
5627876B time date stamp Wed Oct 21 14:39:07 2015
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA name
1 0 0000100A _sumx#8 = #ILT+5(_sumx#8)
^====================== HERE is the point!
Summary
1000 .data
1000 .idata
2000 .rdata
1000 .reloc
1000 .rsrc
2000 .text
BTW, the #8 in the name stands for the 8 bytes (2 x sizeof(int)) of parameters that are given to the function.
You have 2 options to fix that:
use the exact _sumx#8 name in the declaration of the external function in PB (you can use an alias for not changing your PB code):
function int sumx (int a, int b) library "keyadll.dll" alias for '_sumx#8'
I don't consider that solution being very elegant, though.
you can force VC to name the exported as YOU want (and not the reverse!) by using a module definition file (a .def).
in VS, choose to add a new item to the project / module definition file
simply put the names of the functions to export. It will contain
LIBRARY "keyadll.dll"
EXPORTS
sumx
Rebuild your dll and it should be OK for PB.
I am trying to port a 32-bit dll (and application) to 64-bit and I have managed to build it without errors. When trying to load it with my 64-bit application I noticed that the exported function names differ. This is how I export the functions:
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) long __stdcall Connect(char * name, long size);
#ifdef __cplusplus
}
#endif
In Dependency Walker the exported functions have the following format:
32-bit: _Connect#8
64-bit: Connect
In the application using the dll I explicitly load the dll (LoadLibrary is successful) but GetProcAddress fails for 64-bit because it cannot find a function with the provided name.
In our application I keep the function names as follows:
#define ConnectName "_Connect#8"
...
GetProcAddress(Dll, ConnectName);
So I was wondering if it is possible to export the same function names for both 32-bit and 64-bit dlls or is this a bad idea? Or do I need to do the following in my applications:
#if _WIN64
#define ConnectName "Connect"
#else
#define ConnectName "_Connect#8"
#endif
I appreciate any help.
An option you have to export function names without any decoration (independently from the particular calling convention you used in x86, __stdcall, __cdecl, or other) and with the same undecorated name in both x86 and x64 builds, is to export your DLL functions using DEF files.
E.g. you could add a .DEF file like this to your project:
LIBRARY YOURDLL
EXPORTS
Connect #1
AnotherFunction #2
... etc. ...
Repro Follows
Create an empty solution in Visual Studio (I used VS2013), and inside that create an empty Win32 console project (the test client) and an empty Win32 DLL project (the test DLL).
Add this NativeDll.def .DEF file in the DLL project:
LIBRARY NATIVEDLL
EXPORTS
SayHello #1
Add this NativeDll.cpp C++ source code in the DLL project:
///////////////////////////////////////////////////////////////////////////////
//
// NativeDll.cpp -- DLL Implementation Code
//
///////////////////////////////////////////////////////////////////////////////
#include <Windows.h>
#include <atldef.h>
#include <atlstr.h>
//
// Test function exported from the DLL
//
extern "C" HRESULT WINAPI SayHello(PCWSTR name)
{
//
// Check for null input string pointer
//
if (name == nullptr)
{
return E_POINTER;
}
try
{
//
// Build a greeting message and show it in a message box
//
CString message;
message.Format(L"Hello %s from the native DLL!", name);
MessageBox(nullptr, message, L"Native DLL Test", MB_OK);
// All right
return S_OK;
}
//
// Catch exceptions and convert them to HRESULT codes
//
catch (const CAtlException& ex)
{
return static_cast<HRESULT>(ex);
}
catch (...)
{
return E_FAIL;
}
}
Add this NativeClient.cpp C++ source code in the client test project:
///////////////////////////////////////////////////////////////////////////////
//
// NativeClient.cpp -- EXE Test Client Code
//
///////////////////////////////////////////////////////////////////////////////
#include <Windows.h>
//
// Prototype of the function to be loaded from the DLL
//
typedef HRESULT (WINAPI *SayHelloFuncPtr)(PCWSTR /* name */);
//
// Simple RAII wrapper on LoadLibrary()/FreeLibrary().
//
class ScopedDll
{
public:
//
// Load the DLL
//
ScopedDll(PCWSTR dllFilename) throw()
: m_hDll(LoadLibrary(dllFilename))
{
}
//
// Unload the DLL
//
~ScopedDll() throw()
{
if (m_hDll)
{
FreeLibrary(m_hDll);
}
}
//
// Was the DLL loaded successfully?
//
explicit operator bool() const throw()
{
return (m_hDll != nullptr);
}
//
// Get the DLL handle
//
HINSTANCE Get() const throw()
{
return m_hDll;
}
//
// *** IMPLEMENTATION ***
//
private:
//
// The wrapped raw DLL handle
//
HINSTANCE m_hDll;
//
// Ban copy
//
private:
ScopedDll(const ScopedDll&) = delete;
ScopedDll& operator=(const ScopedDll&) = delete;
};
//
// Display an error message box
//
inline void ErrorMessage(PCWSTR errorMessage) throw()
{
MessageBox(nullptr, errorMessage, L"*** ERROR ***", MB_OK | MB_ICONERROR);
}
//
// Test code calling the DLL function via LoadLibrary()/GetProcAddress()
//
int main()
{
//
// Return codes
//
static const int kExitOk = 0;
static const int kExitError = 1;
//
// Load the DLL with LoadLibrary().
//
// NOTE: FreeLibrary() automatically called thanks to RAII!
//
ScopedDll dll(L"NativeDll.dll");
if (!dll)
{
ErrorMessage(L"Can't load the DLL.");
return kExitError;
}
//
// Use GetProcAddress() to access the DLL test function.
// Note the *undecorated* "SayHello" function name!!
//
SayHelloFuncPtr pSayHello
= reinterpret_cast<SayHelloFuncPtr>(GetProcAddress(dll.Get(),
"SayHello"));
if (pSayHello == nullptr)
{
ErrorMessage(L"GetProcAddress() failed.");
return kExitError;
}
//
// Call the DLL test function
//
HRESULT hr = pSayHello(L"Connie");
if (FAILED(hr))
{
ErrorMessage(L"DLL function call returned failure HRESULT.");
return kExitError;
}
//
// All right
//
return kExitOk;
}
Build the whole solution (both the .EXE and the .DLL) and run the native .EXE client.
This is what I get on my computer:
It works without modifications and with the undecorated function name (just SayHello) on both x86 and x64 builds.
__stdcall is not supported (and is ignored) on x64. Quoting MSDN:
On ARM and x64 processors, __stdcall is accepted and ignored by the compiler; on ARM and x64 architectures, by convention, arguments are passed in registers when possible, and subsequent arguments are passed on the stack.
The calling convention on x64 is pretty much __fastcall.
Since the calling conventions and name decoration rules on x86 and x64 differ, you have to abstract this somehow. So your idea with #if _WIN64 goes in the right direction.
You can examine x86 calling conventions and your needs and perhaps devise a macro which could automate the name selection process.
As you can tell, in 64-bit Windows names are not decorated.
In 32-bit __cdecl and __stdcall symbols, the symbol name is prepended by an underscore. The trailing '#8' in the exported name for the 32-bit version of your example function is the number of bytes in the parameter list. It is there because you specified __stdcall. If you use the __cdecl calling convention (the default for C/C++ code), you won't get that. If you use __cdecl, it makes it much easier to wrap GetProcAddress() with something like:
#if _WIN64
#define DecorateSymbolName(s) s
#else
#define DecorateSymbolName(s) "_" ## s
#endif
then just call with
pfnConnect = GetProcAddress(hDLL, DecorateSymbolName("Connect"));
pfnOtherFunc = GetProcAddress(hDLL, DecorateSymbolName("OtherFunc"));
or something similar (error checking omitted in example).
To do this, remember to declare your exported functions as:
__declspec(dllexport) long __cdecl Connect(char * name, long size);
__declspec(dllexport) long __cdecl OtherFunc(int someValue);
In addition to being easier to maintain, if during development the signature of an exported function changes, you don't have to screw around with your #define wrappers.
Downside: if during development the number of bytes in a given function's parameter list changes, it will not be caught by the application importing the function because the changing the signature will not change the name. Personally, I don't think this is an issue because the 64-bit build would blow up under the same circumstances anyway as the names are not decorated. You just have to make sure your application is using the right version of the DLL.
If the user of the DLL is using C++, you can wrap things in a better way using C++ capabilities (wrap the entire explicitly-loaded library in a wrapper class, e.g.):
class MyDLLWrapper {
public:
MyDLLWrapper(const std::string& moduleName); // load library here
~MyDLLWrapper(); // free library here
FARPROC WINAPI getProcAddress(const std::string& symbolName) const {
return ::GetProcAddress(m_hModule, decorateSymbolName(symbolName));
}
// etc., etc.
private:
HMODULE m_hModule;
// etc.
// ...
};
There's actually a lot more you can do with a wrapper class like this, it's just an example.
On edit: since OP mentioned using PInvoke in the comments - if anyone decides to do this, do not forget to add CallingConvention = CallingConvention.Cdecl in the [DllImport] declaration when using PInvoke. __cdecl might be the default for unmanaged C/C++, but is not the default for managed code.
For Win32 build:
If you use __stdcall, you will get something like this (dumped with dumpbin /exports):
__declspec(dllexport) int __stdcall
->
ordinal hint RVA name
1 0 00001240 _F1#0 = _F1#0
2 1 0000124D _F2#0 = _F2#0
And you have to use GetProcAddress("_F1#0") to locate the function pointer.
If you use __cdecl, you will get something like this:
__declspec(dllexport) int __cdecl
->
ordinal hint RVA name
1 0 00001240 F1 = _F1
2 1 0000124D F2 = _F2
And you can use GetProcAddress("F1") to locate the function pointer.
BTW, if you add a XXX.def file to your Visual Studio project. One more link option will be silently added to your linker command line /DEF:"XXX.def" in the All Options window. And if you change your .def file name later for whatever reason, this link option doesn't change accordingly. You need to manually change the def file name in the project properties window.
I'm trying to get an DllExport from vb.net to unmanaged c++ working.
I'm using Robert Giesecke's Unmanaged Exports with Visual Studio 2012 and tried to follow this very helpful hints. I copy the dlls from the .Net project by an post build action in the directory where my *.cpp and *.h files reside.
I checked my dll with dumpbin /EXPORTS Nugget.Discovery.dll and it tells me that there are exports:
File Type: DLL
Section contains the following exports for \Nugget.Discovery.dll
00000000 characteristics
52554A05 time date stamp Wed Oct 09 14:20:21 2013
0.00 version
0 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
0 0 0000532E StartAnnouncing
1 1 0000533E StopAnnouncing
Summary
2000 .reloc
4000 .rsrc
2000 .sdata
4000 .text
But if I try to import it in the cpp file with
#import "Nugget.Discovery.dll"
void StartAnnouncing(int serial);
I get one IntelliSense error and one error after I try to compile:
IntelliSense: cannot open source file "Debug/Nugget.Discovery.tlh"
error C1083: Cannot open type library file: 'nugget.discovery.dll': Fehler beim Laden der Typbibliothek/DLL.
Any idea what I'm doing wrong?
Best regards!
Stefan
As part of DllExport, a .lib file is generated. You can use that to use the normal C++ linker instead of LoadLibrary/GetProcAddress.
Starting from the managed code you posted, on the native side:
extern CALLBACK void StartAnnouncingType(int serial);
extern CALLBACK int TestType(void);
int _tmain(int argc, _TCHAR* argv[])
{
int test = TestPtr();
StartAnnouncingPtr(1);
}
In the settings of you unmanaged project, add Nugget.Discovery.lib to the project properties: Configuration Properties->Linker->Input. And copy the Nugget.Discovery.dll to the output directory.
Ok, thanks to Hans Passant I came to this solution:
This is my code on the managed side:
Imports System.Runtime.InteropServices
Imports RGiesecke.DllExport
Public NotInheritable Class Beacon
Private Sub New()
End Sub
Private Shared _nuggetAnnouncement As NuggetAnnouncement
' ReSharper disable UnusedMember.Local
''' <remarks>Cannot be called from managed code!</remarks>
<DllExport("StartAnnouncing", CallingConvention.StdCall)>
Private Shared Sub StartAnnouncingNative(serial As Integer)
StartAnnouncing(serial)
End Sub
''' <remarks>Cannot be called from managed code!</remarks>
<DllExport("Test", CallingConvention.StdCall)>
Private Shared Function TestNative() As Integer
Return Test()
End Function
' ReSharper restore UnusedMember.Local
Public Shared Sub StartAnnouncing(serial As Integer)
'do something
End Sub
Public Shared Function Test() As Integer
Return 42
End Function
End Class
Interesting is, that I cannot call functions that are marked with <DllExport> from managed code (even if they are Public).
And this is the code on the native side:
typedef void (CALLBACK* StartAnnouncingType)(int);
typedef int (CALLBACK* TestType)(void);
int _tmain(int argc, _TCHAR* argv[])
{
HINSTANCE dllHandle = NULL;
StartAnnouncingType StartAnnouncingPtr = NULL;
TestType TestPtr = NULL;
wchar_t dllNameWide[64];
int size = mbstowcs(dllNameWide, "Nugget.Discovery.dll", sizeof(dllNameWide));
dllHandle = LoadLibrary(dllNameWide);
if (NULL != dllHandle)
{
//Get pointer to our function using GetProcAddress:
StartAnnouncingPtr = (StartAnnouncingType)GetProcAddress(dllHandle,"StartAnnouncing");
TestPtr = (TestType)GetProcAddress(dllHandle,"Test");
int test;
if (NULL != TestPtr) test = TestPtr();
int serial = 1;
if (NULL != StartAnnouncingPtr) StartAnnouncingPtr(1);
//Free the library:
FreeLibrary(dllHandle);
}
}
Are there any other better solutions?
Ciao!
Stefan
How can i demangle name in MSVC? There's abi::__cxa_demangle function in gcc. In MSDN i've found UnDecorateSymbolName:
http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms681400%28v=vs.85%29.aspx
Unfortunately, this function can't undecorate even such symbol:
#include <Windows.h>
#include <DbgHelp.h>
#include <cstdlib>
#include <iostream>
#include <typeinfo>
int main()
{
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
if (!SymInitialize(GetCurrentProcess(), NULL, TRUE))
{
std::cout << "SymInitialize returned error: " << GetLastError() << '\n';
return EXIT_FAILURE;
}
class Foo {};
Foo instance;
const char* decorated_name = typeid(instance).name();
char undecorated_name[1024];
if (!UnDecorateSymbolName(decorated_name, undecorated_name, sizeof(undecorated_name) / sizeof(*undecorated_name), UNDNAME_COMPLETE))
{
std::cout << "UnDecorateSymbolName returned error: " << GetLastError() << '\n';
return EXIT_FAILURE;
}
std::cout << "Decorated name: " << decorated_name << '\n'
<< "Undecorated name: " << undecorated_name << '\n';
}
Output
Decorated name: ?AVFoo#?4?main#
Undecorated name: ?AVFoo#?4?main#
If i am doing it wrong?
I've heard somewhere about _unDName function, but i can't find any example with it. In which header file it is defined?
Visual studio already shipped a utility called undname. For my VS2010 and VS2013 install, it's installed to
%VSINSTALL%\vc\bin directory.
And for x64 platform, in
%VSINSTALL%\vc\amd64\bin directory.
The example usage is:
D:\work\VS2013>undname "?static_x#?1??getX##YAAAUX##XZ#4U2#A"
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.
Undecoration of :- "?static_x#?1??getX##YAAAUX##XZ#4U2#A"
is :- "struct X `struct X & __cdecl getX(void)'::`2'::static_x"
Another way to get the demangled name is use /FAsc /Faoutput.asm compiler option, which will produce the assembly list, in which each mangled name is commented with it's demangled name. See This link for reference.
It seems UndecorateSymbolName/__unDName can't handle function-local names. If you move the class definition to the global scope, .name() will return already demangled name (use .raw_name() to get the mangled name).
To demangle the (global) raw name manually, you need two changes to your code:
1) skip the leading period in the mangled name (i.e. start at the '?').
2) instead of 0, use the flag value 0x2800 (UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY).
I found this out from the CRT sources of VS2012:
if ((pTmpUndName = __unDName(NULL, (this->_M_d_name)+1, 0,
&_malloc_base, &_free_base,
UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY)) == NULL)
Update for Visual Studio 2019.
(1) typeid(instance).name() - used in the request - already returns the undecorated name: further demangling is not required in this case
(2) the command UNDNAME.EXE, provided in the bin folders, does not work correctly, even if we take off the initial dot. For example ".?AVFoo#?1??main##YAHXZ#" is unmangled as " ?? int __cdecl main(void)'::2'::AVFoo", giving an invalid name AVFoo. The correct name must be Foo
(3) the dbghelp API UnDecorateSymbolName() does not work either
(4) the correct code can be deduced from the assembly generated by the compiler for the directive typeid()
Here is a little program which will undecorate correctly all the C++ mangled symbols:
// compile as: cl -W3 und.c
#include <windows.h>
#include "dbghelp.h"
#include <stdio.h>
#pragma comment(lib, "dbghelp.lib")
extern char *__unDName(char*, const char*, int, void*, void*, int);
int
main(int argc, char **argv)
{
const char *decorated_name = 0;
char undecorated_name[1024];
if (argc == 2) decorated_name = argv[1];
else {
printf("usage: %s <decorated_name>\n", argv[0]);
return 1;
}
__unDName(undecorated_name, decorated_name+1, 1024, malloc, free, 0x2800);
printf("Decorated name: %s\n", decorated_name);
printf("Undecorated name: %s\n", undecorated_name);
return 0;
}
For example:
und ".?AVFoo#?1??main##YAHXZ#"
Decorated name: .?AVFoo#?1??main##YAHXZ#
Undecorated name: class int __cdecl main(void)'::2'::Foo
UndecorateSymbolName also does not work as my expectation. If you want to iterate over members of struct or class, use boost fusion to implement reflection in C++.