I have a basic solution file (.sln) where I was able to reproduce a problem I have been facing recently.
It contains 3 projects:
1.) MathTest.lib - containing methods that might cause a mathematical error, like acos(1.1).
2.) MathTestDll.dll - calls the methods from the above lib.
3.) UnitTest.exe - calls the exported method in the DLL that should cause the error.
What I'm trying to do is fairly simple:
The following code contains the _matherr() routine and should ideally link fine. The call to acos() with a value of 1.1 is invalid (invalid input) and should cause an error which should be handled by the implemented _matherr() handler. I hope I'm right about the behavior of _matherr(). Please let me know.
MathTest.lib
#include "MathTest.h"
#include <iostream>
#include <math.h>
int _matherr(_exception* _Except)
{
std::cout << _Except->name;
return -1;
}
void MathTest::ThrowMatherr(float par)
{
float result = acos(par);
std::cout << result;
}
This 'ThrowMatherr()' method will be called by the DLL as follows:
MathTestDll.dll
void MatherrCaller::CauseMatherr()
{
MathTest* mathtest = new MathTest();
mathtest->ThrowMatherr(1.1);
}
which is then exported as:
extern "C" __declspec(dllexport) void CallThisToCauseMatherr();
void CallThisToCauseMatherr()
{
MatherrCaller* caller = new MatherrCaller();
caller->CauseMatherr();
}
This exported method will be called by a simple test.
UnitTest.exe
#include <windows.h>
typedef void (*METHODTOCALL)();
int main()
{
HMODULE module = LoadLibrary((LPCSTR)"..\\Debug\\MatherrTestDll.dll");
if(module != NULL)
{
METHODTOCALL ProcAdd = (METHODTOCALL) GetProcAddress(module, (LPCSTR)"CallThisToCauseMatherr");
if (NULL != ProcAdd)
{
(ProcAdd)();
}
FreeLibrary(module);
}
return 0;
}
All methods get called fine. But the acos() method which has been passed invalid input never calls the _matherr() error handler. Please let me know how I can fix this.
I had to make the question detailed to get my point through. Please don't mind.
It is explicitly mentioned in the documentation for _matherr:
For special error handling, you can
provide a different definition of
_matherr. If you use the dynamically linked version of the C run-time
library (Msvcr90.dll), you can replace
the default _matherr routine in a
client executable with a user-defined
version. However, you cannot
replace the default _matherr routine
in a DLL client of Msvcr90.dll.
You'll need to put the override in the EXE module. Alter your unit test to accommodate this.
Related
I have some code that is compiled as a shared library and used with a universal driver, which can be used with other shared libraries that are specific to a particular application.
My question pertains to obtaining some sort of indicator of the name of the binary containing a code that lives in that shared library.
For example, let's say I have 3 files, the first is driver.cpp, the universal driver:
#include "interface.h"
#include <stdio.h>
int main(int argc, char *argv[]) {
//perform a function from the shared library
std::cout << foobar() << std::endl;
}
The second is sharedlibrary.cpp, the specific implementation for one case of many:
#include "interface.h"
char* foobar() {
return x;
}
Where x is some indicator that this function is defined in sharedlibrary.cpp, or that this function is linked from sharedlibrary.so, or the current stack frame is using the specific binary rather than just being included in driver.cpp.
The last file is interface.h, which provides the interface to the library via extern "C"
extern "C" {
char foobar();
}
I would like to reiterate, for clarity, that I am looking for some indication that this function is being linked from sharedlibrary.so. Many solutions looking for runtime filenames give the executable name using either argv[0] or readlink(), but I have no control over the actual naming of driver.cpp or its executable name. Rather, I can distribute sharedlibrary.so, and would like to be able to use its name from within itself, if possible.
If it helps, I know that a microsoft-specific solution could be to use AfxGetApp()->m_pszAppName to obtain the DLL name. However, I am looking for a linux solution that does not necessarily need to be portable.
EDIT: I do not know or control the names of driver.cpp, sharedlibrary.cpp, or sharedlibrary.h at compile time. I wish to discover the name of sharedlibrary.cpp at run time.
The updated sharedlibrary.cpp with x replaced with the solution looks like this
#include "interface.h"
#include <dlfcn.h>
void func() {
//Some function that is defined in sharedlibrary.cpp
}
char* foobar() {
Dl_info DlInfo;
if(!dladdr((void*)func, &DlInfo)) {
return "default_name";
}
return DlInfo.dli_fname;
}
Obtaining filename at runtime for a shared library c++
My question pertains to obtaining some sort of indicator of the name of the binary containing a code that lives in that shared library.
You can use int dladdr(void *addr, Dl_info *info. It fills a following structure for you:
typedef struct {
const char *dli_fname; /* Pathname of shared object that contains address */
void *dli_fbase;
const char *dli_sname;
void *dli_saddr;
} Dl_info;
You can pass the address of a function exported by the shared library as the argument addr. Or within such function, you could use the instruction pointer value of the current stack frame - if you know how to obtain it.
I believe you must link with the libdl library.
You can use the buildsystem to generate the dynamic library name for linking and preprocess that inside of a header with a function that return a defined macro, in cmake you can see how to do that here.
Then you use the configured-file to return the defined value in a function that's exported from within the dll.
#include "library_name_macro.h"
auto __dllexport libraryName() -> std::string { return LIBRARY_NAME_MACRO; }
I hope, I have understood your question correctly. I hope my answer helps. You know the shared library name, you link that shared library to your program, Later in run time you want to figure out whether a particular function is present in library or not and this logic should be part of shared library itself.
Let's take an example that you have shared library called librandom.so, You have linked this library to your application. You can implement the following function in a librandom.so library, You can pass function name which you want to check whether it is present or not. I have not tested this code, there may be errors. The idea I am proposing is library loads itself again to check whether the function is present when this function is called. May not be ideal method but should serve your purpose.
int isFuncPresent(char funcName[])
{
int isFuncFound = 1;
void *lib_handle;
int x;
char *error;
lib_handle = dlopen("librandom.so", RTLD_LAZY);
if (!lib_handle)
{
fprintf(stderr, "%s\n", dlerror());
isFuncFound = 0;
}
fn = dlsym(lib_handle, funcName);
if ((error = dlerror()) != NULL)
{
fprintf(stderr, "%s\n", error);
isFuncFound = 0;
}
dlclose(lib_handle);
return isFuncFound;
}
Okay so I'm coming dangerously close to a repost here but my situation is a little bit different than the numerous other posters about this function. I am interfacing with a DLL that was written way back in the day and all I have is the file. I don't have a .lib file so I'm using the LoadLibrary and GetProcessAddress functions. I followed the tutorial on the MSDN website to get the basic structure. the DLL is located in the project folder. it compiles. at run time, I am getting a numerical value for "hinstLib" so I'm assuming the DLL was found. I am getting a null value for "ProcAdd" variable. Other posters had there issues resolved by putting extern C in the DLL functions but I don't really have that option. not to mention, to my knowledge this DLL was written in plain C. I do have an interface document and am quite sure I have the function name correct (replaced with a generic example for these purposes). I honestly didn't run anything past the ProcAdd assignment because it came out NULL. Any thoughts as to why this is giving me a 0 value for the function assignment would be great appreciated. Note: unfortunately due to various reasons I can't upload the DLL.
#include <iostream>
#include "stdafx.h"
#include "Windows.h"
#include <stdio.h>
typedef int(__cdecl *MYPROC)(LPWSTR);
using namespace std;
int main()
{
HINSTANCE hinstLib;
MYPROC ProcAdd;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
hinstLib = LoadLibrary(TEXT("dllName.dll"));
if (hinstLib != NULL)
{
ProcAdd = (MYPROC) GetProcAddress(hinstLib, "funcName");
// If the function address is valid, call the function.
if (NULL != ProcAdd)
{
fRunTimeLinkSuccess = TRUE;
//(ProcAdd) (L"Message sent to the DLL function\n");
}
// Free the DLL module.
fFreeResult = FreeLibrary(hinstLib);
}
// If unable to call the DLL function, use an alternative.
if (! fRunTimeLinkSuccess)
printf("Message printed from executable\n");
return 0;
}
Compilers usually mangle function names, then a function named funcName may appear inside the DLL with a name funcName#16 , for example... It depends on calling convention and are important for a function to be called properly. For __cdecl calling convention you probably need _funcName :-) .
I am getting warnings when I am trying to include <boost/thread.hpp> in C++ Builder. For every unit I am including it, C++ Builder shows up these 2 lines:
thread_heap_alloc.hpp(59): W8128 Can't import a function being defined
thread_heap_alloc.hpp(69): W8128 Can't import a function being defined
Already tried some things, nothing worked though.
It compiles correctly, however, it's getting on my nerves. Why is this message being shown?
The lines are:
#include <boost/config/abi_prefix.hpp>
namespace boost
{
namespace detail
{
inline BOOST_THREAD_DECL void* allocate_raw_heap_memory(unsigned size)
{
void* const eap_memory=detail::win32::HeapAlloc(detail::win32::GetProcessHeap(),0,size);
if(!heap_memory)
{
throw std::bad_alloc();
}
return heap_memory;
}
inline BOOST_THREAD_DECL void free_raw_heap_memory(void* heap_memory)
{
BOOST_VERIFY(detail::win32::HeapFree(detail::win32::GetProcessHeap(),0,heap_memory)!=0);
}
where 59 is the { below the BOOST_THREAD_DECL, as is 69. Looks like BOOST_THREAD_DECL is not defined properly or mis-defined, trying to follow through the Boost code is not that easy.
This is Boost 1.39.
add #define BOOST_THREAD_USE_LIB before including the thread.hpp.
This is what I tested:
#define BOOST_THREAD_USE_LIB
extern "C"
{
namespace boost
{
void tss_cleanup_implemented( void )
{
/*
This function's sole purpose is to cause a link error in cases where
automatic tss cleanup is not implemented by Boost.Threads as a
reminder that user code is responsible for calling the necessary
functions at the appropriate times (and for implementing an a
tss_cleanup_implemented() function to eliminate the linker's
missing symbol error).
If Boost.Threads later implements automatic tss cleanup in cases
where it currently doesn't (which is the plan), the duplicate
symbol error will warn the user that their custom solution is no
longer needed and can be removed.*/
}
}
}
#include <boost/thread.hpp>
Then set 'Link with Dynamic RTL' and 'Link with Runtime Packages'.
This does a clean build and starts a thread properly.
Given two IDL definitions: (I'm only implementing a client, the server side is fixed.)
// Version 1.2
module Server {
interface IObject {
void Foo1();
void Foo2() raises(EFail);
string Foo3();
// ...
}
};
// Version 2.3
module Server {
interface IObject {
// no longer available: void Foo1();
void Foo2(string x) raises(ENotFound, EFail); // incompatible change
wstring Foo3();
// ...
}
};
(Edit Note: added Foo3 method that cannot be overloaded because the return type changed.)
Is it somehow possible to compile both stub code files in the same C++ CORBA Client App?
Using the defaults of an IDL compiler, the above two IDL definitions will result in stub code that cannot be compiled into the same C++ module, as you'd get multiple definition errors from the linker. The client however needs to be able to talk to both server versions.
What are possible solutions?
(Note: We're using omniORB)
(Adding answer from one Stefan Gustafsson, posted in comp.object.corba 2011-03-08)
If you look at it as a C++ problem instead of a CORBA problem, the
solution is C++ namespaces.
You could try to wrap the different implementations in different C++
namespaces.
Like:
namespace v1 {
#include "v1/foo.h" // From foo.idl version 1
}
namespace v2 {
#include "v2/foo.h" // from foo.idl version 2
}
And to be able to compile the C++ proxy/stub code you need to create C++
main files like:
// foo.cpp
namespace v1 {
#include "v1/foo_proxy.cpp" // filename depend on IDL compiler
}
namespace v2 {
#include "v2/foo_proxy.cpp"
}
This will prevent the C++ linker complaining since the names will be
different. Of course you
could run into problems with C++ compilers not supporting nested
namespaces..
A second solution is to implement the invocation using DII, you could
write a C++ class
class ServerCall {
void foo2_v1() {
// create request
// invoke
}
void foo2_v2(String arg) {
// create_list
// add_value("x",value,ARG_IN)
// create_request
// invoke
}
}
By using DII you can create any invocation you like, and can keep full
control of your client code.
I think this is a good idea, but I haven't been able to try it out yet, so there may lurk some unexpected surprises wrt to things no longer being in the global namespace.
What comes to my mind would be splitting the client code into separate libraries for each version.
Then you can select the correct client depending on the version to be used.
In a recent project we handled this by introducing a service layer with no dependency to the CORBA IDL.
For example:
class ObjectService
{
public:
virtual void Foo1() = 0;
virtual void Foo2() = 0;
virtual void Foo2(const std::string &x) = 0;
};
For each version, create a class derived from ObjectService and implement the operations by
calling the CORBA::Object. Each derived class must be in separate library.
In the client implementation, you only operate on instances of ObjectService.
CORBA::Object_var remoteObject=... // How to get the remote object depends on your project
ObjectService *serviceObject=0;
// create a service object matching the remote object version
// Again, this is project specific
switch (getRemoteObjectVersion(remoteObject))
{
case VERSION_1_2:
serviceObject=new ServiceObjectImpl12(remoteObject);
break;
case VERSION_2_3:
serviceObject=new ServiceObjectImpl23(remoteObject);
break;
default:
// No matching version found, throw exception?
break;
}
// Access remote object through service object
serviceObject->Foo2("42");
I've spent days reading and re-reading every tutorials I've found on the subject, and spent hours (and even days) browsing related questions here at SO, but I still can't get the following to work. Accept my apologies if this is a duplicate: chances are that I've seen and re-read many times the duplicate questions but couldn't understand the relevance of the answers to my problem. With that out of the way...
I'm trying to implement a plugin architecture for my Application. Plugins are compiled and installed as libraries. At run time, the Application then uses dlopen() / dlsym() to load and link to the plugin's functions.
The idea is that plugins (libraries) will implement a set of functions to return data to the main Application, or manipulate data passed from the Application.
In order to test this idea, I tried to implement a function (inside the plugin) that would return the (human readable) name of the plugin itself (as a std::string). I thought that would be something simple to start with.... :-/
Here is what I got so far:
// Plugin.cpp
extern "C" void plugin_name(std::string *name) {
name = new std::string("Example plugin name");
}
// Application.cpp
void* handle = dlopen("libplugin.so", RTLD_LAZY);
typedef void (*plugin_t)(std::string*);
dlerror(); // Reset errors.
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ... Some error handling code.
std::string my_plugin_name;
call_plugin_name(&my_plugin_name);
dlclose(handle);
// More code that displays my_plugin_name.
I've tried many different combinations, including one that seemed more straigtforward (but didn't work any better) where the plugin name is returned:
// Plugin.cpp
extern "C" std::string plugin_name(void) {
return std::string("Example plugin name");
}
I know I'm close: the code compiles and the Application stopped crashing ;)
However, I've got an empty space where I'd expect seeing the actual plugin name.
All the tutorials I've read so far go very quickly over the mechanism by which data is passed both ways: plugin <=> Application. What I'm trying to do with a "simple" std::string, I wish to do later with much more complex objects (i.e. a plugin function would take an object by reference and change some of its properties). The tutorials more or less all stop at the point of creating a pointer with dlsym() and do not give much examples on how to use this pointer.
So, how to do all that?
Another pertinent question: do I use a common header that I'd use both with the Application and with the plugin and where I'd define the function calls signature? How would I do this and how would that help?
The signature of a function is generated from its name and argument types (return value type doesn't matter). When you declare function with extern "C", C symbol naming scheme is used which apparently can't handle C++ types like std::string. That's why passing std::string as an arguments doesn't work.
I can't explain why returning std::string doesn't work. Maybe different calling conventions are used.
Anyway the correct way of importing C++ code from a shared library is to return pointers to C++ types from entry points. And this entry points have to have arguments with types available in C. (Entry point is a documented function exported from a shared library)
Here is a good article on basic aspects of loading C++ classes from shared libraries. This article will answer your question throughly.
Please note that there are pitfalls when using exceptions thrown from a shared library to the main applications. And with dynamic_cast of objects created inside a library. I've mentioned this topics so that you could be somewhat prepared when you face this problems.
[edit]
To make my answer more clear I'll add a couple of examples.
To get the plugin name you can use:
extern "C" const char * plugin_name() {
return "Example plugin name";
}
// main.cc:
void* handle = dlopen("libplugin.so", RTLD_LAZY);
// ...
typedef const char * (*plugin_t)();
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ...
std::string my_plugin_name(call_plugin_name());
// use it
To really use the plugin functionality you should declare a base class in a header:
// plugin.h
class Plugin {
public:
virtual void doStuff() = 0;
virtual ~Plugin() = 0;
};
// plugin.cc
Plugin::~Plugin() {
}
// myplugin.cc
class MyPlugin : public Plugin {
virtual void doStuff() {
std::cout << "Hello from plugin" << std::endl;
}
};
extern "C" Plugin *createMyPluginInstance() {
return new MyPlugin;
}
Try:
extern "C" void plugin_name(std::string **name) {
*name = new std::string("Example plugin name");
}
...
std::string *my_plugin_name;
call_plugin_name(&my_plugin_name);
As you are assigning a copy of the pointer you passed as the argument, not the one you intended to assign.
EDIT Here you go:
File main.cpp
#include <iostream>
#include <dlfcn.h>
#include <string>
// Application.cpp
int main() {
void* handle = dlopen("libplugin.so", RTLD_LAZY);
typedef void (*plugin_t)(std::string**);
dlerror(); // Reset errors.
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ... Some error handling code.
std::string *my_plugin_name;
call_plugin_name(&my_plugin_name);
dlclose(handle);
// More code that displays my_plugin_name.
std::cout << "Plugin name is " << *my_plugin_name << std::endl;
delete my_plugin_name;
return 0;
}
File plugin.cpp
#include <string>
extern "C" void plugin_name(std::string **name) {
*name = new std::string("example plugin name");
}
Just a word of warning. Although this compiles and runs, passing C++ types across the dll boundry is risky and the above code is just your code fixed enough to compile and run, it is not safe and has very explicit memory handling. You may want to attack the problem in a different way.
Please have a read of this question and its answers. There are many opportunities for incompatibilities across the shared lib boundaries in C++.