__declspec(dllexport) static linked libraries to dll - c++

I have a simple project setup for an OpenGL-project for University.
An API project where every library I want to use (GLEW, GLFW, GLM) is linked statically. These libraries should be combined with my own API code into one single DLL file.
The other project should only have one dependency, the DLL. Throw that DLL it should get access to the API code and all libraries linked inside the API.
My problem is that inside the API I have access to all functions of all libraries I linked. But inside the actual project which has the API as a dependency, I can call the functions and the compiler throws out no error (of cause) because the function declaration is in the linked header files but the linker does not find the function definition inside the DLL, which mean the build of the API does not export the linked libraries into the DLL.
In the API project, I also defined the necessary preprocessor definitions:
I defined _GLFW_BUILD_DLL:
from "glfw3.h" l. 233-245
/* GLFWAPI is used to declare public API functions for export
* from the DLL / shared library / dynamic library.
*/
#if defined(_WIN32) && defined(_GLFW_BUILD_DLL)
/* We are building GLFW as a Win32 DLL */
#define GLFWAPI __declspec(dllexport) <----- this one is active
#elif defined(_WIN32) && defined(GLFW_DLL)
/* We are calling GLFW as a Win32 DLL */
#define GLFWAPI __declspec(dllimport)
#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL)
/* We are building GLFW as a shared / dynamic library */
#define GLFWAPI __attribute__((visibility("default")))
#else
/* We are building or calling GLFW as a static library */
#define GLFWAPI
#endif
I defined GLEW_BUILD:
from "glew.h" l. 200-208
#ifdef GLEW_STATIC
#define GLEWAPI extern
#else
#ifdef GLEW_BUILD
#define GLEWAPI extern __declspec(dllexport) <---- this one is active
#else
#define GLEWAPI extern __declspec(dllimport)
#endif
#endif
Any help?
EDIT:
The linker options:
/OUT:"D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin\Debug\Win32\CGAPI.dll" /MANIFEST /NXCOMPAT /PDB:"D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin\Debug\Win32\CGAPI.pdb" /DYNAMICBASE "glew32s.lib" "glfw3.lib" "opengl32.lib" "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /IMPLIB:"D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin\Debug\Win32\CGAPI.lib" /DEBUG /DLL /MACHINE:X86 /INCREMENTAL /PGD:"D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin\Debug\Win32\CGAPI.pgd" /SUBSYSTEM:WINDOWS /MANIFESTUAC:NO /ManifestFile:"D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin-int\Debug\Win32\CGAPI.dll.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /LIBPATH:"D:\Programmierung\C++-Projekte\CG-Project\Dependencies\GLFW\Win32\lib-vc2019\" /LIBPATH:"D:\Programmierung\C++-Projekte\CG-Project\Dependencies\GLEW\lib\Release\Win32\" /TLBID:1
The one I added:
/WHOLEARCHIVE:glew32s.lib
/WHOLEARCHIVE:glfw3.lib
/WHOLEARCHIVE:opengl32.lib
EDIT 2:
So I had to download the source code from glew and build it myself and also I had to remove the .res file inside the option of my own glew build. Now the API builds successfully but the methods aren't part of the DLL. So nothing changed.

