Click here to Skip to main content
15,885,546 members
Articles / Desktop Programming / Win32
Tip/Trick

Importing a Locally Defined Symbol without Using a Module Definition File

Rate me:
Please Sign up or sign in to vote.
4.75/5 (4 votes)
27 Feb 2016CPOL4 min read 11.1K   7  

Introduction

In the beginning, I used Module Definition Files to identify symbols that I wanted the linker to mark as exported from my Win32 dynamic link libraries. More recently, however, I have abandoned the practice because it hides the symbol names from the listings generated by DumbBin.exe, which I generate as part of the technical documentation of my production code libraries, and to which I regularly refer to identify dependencies and solve linkage problems.

Today, I was reminded that module definition files make importing a locally defined symbol transparent. Nevertheless, before I added a .DEF file to this library, which exports over sixty functions, I sought another way to import the two locally defined symbols that I needed to use in another routine exported by the same library..

Background

Following are the key concepts that must be understood for the remainder of this article to make sense.

  1. A locally defined symbol is a function that is exported by a DLL, and called (imported) by another function defined in the same library. 
  2. The alternative to using a module definition (.DEF) file is specifying __declspec(dllexport) when a function is defined, and __declspec(dllimport) when it is declared (imported),

The second feat is usually accomplished by writing somethng like the followng into the header that declares the functions exported by it.

C++
#if defined ( _BUILDING_WWKERNELLIBWRAPPER )
    #define LIBSPEC_WWKERNELLIBWRAPPER_API  __declspec(dllexport)
#else
    #define LIBSPEC_WWKERNELLIBWRAPPER_API  __declspec(dllimport)
#endif  /* #if defined ( _BUILDING_WWKERNELLIBWRAPPER ) */

The first nonblank line in any source file that defines an exported function is the following.

C++
#define _BUILDING_WWKERNELLIBWRAPPER

The effect of the above definition, which must, at a minimum, precede the #include directive that brings the header in which the routine is declared into the compilation stream, is that when LIBSPEC_WWKERNELLIBWRAPPER_API appears in the function prototype, it is replaced by __declspec(dllexport).

Coinversely, since _BUILDING_WWKERNELLIBWRAPPER is undefined in normal usage, when the function is declared in any routine that imports it, LIBSPEC_WWKERNELLIBWRAPPER_API becomes __declspec(dllimport), and the linker resolves the externally defined symbol.

Unless your library improts its own symbols, this works great.

Bending the Rules

Solving this problem requires a temporary suspension of the preprocessor macro that controls the decoration of functions, designating whether they are being exported or imported. For the case at hand, that symbol is _BUILDING_CREATEGUIDSTRING_. By suspension, I mean that the definition must be temporarily overridden when the functions are declared in the routine that needs to import them, then reinstated for the remaining declarations.

The suspension is achieved by feeding the macro name, as a quoted string, to #pragma push_macro  and #pragma pop_macro, a pair of nonstandard preprocessor directive that are, thankfully, implemented by the two most widely used C++ compilers, Microsoft Visual C++ and GCC.

The routine that needs to call (import) another routine in the library defines a distinctively named preprocessor symbol, as shown below.

C++
#define _BUILDING_CREATEGUIDSTRING_

It happens that the routines that I need to import are declared in a subsidiary header that is nested in the main library header. Hence, the suspension and reinstatement are wrapped around the nested #include in the main library header, as shown below.

C++
#if defined (_BUILDING_CREATEGUIDSTRING_ )
    #pragma push_macro ( "LIBSPEC_P6CSTRINGLIB1_API" )
    #undef LIBSPEC_P6CSTRINGLIB1_API
    #define LIBSPEC_P6CSTRINGLIB1_API __declspec(dllimport)
#endif    /* #if defined (_BUILDING_CREATEGUIDSTRING_ ) */

#include <BinToHex_WW.H>

#if defined (_BUILDING_CREATEGUIDSTRING_ )
    #pragma pop_macro ( "LIBSPEC_P6CSTRINGLIB1_API" )
#endif /* #if defined (_BUILDING_CREATEGUIDSTRING_ ) */

Had the declarations been in a monolithic header, the suspension and reinstatement would be handled in the same way, but would be wrapped around the declarations, themselves.

The linkage editor emits two warnings, as it would if you used a module definition file.

C++
1>CreateGUIDStringA.obj : warning LNK4217: locally defined symbol _BinToHexA_WW imported in function _CreateGUIDStringA@4
1>CreateGUIDStringW.obj : warning LNK4217: locally defined symbol _BinToHexW_WW imported in function _CreateGUIDStringW@4

Since I expect the warnings, which remind me of the harmless circular dependency, I have never investigated suppressing them. It's more important that I avoided recreating the module definition file, a tedious, error prone task that might have forced me to re-link dozens of modules that import one or more other symbols from this library. Instead, I made a couple of small changes in one library header, rebuilt the library, and progressed to the next task.

Points of Interest

This is hardly my first encounter with #pragma directives, nor is it my first encounter with preprocessor directives that manipulate a stack. For example, #pragma push and #pragma pop are old friends, used from time to time to suppress warnings that can be safely ignored.

Discovering #pragma push_macro and #pragma pop_macro was a pleasant surprise, which enabled me to quickly solve an otherwise difficult problem, without abandoning __declspec(dllexport) and __declspec(dllimport) to control exporing and importing of entry points.

In closing, thanks are due to Peter for the answer he gave on Stack Overflow six years ago, in answer to, "Can I redefine a C++ macro then define it back?" at http://stackoverflow.com/questions/1793800/can-i-redefine-a-c-macro-then-define-it-back.

History

Monday, 11 January 2016 - Initial publication

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
I deliver robust, clean, adaptable, future-ready applications that are properly documented for users and maintainers. I have deep knowledge in multiple technologies and broad familiarity with computer and software technologies of yesterday, today, and tomorrow.

While it isn't perceived as sexy, my focus has always been the back end of the application stack, where data arrives from a multitude of sources, and is converted into reports that express my interpretation of The Fundamental Principle of Tabular Reporting, and are the most visible aspect of the system to senior executives who approve the projects and sign the checks.

While I can design a front end, I prefer to work at the back end, getting data into the system from outside sources, such as other computers, electronic sensors, and so forth, and getting it out of the system, as reports to IDENTIFY and SOLVE problems.

When presented with a problem, I focus on identifying and solving the root problem for the long term.

Specialties: Design: Relational data base design, focusing on reporting; organization and presentation of large document collections such as MSDS libraries

Development: Powerful, imaginative utility programs and scripts for automated systems management and maintenance

Industries: Property management, Employee Health and Safety, Services

Languages: C#, C++, C, Python, VBA, Visual Basic, Perl, WinBatch, SQL, XML, HTML, Javascript

Outside Interests: Great music (mostly, but by no means limited to, classical), viewing and photographing sunsets and clouds, traveling by car on small country roads, attending museum exhibits (fine art, history, science, technology), long walks, especially where there is little or no motor traffic, reading, especially nonfiction and thoughtfully written, thought provoking science fiction

Comments and Discussions

 
-- There are no messages in this forum --