Creating statically linked, non-MFC DLLs

This article illustrates how to create and use Statically linked Non-MFC (Implicit) DLLs. In order to build applications using DLLs in this way you must have .dll distribution with .lib & .h files. Lemme create DLL which can export whole class with method and variable and global variable with global procedure. I call this as HookWorks because have used this code to window's hook, and leave without changes. To start from 0, select New->Project->Win32 DLL. Select empty project and click finish (no need to help from wizards! ;). First going to planning our exports and declare them in

//**************HWMain.h file*****************

//HWMain.h Non-MFC DLL header file

//sure it will be included only once
#ifndef _ATM_HWMAIN_H_
#define _ATM_HWMAIN_H_
#pragma once

//to speedup & reduce size
#define VC_EXTRALEAN

//Header body
You can use .DEF file if wanna to use ordinal exports, but here I use exports by names. Create some usefull macros
//export macros
#define DLL_EXPORT __declspec(dllexport) 
//import macro
#define DLL_IMPORT __declspec(dllimport)
After completing your DLL, you can use DUMPBIN [options] files to view export table of DLL.
// ...
#include <windows.h>

//to export for C++ & C
#ifdef __cplusplus 
 extern "C" 
 {  
#endif
Here, the class will be exported with all of its members. Now you only need to create a new instance of the object and all members will be accessable
//class export, with all definitions
class DLL_EXPORT CHookWorks
{
public:
 //blah blah method
 int HookWorksFunc(void);

 //blah blah variable
 int m_iHookWorks;

 //con and decon
 CHookWorks();
 ~CHookWorks();
};
Here is a variable which will be accesable as extern in your "client"
//exported variable
DLL_EXPORT int exp_iVar=99;

this function will be directly accessable 

//exported function
DLL_EXPORT BOOL ExportedFunc(BOOL bParam);

copmleted ... only close this "language unconveniences".

#ifdef __cplusplus 
   } 
#endif

//EOF Header body
#endif 
//*******************HWMain.h file**********************

implementation is too simple with some explains:

//*******************HWMain.cpp file********************
#include "HWMain.h"

//Regular DLL Entry Point, do not have any importancy 
//to me here
BOOL WINAPI DllMain(HINSTANCE hinstDLL, // DLL module handle
                    DWORD fdwReason, // for calling function
                    LPVOID lpvReserved   // reserved
                    )

{				   
 switch(fdwReason)
 { 
  case DLL_PROCESS_ATTACH:
   /* Indicates that the DLL is being loaded into the 
   virtual address space of the current process as 
   a result of the process starting up or as a 
   result of a call to LoadLibrary.DLLs can use 
   this opportunity to initialize any instance data 
   or to use the TlsAlloc function to allocate a 
   Thread Local Storage (TLS) index.*/
  break;

  case DLL_THREAD_ATTACH:
   /* Indicates that the current process is creating a 
	 new thread.When this occurs, the system calls the 
	 entry-point function of all DLLs currently attached 
	 to the process. The call is made in the context of 
	 the new thread. DLLs can use this opportunity 
   to initialize a TLS slot for the thread. A thread 
	 calling the DLL entry-point function with 
	 DLL_PROCESS_ATTACH does not call the DLL entry-point 
	 function with DLL_THREAD_ATTACH. Note that a DLL's 
	 entry-point function is called with this value only 
	 by threads created after the DLL is loaded by the 
   process. When a DLL is loaded using LoadLibrary, 
	 existing threads do not call the entry-point 
	 function of the newly loaded DLL.*/
  break;

  case DLL_THREAD_DETACH:
   /* Indicates that a thread is exiting cleanly. 
   If the DLL has stored a pointer to allocated 
   memory in a TLS slot, it uses this opportunity 
   to free the memory. The system calls the entry-point 
   function of all currently loaded DLLs with this value. 
   The call is made in the context of the exiting thread.*/
  break;

  case DLL_PROCESS_DETACH: 
   /* Indicates that the DLL is being unloaded from 
   the virtual address space of the calling process 
   as a result of either a process exit or a call to 
   FreeLibrary. The DLL can use this opportunity to 
   call the TlsFree function to free any TLS indices 
   allocated by using TlsAlloc and to free any thread 
   local data.*/
  break;

  default:
   break;
 }

 return TRUE;
}

//class export, with all definitions
//member method
int CHookWorks::HookWorksFunc()
{
 m_iHookWorks=10;
 return 2;
}

//constructor
CHookWorks::CHookWorks()
{
 m_iHookWorks=1;
}

//destructor
CHookWorks::~CHookWorks()
{
}

