EDK - The 'global' keyword and precompiled headers
--------------------------------------------------

EXE and DLL files are almost identical under Windows. The difference is only a single bit in the header. Normally, an EXE will load a DLL and then call its functions, but the DLL can also call back functions of the EXE. Thus, from the viewpoint of a PC64Win extension, the emulator is just a large library which contains all the global functions declared in EDK.h.

In order to allow an extension to call functions in PC64Win, the emulator must export them. As PC64Win will also call its own functions from inside, this leads to three cases:

1) A function resides in the same CPP file than the caller.
2) A function resides in a different CPP file but in the same EXE/DLL file.
3) A function resides in a different EXE/DLL file.

Unfortunately, Visual C++ 4.2 doesn't provide a model which meets all three conditions. Without using the preprocessor, you will always get a compiler or linker error in one of the cases. That's why the EDK is defining a 'global' specifier. This keyword must be added to all functions which may be used by extensions in other DLLs. The only exception are functions which are explicitly declared as inline.

In MyClass.cpp, the keyword 'global' will always expand to __declspec(dllexport). In MyClass.h, the three cases are resolved the following way:

Case                   MyClass.h
--------------------------------------------
1) same CPP            __declspec(dllexport)
2) same EXE/DLL        <empty>
3) different EXE/DLL   __declspec(dllimport)

The macros 'MyClass_cpp' and 'EDKMAIN' will control which one of the three definitions is used. Instead of just writing '#include "MyClass.h"', the following syntax must be used to add MyClass to EDK2.h, the header file for the EDK library:

#undef global
#if defined(MyClass_cpp)
  #define global __declspec(dllexport)
#elif defined(EDKMAIN)
  #define global
#else
  #define global __declspec(dllimport)
#endif
#include "MyClass.h"

The source file MyClass.cpp will then have the following header:

#include <EDK1.h>
#define MyClass_cpp
#include <EDK2.h>

In addition, the make file for the main EXE must define 'EDKMAIN' on the compiler's command line in order to allow MyClass to be called from within.

One drawback of defining a different name macro for each CPP file is that it doesn't allow precompiled headers. That's why there is no single '#include <EDK.h>' in a CPP file. Instead, each component must first include EDK1.h, then define Name_cpp, and then include the second part EDK2.h. This allows EDK1.h to include the big header files like AFXWin.h. When the main EXE is compiled, Visual C++ 4.2 will then use precompiled headers at least for the large header files. In PC64Win, if you set 'Automatic use of precompiled headers through header EDK1.h', this will reduce the time for a complete rebuild from 10:35 min to 4:08 min.

A second drawback is that it isn't possible to split MyClass.cpp up into several files. In other words, one component makes one CPP file and one H file.

This approach works for global functions and class member functions. Although it is generally no good idea to use them, the following syntax may be used for global variables:

#if defined(MyFile_cpp)
  HINSTANCE ghinst;
  __declspec(dllexport) HINSTANCE* gphinst = &ghinst;
#elif defined(EDKMAIN)
  extern HINSTANCE ghinst;
#else
  __declspec(dllimport) HINSTANCE* gphinst;
  static HINSTANCE& ghinst = *gphinst;
#endif

This declares and defines a global variable named 'ghinst' which is accessible from both the main EXE and the DLLs, at least in C++. In inline assembler, if located in a DLL, 'mov EAX,ghinst' will give you the address of ghinst and not its content! If that line is located in the main EXE, 'mov EAX,ghinst' will work as expected. 