I don't fully understand your motivation for doing things this way. The common approach (that I'd also use) is shipping the OpenGL .dlls (I'm not aware of the licensing implications) along with yours.
However, even if this looks like an XY Problem, in order to achieve your goal, you could pass [MS.Docs]: /WHOLEARCHIVE (Include All Library Object Files) to the linker. In order to differentiate which .libs to include and which not, specify the flag once per each target library: /wholearchive:glew32.lib /wholearchive:glfw3.lib /wholearchive:glm.lib.
Check [SO]: Exporting symbols in static library that is linked to dynamic library (#CristiFati's answer) for more details.
As a note: the .libs must have been built with exportable symbols. If not, you'll have to export them "manually", using one of the other choices:
/EXPORT linker option
Module definition (.def) files
or rebuild the .lib as static but with exportable symbols. However, there's high chance it won't work OOTB (you'l probably need to modify a file or 2 or manually pass a compile flag), as it's not a normal way (most of the people would agree that's a NO-NO, especially for someone without a fairly deep know-how) of doing things.

Related

Auto link static libraries from header file

I'm writing a library for in-house use and I want to automate the linking of static library (.lib) files so users don't have to know all the libraries they need. We use Visual Studio 2015 so I've knocked something up using pragma which seems to work but I'm getting a lot or warnings, which I presume are caused by what I'm doing.
Warning LNK4221 This object file does not define any previously
undefined public symbols, so it will not be used by any link operation
that consumes this library
I include this code in all the public facing interface header files and extend it to include internal and external libraries necessary.
#ifndef GFXK_LIBS_NET_IMPORT__
#define GFXK_LIBS_NET_IMPORT__
#ifdef _DEBUG
#ifdef WIN32
#pragma comment( lib, "gfxk_net_Debug_Win32-v140.lib" )
#else
#pragma comment( lib, "gfxk_net_Debug_x64-v140.lib" )
#endif
#else
#ifdef WIN32
#pragma comment( lib, "gfxk_net_Release_Win32-v140" )
#else
#pragma comment( lib, "gfxk_net_Release_x64-v140" )
#endif
#endif /*_DEBUG*/
#endif /*GFXK_LIBS_NET_IMPORT__*/
My question is how do I do this nicely so that I can remove this hack job. I'm looking for something similar to what Boost does with it's auto linking, I can't find out how to do this. I'm not too worried about cross platform as library targets only Windows at this time.
You can specify .libs path in your project settings Configuration Properties -> Linker -> Input -> Additional dependencies. You may specify different settings for different configurations and platforms. That would be a clearer way.
But the warning won't appear after you add functions to your .lib and use them in your project. Warning is just saying that this pragma-lib statement is pointless as no functions are really imported.

Where is *.lib after generating *.dll with VC++?

I generated a *.dll with VC++. When I want to use it, a *.lib is required. But I cannot find it. How to get the *.lib? Thanks.
Unless you specify otherwise, the .lib will be generated in the same directory as the .DLL.
If you're getting a dll but not a lib, chances are pretty good that somehow or other you're not actually exporting anything from the dll. In such a case, the linker will create the dll, but won't automatically create a matching import library.
This really depends on your project settings.
Take a look at *.vcprojx
and search for similar pattern:
<link>
<ImportLibrary>.\Release/yourlibrary.lib</ImportLibrary>
</link>
Usually, Visual Studio puts the .lib right next to the .dll file.
YOur case sounds like it wouldn't generate a .lib at all. When building libraries as dll, if you want to link to that library in another project (as opposed to using dllopen and the likes), you have to specify which functions should be exported to a lib. For this, you have to prepend all classes or functions you want to export with a __declspec(dllexport) when building the library, and __declspec(dllimport) when linking it.
What you often find is some macro like this:
#ifdef WIN32
#ifdef MYLIB_EXPORTS
#define MYLIBAPI __declspec(dllexport)
#else
#define MYLIBAPI __declspec(dllimport)
#endif
#else
#define MYLIBAPI
#endif
Then, when building the lib, you define the MYLIB_EXPORTS preprocessor, so that it exports, while linking against it imports. Your own Code could then look like this
class MYLIBAPI MyClass
{
public:
void SomeFunction()
}
MYLIBAPI void SomeGlobalFunction();
Now, MyClass and SomeGLobalFunction are exported when building, and occur in the lib file.

Create C++ Object File Library (.lib) using stubbed functions

I'm trying to create a .lib file to go with an external .dll so we can compile our code against it (VS2010). In this external library functions are declared using __stdcall, they don't specify __declspec(dllimport). Example:
extern "C"
{
int __stdcall exampleFunction(int firstArgument);
}
I've tried the following:
Create .lib from .def
Creating a .def file as follows:
EXPORTS
method_1
method_1#16 #1 ; ordinal from dumpbin
...
Then creating the .lib from this
lib /MACHINE:x86 /def:my_def_file.def OUT:my_library.lib
The code links ok but throws a "Privileged Instruction" exception when first invoking a method from the dll.
Create .lib from .obj
As detailed in this kb another possibility is to create a set of stub functions, compile them into a .obj file and create the .lib from that. I am however unable to even get the example on the kb page to work, e.g.
// my_library.cpp
extern "C" __declspec(dllexport) void _stdcall Function(void) {}
When I run the required commands on this file:
CL /c /Ob0 my_library.cpp
lib /def:my_library.obj /MACHINE:x86
Then opening my_library.obj does show the string "_Function#0", as expected but my_library.lib shows no trace of this. Furthermore I get some funky warning messages when running the lib command:
hasp_api.obj : warning LNK4017: L☺♥ statement not supported for the target platform; gnored
hasp_api.obj : warning LNK4017: ► statement not supported for the target platform; ignored
I'm all for peace and love but I'm kinda stuck and its driving me crazy, any help with either of the methods above would be appreciated!

Error when using dllimport in a DLL client

I'm currently creating a DLL and the client which goes with it using the stored procedure mentioned at a lot of places on the internet. Basically, create a DLL project which actually defines a PROJECT_EXPORTS in the Project.h file.
Something like this:
// Assume the name of the project is SanProj and the header file is SanProj.h
#ifdef SANPROJ_EXPORTS
#define SANPROJ_API __declspec(dllexport)
#else
#define SANPROJ_API __declspec(dllimport)
#endif
Now the normal way of using this header is to include this in all the headers of your API classes and using SANPROJ_EXPORTS for "exporting" declarations when in the DLL and "importing" declarations when used as a client. For e.g. let's say we have a header file with a currency class:
// currency.hpp
#include "SanProj.h"
#include <ostream>
#include <string>
namespace SanProj {
class SANPROJ_API Currency {
public:
Currency();
const std::string& name();
const std::string& code();
bool empty() const;
protected:
std::string name_;
std::string code_;
};
SANPROJ_API bool operator==(const Currency&,
const Currency&);
SANPROJ_API bool operator!=(const Currency&,
const Currency&);
SANPROJ_API std::ostream& operator<<(std::ostream& out, Currency& c);
}
And another header file with specific currencies:
// allccy.hpp
namespace SanProj {
class SANPROJ_API USDCurrency : public Currency {
public:
USDCurrency() {
name_ = "American Dollar";
code_ = "USD";
}
};
class SANPROJ_API CADCurrency : public Currency {
public:
CADCurrency() {
name_ = "Canadian Dollar";
code_ = "CAD";
}
};
}
The above classes form the contract of the DLL project. Now let's look at the client project files, which is a single class with main function:
#include "currency.hpp"
#include "allccy.hpp"
#include <iostream>
using namespace SanProj;
int main(int argc, char* argv[])
{
USDCurrency uccy;
std::cout << uccy;
}
Assuming all referencing/settings are already done in the Visual Studio project, I get the following error when trying to compile the client:
1>testdll.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall SanProj::USDCurrency::~USDCurrency(void)" (__imp_??1USDCurrency#SanProj##QAE#XZ)
1>testdll.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall SanProj::USDCurrency::USDCurrency(void)" (__imp_??0USDCurrency#SanProj##QAE#XZ)
Not surprisingly, this error goes away when I remove the dllimport part from the SanProj.h file and the executable is created.
My question is, what's the point of the IDE generated dllimport if we can't compile clients against the header? Is there a way I can continue to use the header with both dllimport and dllexports and remove the linker errors? Also, why is it trying to resolve the symbol which has dllimport from the LIB file?
TIA,
/sasuke
EDIT: Linker Command used by VisualStudio; as you can see, it has the LIB file.
/OUT:"E:\vsprojects\SomeSln\Release\testdll.exe" /INCREMENTAL:NO
/NOLOGO "E:\vsprojects\SomeSln\Release\SanProj.lib" "kernel32.lib"
"user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib"
"shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib"
"odbccp32.lib" /MANIFEST
/ManifestFile:"Release\testdll.exe.intermediate.manifest"
/ALLOWISOLATION /MANIFESTUAC:"level='asInvoker' uiAccess='false'"
/DEBUG /PDB:"E:\vsprojects\SomeSln\Release\testdll.pdb"
/SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF
/PGD:"E:\vsprojects\SomeSln\Release\testdll.pgd" /LTCG /TLBID:1
/DYNAMICBASE /NXCOMPAT /MACHINE:X86 /ERRORREPORT:QUEUE
EDIT: Sure I'm wrong as the answer of jcopenha is the answer. The linker complains about the missing constructor and destructor that you don't export in the DLL. However the rest is still valid.
[...]
You should have two build targets (or projects, depending on the environment you use).
First target will build the DLL. The files this target will need to be built are, based on what you report:
currency.hpp
allccy.hpp
and probably the implementation of the base class "currency". You must have defined SANPROJ_EXPORTS in the preprocessor defines in order to use the currency.hpp file as a definition of the functions exported by your DLL.
This target will produce a .DLL file and probably (depends on configuration) a .lib file. It may also generate other files like a text representation of the exports of the library (a .DEF file).
Then, you need to build your application (the second target/project):
the header file you need is just simply the same of the library for the #include part. Just be sure to NOT define SANPROJ_EXPORTS or the compiler will try to export again the simbols instead of importing them.
Then you need to add the following settings to the compiler and linker:
Add to the include path the directory containing the .hpp header.
Add to the libraries path of the linker (lib) the directory
containing the .lib file.
Tell the linker to also link against the .lib you just created (add the full name of your lib file, assuming the DLL is named "currency" probably it will be "currency.lib".
Where and how to add this settings depends on the toolchain/environment and compiler you are using.
In the end, be sure that the compiled executable will be able to find the DLL in the project folder or in a system directory (in the PATH) or it will not start. Just copy the DLL with a post-build step if you have the executable in a different folder than the one used to build the DLL.
The fact that removing the _dllimport part will build the project is probably due to the compiler finding the header AND the implementation of the functions you meant to export, and building all of them statically into the executable.
Assuming you are not in the .NET "managed world", and that we are talking about Windows, there are some more points to look at if you want to distribute your library, but this is another answer.
It is specifically complaining about the constructor and destructor for the USDCurrency class but your code does not show those methods as being adorned with SANPROJ_API.
And since you define the USDCurrency constructor in the header, when you remove the dllimport from the class USDCurrency you are getting an implementation defined in your current project and not a reference to the one defined in the DLL.
Everyone else has beat this, I will too.
Re the compiler adding libs, the compiler does just that: compiles. The linker links (doh). Your's appears to be a linker configuration issue, and there are a number of ways to solve this.
If you have a multi-project solution file (.sln) that contains both your DLL project and your EXE project, you can establish an explicit dependency by setting the DLL project to be 'referenced' by the EXE project in the EXE project. Within this, make sure the "Link Library Dependencies" is marked 'true'.
The References configuration is actually a .NET addition starting with VS2005, though it still works for standard C/C++ projects as well. You can skip that and configure the import library to link implicitly on the Linker/Input settings of the EXE project. A setting called "Link Library Dependencies" can me marked true there as well. This also requires you configure the solutions project dependencies (Build/Project Dependencies...). In your case you select your EXE project as "depends on.." and check the DLL project. This ensures that your EXE will be relinked whenever your DLL project is rebuilt and a new import library is created.
If requested, screenshots of all of this are available. It gets to be old habit to set it up after a few go-arounds. At this point I'm fairly sure I can do it blind-folded.
It seems that there is no solution to this problem. I ended up giving up on using dllimport in the client code and take a performance hit. :(

Using #pragma detect_mismatch to ensure a DLL uses the correct statically linked library

I have a static S.lib that is used by my D.dll.
I'm trying to use #pragma detect_mismatch to make sure that both were compiled under the same release or debug settings.
I've followed Holger Grund's instructions here
http://boost.2283326.n4.nabble.com/Boost-and-Microsoft-s-SECURE-SCL-td3025203.html
dumpbin on S.lib shows:
Linker Directives
-----------------
/FAILIFMISMATCH:"COMPILED_DEBUG=1"
/INCLUDE:_dll_impl_interface_mismatch_check
/DEFAULTLIB:"MSVCRTD"
/DEFAULTLIB:"OLDNAMES"
I compile D.dll successfully, which should not happen.
dumpbin on D.dll's D.lib shows:
Linker Directives
-----------------
/FAILIFMISMATCH:"COMPILED_DEBUG=2"
/INCLUDE:_dll_impl_interface_mismatch_check
/DEFAULTLIB:"uuid.lib"
/DEFAULTLIB:"uuid.lib"
/FAILIFMISMATCH:"_MSC_VER=1600"
/FAILIFMISMATCH:"_ITERATOR_DEBUG_LEVEL=2"
/DEFAULTLIB:"msvcprtd"
/DEFAULTLIB:"MSVCRTD"
/DEFAULTLIB:"OLDNAMES"
Any help would be greatly appreciated.
EDIT:
I accidentally defined the symbol 'dll_impl_interface_mismatch_check' in BOTH my static library and my consuming DLL. This meant that the symbol was not looked for in the static library S.lib, and the mismatch directive was never found. I think.
I'm just guessing here - I'll have to experiment with this tonight.
Holger Grund's instructions are designed for objects that depend on the DLL. In your case the DLL depends on the static lib.
So, I'm guessing that you want the _dll_impl_interface_mismatch_check object to be added to the static lib rather than the DLL. So instead of:
extern "C" const char dll_impl_interface_mismatch_check=0;
cl /c /Zl foo.cpp
lib D.lib foo.obj
try:
extern "C" const char dll_impl_interface_mismatch_check=0;
cl /c /Zl foo.cpp
lib S.lib foo.obj
You have to build a string with the preprocessor that represents your build settings, and use that string with #pragma detect_mismatch.
E.g.
#if defined(_DEBUG)
#define FOO_DEBUG_PART "_debug"
#else
#define FOO_DEBUG_PART "_release"
#endif
#if defined(_MT)
#define FOO_CRT_PART1 "_MT"
#else
#define FOO_CRT_PART1 "_st"
#endif
#if defined(_DLL)
#define FOO_CRT_PART2 "_DLL"
#else
#define FOO_CRT_PART2 "_LIB"
#endif
// ...
#define FOO_BUILD_SETTINGS FOO_DEBUG_PART FOO_CRT_PART1 FOO_CRT_PART2 /* ... */
#pragma detect_mismatch("foo_build_settings", FOO_BUILD_SETTINGS)
The IMO better solution though is to use #pragma comment(lib) to link to your libraries, and then build a similar string and use it as part of the file name of the lib:
// build FOO_BUILD_SETTINGS like above
#pragma comment(lib, "mylib" FOO_BUILD_SETTINGS)
That way you cannot use the wrong library (unless you either change the code or the lib is created with the wrong file name ... or renamed afterwards). And of course, if you're as paranoid as I am, you can always do both :)