//exported function declaration
DLL_EXPORT BOOL ExportedFunc(BOOL bParam)
{
 return bParam;
}

//*******************HWMain.cpp file********************
Yep. It's ready. Simply compile after checking that you don't want MFC support (in the Linker settings). After building the project, you will 3 files that you need to distribute:
  • DEBUG build
    • HookWorks.dll
    • HookWorks.lib
    • HWMain.h
  • RELEASE build
    • HookWorks.dll
    • HookWorks.lib
    • HWMain.h
The DLL must be stored in the same folder as the client application or in system DLL paths in order for it to be accessable from the client. If the client application can't find the DLL, you will receive an error message.

Now create new project File->New->Project->Win32 application, select empty project (no need to wizards). Go to Project->Settings and use MFC as dynamic linked. I put my DEBUG and RELEASE .lib files in DEBUG and RELEASE paths for client project and point to Project's linker seek them there related on client version RELEASE/DEBUG. Only unconvenience it's a rewriting of dll file when switch from RELEASE to DEBUG and back. In client app, I only call export variables and procedures and show them in wnd.

//*************** stdafx.h file ************************
#ifndef _ATM_STD_AFX_H_
#define _ATM_STD_AFX_H_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define WIN32_LEAN_AND_MEAN		

#include  
#endif
//*************** stdafx.h file ************************

//************** stdafx.cpp file ***********************
#include "stdafx.h"
//************** stdafx.cpp file ***********************

//************** HWImplicitUse.h ***********************
// HWImplicitUse.h 
//
#ifndef _ATM_HW_IMPLICITUSE_H_
#define _ATM_HW_IMPLICITUSE_H_
#pragma once

#include "stdafx.h"
//its my DLL project's path
#include "D:\Developing\cpp\HookWorks\HWMain.h"


//CMainApp Declarations
class CMainApp:public CWinApp
{
public:
 CMainApp();
 ~CMainApp();
 
 //will be test here
 virtual int Run( );
protected:
 BOOL InitInstance();
};

//CMainWnd Declarations
class CMainWnd:public CFrameWnd
{
public:
 CMainWnd();
 ~CMainWnd();
};

#endif
//************** HWImplicitUse.h ***********************

//************* HWImplicitUse.cpp **********************
// HWImplicitUse.cpp 
//

#include "stdafx.h"
#include "HWImplicitUse.h"

//CMainApp Implementation
CMainApp::CMainApp()
{}

CMainApp::~CMainApp()
{}

BOOL CMainApp::InitInstance()
{
 if (!CWinApp::InitInstance())
  return FALSE;

 CMainWnd* t_pMainWnd=new CMainWnd();
 
 if (t_pMainWnd==NULL)
  return FALSE;

 m_pMainWnd=t_pMainWnd;

 m_pMainWnd->ShowWindow(m_nCmdShow);
 m_pMainWnd->UpdateWindow();

 return TRUE;
}

int CMainApp::Run( )
{
 CClientDC t_cDC(m_pMainWnd);
 char szDLLParams[]="                                            ";
 
 //easy to create new instance
 CHookWorks* t_hwDLLClass=new CHookWorks();

 //easy access to instance's method
 int t_iDLLClassVar=t_hwDLLClass->HookWorksFunc();

 wsprintf(szDLLParams,"HookWorksFunc=%d",t_iDLLClassVar);
 t_cDC.TextOut(0,0,szDLLParams);

 //easy access to instance's variable
 t_iDLLClassVar=t_hwDLLClass->m_iHookWorks;
 
 wsprintf(szDLLParams,"m_iHookWorks=%d",t_iDLLClassVar);
 t_cDC.TextOut(0,15,szDLLParams);

 //easy access to global export variable
 t_iDLLClassVar=exp_iVar;
 
 wsprintf(szDLLParams,"exp_iVar=%d",t_iDLLClassVar);
 t_cDC.TextOut(0,30,szDLLParams);

 //and easy access to global export function
 UINT t_bDLLExpFunc=(UINT)ExportedFunc(FALSE);

 wsprintf(szDLLParams,"ExportedFunc(FALSE)=%d",t_bDLLExpFunc);
 t_cDC.TextOut(0,45,szDLLParams);

 //all other works to parents
 return CWinApp::Run();
}


CMainApp MainApplication;

//CMainWnd Implementation
CMainWnd::CMainWnd()
{
 Create(NULL,"HookWorks DLL Implicit Usage Demo");
}

CMainWnd::~CMainWnd()
{
}
//************* HWImplicitUse.cpp **********************

Downloads

Download demo project - 5 Kb