I have a program written in C on LINUX environment. I have another wrapper application written in C++ on Windows.

I have a DLL of my C code which I cannot change/modify (I also have access to the original source code) but without exports. I cannot modify the original source code so I have just added a a DEF file to export the functions that I need to use in my wrapper.

My question is, can I use this DLL now without recompiling my C code to incorporate the DEF file? Because I have a lot of problems recompiling because of the different OS's that each program was developed on.

My C code returns 100+ errors when recompiled (as C) on MS Visual Studio because, for example, uint8_t illegal use of type and undefined memcmp..etc.

Any help is appreciated.

Recommended Answers

All 8 Replies

> can I use this DLL now without recompiling my C code

No.

The ABIs (an ABI specifies things like data size, alignment, calling convention, binary formats of programs, object files and libraries, name decoration, exception propagation and so on) are different.

>>My question is, can I use this DLL now without recompiling my C code to incorporate the DEF file?

Yes, as far as I know, it shouldn't be a problem. (btw, I know that I am contradicting vijayan121 on this, he may be right, but I think not, I think he is being a bit over-cautious, ABI shouldn't be a problem here)

Because your DLL is entirely written and compiled in C, binary compatibility should not be an issue. Of course, if your C code uses fixed-size integer type (like uint8_t and such) then you need your C++ code to use the same types, and be sure that they correspond (in endian-ness for example). The calling convention is easy to determine, if there is no special specification of the calling convention in the C source, then it is "__cdecl" which is also the default in C++ (but you're better off specifying it explicitly). Binary footprints and alignment for types and structs in C are essentially fixed (given that the OS and target platform is the same for both compilations). Name decoration is not done in C. And if you have exceptions propagating across the module boundary (an exception thrown in the DLL propagating into the application code), then that is an error regardless of what languages or compilers or settings or whatever, exceptions should never be allowed to propagate across a module boundary (that's a fundamental rule when programming DLLs). Now, if your DLL is actually programmed in C++, ABI issues are certainly a concern (although it is possible to overcome them, but hard), but for C code they are not.

Now, about the "how", you won't be able to use your DLL in the usual way (with load-time linking using an import library ".lib" file), you will have to use run-time linking using the "LoadLibrary()", "GetProcAddress()" and "FreeLibrary()" functions. Here is a msdn description of how to do that.

Before, you should make sure that the DLL in question has its functions exported correctly. For that, you can use the "dumpbin.exe" program in windows (should ship with Visual Studio / C++). With the command "dumpbin.exe /EXPORTS my_file.dll" you should see the list of exported functions.

If your DLL doesn't have the exports showing up when listing them with dumpbin (which would make it a very useless DLL), then you will for sure need to recompile from the source files. Now, if you can't get it to work with MSVC, then you can try MinGW (with GCC) to generate the DLL with exported symbols (using your DEF file). That DLL should be loadable (at run-time, with LoadLibrary) from your MSVC C++ code (of course, the names, prototypes and calling convention has to match perfectly for every function). Your DLL must have been originally compiled by some kind of Windows compiler (if not MSVC, there is a good chance in was GCC), so use that compiler to recompile the DLL with the exports.

>>Yes, as far as I know, it shouldn't be a problem. (btw, I know that I am contradicting vijayan121 on this, he may be right, but I think not, I think he is being a bit over-cautious, ABI shouldn't be a problem here)

>>If your DLL doesn't have the exports showing up when listing them with dumpbin (which would make it a very useless DLL), then you will for sure need to recompile from the source files

Aren't those contradictionary statements?

>>Aren't those contradictionary statements?

Ok, let me restate that more clearly. If all the functions you need are exported already from the DLL, then you can use it without any major issues, but you will need to load it at run-time (e.g. with LoadLibrary) if you don't have an import library for it.

My first statement was more in response to vijayan's assessment about ABI being an issue here, which I don't think it is. But, of course, with or without ABI issues, you certainly need the required functions to be exported from the DLL, otherwise it is not usable in any context. My first statement was thus starting from the assumption that the DLL has functions exported from it, and my second statement was in response to the OP's vague statement that the DLL was "without exports", which certainly entails a recompilation in order to export the required functions.

> I have a program written in C on LINUX environment.

The Linux environment (AFAIK) is emulated on Windows by Cygwin; cygwin dll and msvcrt.dll cannot be used together in the same program. The ABIs are different.
http://www.cygwin.com/faq/faq.programming.html#faq.programming.msvcrt-and-cygwin

You need to recompile. Or build the program (which uses the dll) with the Cygwin tool-chain.

> And if you have exceptions propagating across the module boundary
> (an exception thrown in the DLL propagating into the application code),
> then that is an error regardless of what languages or compilers or settings or whatever,
> exceptions should never be allowed to propagate across a module boundary
> (that's a fundamental rule when programming DLLs)

If it were so, libstdc++.so or libboost_thread.so would be examples of fundamental programming errors. As in everything else, what is required is that the library and the program using the library have to be ABI compatible.

Thank you for your responses. I have tried using dumpbin to generate the proper exports but I get a linker error:

Unable to Locate Component
This application has failed to start because mspdb100.dll was not found. Re-installing the application may fix this problem.

So I turned to MinGW. I couldn't get MSYS to install on my system but I hope it's not a problem since I can use MinGW packages just fine. I use 64-bit Windows Vista and MSYS doesn't work on it.


The man pages on MinGW don't provide enough examples, and there aren't good guides online.

Because I got some problems, I went back to using a simpler program. I have explained my program before in this thread. That program works just fine, but I need to do it again using MinGW and not Visual Studio this time. Let me explain:

I have the following simple program:

/*addition.c*/
#include "addition.h"
#include <stdio.h>
 
int add(int x, int y){
return x+y;
}
/*addition.h*/
int add(int x, int y);
/*mydef.def*/
LIBRARY	"addition"
EXPORTS
		add

All of this is created by hand, meaning just notepad. No Visual Studio or GCC involved yet.

To compile that code, in the command line I wrote this:

gcc -c addition.c
dlltool -d mydef.def -e exports.o -l addition.lib addition.o
gcc addition.o exports.o -o addition.dll

My addition.dll is then created and I copied it to C:\


I then went to Visual Studio and created a C++ Project with the following:

/*wrapper.cpp*/
#include <Windows.h>
#include <iostream>
 
using namespace std;
 
int CallMyDLL(void) 
{ 
    /* get handle to dll */ 
   HINSTANCE hGetProcIDDLL = LoadLibrary("C:\addition.dll"); 
 
   /* get pointer to the function in the dll*/ 
   FARPROC lpfnGetProcessID = GetProcAddress(HMODULE (hGetProcIDDLL),"add"); 
 
   /* 
      Define the Function in the DLL for reuse. This is just prototyping the dll's function. 
      A mock of it. Use "stdcall" for maximum compatibility. 
   */ 
   typedef int (__stdcall * pICFUNC)(int, int); 
 
   pICFUNC add; 
   add = pICFUNC(lpfnGetProcessID); 
 
   /* The actual call to the function contained in the dll */ 
   int intMyReturnVal = add(4, 5); 
 
   /* Release the Dll */ 
   FreeLibrary(hGetProcIDDLL); 
 
   /* The return val from the dll */ 
    returnintMyReturnVal; 
} 
 
int main(){
int res=0;
res = CallMyDLL();
cout << "The result is ";
cout << res;
}

When running this program, I get an Unhandled Exception at 0x00000000 problem. Note that I have used LoadLibrary before and was able to correctly call the function needed for my previous thread, but that was created fully using MS Visual Studio (def file, c files, and compilation is done completely using Visual Studio). I need to do this via MinGW this time.

I have a feeling that I'm creating or linking the .def file incorrectly using dlltool, because I have gotten the Unhandled Exception before when I did not have the Def file.

Well, the "unhandled exception" is due to the fact that the function pointer obtained from the "GetProcAddress" is NULL, i.e., it is not found in the DLL. You should check for the pointer being NULL and I'm sure it will confirm that.


I didn't initially understand that you generated the DLL with Cygwin (Linux environment), I'm not very familiar with it. Certainly, if your DLL was generated like that, you cannot load it from an MSVC program, at least, not easily. As said, they are not compatible.

Your DLL has to be compiled with a native Windows compiler (not a *nix compiler wrapped in an emulated environment like Cygwin). MinGW should work for that. To install it, you can either simply install CodeBlocks (with mingw) which will do the install of MinGW automatically, or you can install it manually (which means to unzip the latest release of MinGW to some folder, and then add the path "C:\Path\to\MinGW\bin" to the PATH environment variable in Windows).


@vijayan121:
>>If it were so, libstdc++.so or libboost_thread.so would be examples of fundamental programming errors. As in everything else, what is required is that the library and the program using the library have to be ABI compatible.

Comparing *nix shared-objects with Windows DLLs is comparing apples and oranges, they are completely different in almost every way (linking model, dynamic loading model, ABI specification, memory model, etc.). They just look similar to a novice, or if you don't care about the details. As this discussion is on Windows, then the rule of not propagating exceptions across a DLL boundary is totally valid and very well known (any system library that throws exceptions will only throw Windows SEH exceptions through DLL boundaries, never native C++ exceptions, although it might get automatically translated after the boundary is crossed, but if you do your own DLL, you have to either do the translations before and after the boundary or use an error-code mechanism instead). As for *nix, the boundary between shared-objects and applications is almost non-existent, by design, which makes shared-objects a lot easier to deal with then windows DLLs.

> The man pages on MinGW don't provide enough examples, and there aren't good guides online.

Have you looked at the MinGW wiki?
For instance, this entry: http://www.mingw.org/wiki/MSVC_and_MinGW_DLLs


> Comparing *nix shared-objects with Windows DLLs is comparing apples and oranges,
> they are completely different in almost every way
> (linking model, dynamic loading model, ABI specification, memory model, etc.).

The Unix/ELF and Windows/PE dynamic linking differ in several interesting ways; for example, Unix/ELF is far more flexible, while Windows/PE is more efficient. But these differences are largely irrelevant to how exceptions are propagated through the call stack.


> any system library that throws exceptions will only throw Windows SEH exceptions
> through DLL boundaries, never native C++ exceptions

msvcrt.dll throws C++ exceptions across DLL boundaries. For instance, it exports
void * __cdecl operator new(unsigned int) ; which throws std::bad_alloc.


> any system library ... ... but if you do your own DLL,
> you have to either do the translations before and after the boundary
> or use an error-code mechanism instead

This is absolute nonsense. As long as the DLL and the program using the DLL do not violate the basics of the separate compilation model - compile and link with the same tool-chain, with the same or compatible switches - and do not violate fundamental rules like the ODR - for instance by having two different instances of the CRT, one for the DLL and another for the program using the DLL - C++ exceptions can be seamlessly propagated across DLL boundaries. In fact, _CxxThrowException() and friends only look at the call stack; they do not even know that a DLL boundary was crossed.

May be an example would help: The attachment is a Microsoft C++ (2010) work-space that illustrates throwing an exception in DLL B which is caught and rethrown by DLL A which is then caught and handled in main()

The source files posted here for convenience:

dll_B files:

/////////  b.h  ////////////////////////
#ifdef DLL_B_IMPL_

__declspec(dllexport) void foo() ;

#else

__declspec(dllimport) void foo() ;

#endif

////////// b.cc ///////////////////
#define DLL_B_IMPL_
#include "b.h"
#include <iostream>
#include <stdexcept>

__declspec(dllexport) void foo()
{
    try
    {
        throw std::runtime_error( "'exception thrown by dll_B'" ) ;
    }
    catch( const std::runtime_error& e )
    {
        std::cout << "catching exception " << e.what() << " in dll_B\n\tand throwing it again\n\n" ;
        throw ;
    }
}

dll_A files:

/////////  a.h  ////////////////////////
#ifdef DLL_A_IMPL_

__declspec(dllexport) void bar() ;

#else

__declspec(dllimport) void bar() ;

#endif

////////// a.cc ///////////////////
#define DLL_A_IMPL_
#include "a.h"
#include "..\dll_b\b.h"
#include <iostream>
#include <stdexcept>

__declspec(dllexport) void bar()
{
    try
    {
        foo() ;
    }
    catch( const std::runtime_error& e )
    {
        std::cout << "catching exception " << e.what() << " in dll_A\n\tand throwing it again.\n\n" ;
        throw ;
    }
}

main.cc:

#include <iostream>
#include <stdexcept>
#include "..\dll_a\a.h"

int main()
{
    try
    {
        bar() ;
    }
    catch( const std::runtime_error& e )
    {
        std::cout << "catching exception " << e.what() << " in main.\n" ;
    }
}

Output:

catching exception 'exception thrown by dll_B' in dll_B
        and throwing it again

catching exception 'exception thrown by dll_B' in dll_A
        and throwing it again.

catching exception 'exception thrown by dll_B' in main.
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.