本章节的内容来自《Windows核心编程》第五版第19章。详情请细看此书
- dll基础
dll可用于多个程序共享代码,因为如果有多个程序调用同一个dll,这个dll只会被加载到内存一次。这就对线程安全性有了要求。如果保证资源访问的正确性与安全性?这就需要在编写时注意资源的保护和函数中数据资源的存储。一下两点要注意:1>避免使用单一的全局变量,如:int g_nCarNum;这样做所有加载的进程都可以访问他;2>如果访问全局资源,注意加锁、关键段等。
构建dll的步骤:
- 头文件:其中包含了待导出的函数原型、结构和符号声明
- C/C++源文件,包含头文件中声明的实现
- 编译器为每个cpp文件生成.obj文件
- 连接器将每个.obj模块合并,从而生成.dll
- 如果至少包含了一个函数/变量,则会生成一个.lib文件(lib路径声明在 链接器-> 高级->导入库)。
构建dll模块:
dll模块可以用于导出函数、类、和变量。一般变量我们不会使用,也没有必要。原书中只提供了导出函数的方法,再次我提供两个分别为导出函数和导出类的方法:
首先声明:dll也是有main函数的,只是根据MSDN定义,这个dllmain是可有可无的,下面的第一个程序中我对main函数写了一个示例。
1>导出函数
testFunc.h
#pragma once
#include "iostream"
#ifdef WIN32
#define YCD_EXTERN extern "C" __declspec(dllexport)
#else
#define YCD_EXTERN extern "C"
#endif // WIN32
typedef void* YCD_HANDLE;
YCD_EXTERN int MyFun(YCD_HANDLE hTest, int nStartTime, int nEndtime);
testFun.cpp
#include "test.h"
#include "windows.h"
#include "Psapi.h"
int DllMain(_In_ void* DllHandle, // DLL 调用模块
_In_ unsigned long Reason, // 调用情况
_In_opt_ void* Reserved) // reserved
{
// 在不同情况下都会调用dllmain函数,分别处理;
switch (Reason)
{
// 加载 DLL
case DLL_PROCESS_ATTACH:
{
char lpMainMoudleName[MAX_PATH] = { 0 };
char lpMessage[MAX_PATH + 64] = { 0 };
// 获取PID和主模块名;将弹出消息框
DWORD dwPID = GetCurrentProcessId();
GetModuleBaseName(GetCurrentProcess(), NULL, lpMainMoudleName, MAX_PATH);
wsprintf(lpMessage, "Process Name :%s, PID :%u", lpMainMoudleName, dwPID);
MessageBox(NULL, lpMessage, "testFun.dll", MB_OK);
break;
}
// 新建线程;
case DLL_THREAD_ATTACH:
{
break;
}
// 线程退出
case DLL_THREAD_DETACH:
{
break;
}
// 进程退出,释放DLL
case DLL_PROCESS_DETACH:
{
break;
}
default:
break;
}
return true;
}
YCD_EXTERN int MyFun(YCD_HANDLE hTest, int nStartTime, int nEndtime)
{
return nEndtime - nStartTime;
}
2> 导出类
externClass.h
#pragma once
#include "iostream"
using namespace std;
#ifdef WIN32
#define YCD_EXTERN extern "C" __declspec(dllexport)
#else
#define YCD_EXTERN extern "C"
#endif
class MyClassImpl
{
public:
MyClassImpl();
~MyClassImpl();
int OutPut();
private:
};
YCD_EXTERN class MyClass
{
public:
MyClass();
~ MyClass();
int Output();
private:
MyClassImpl* m_pMyclassImpl;
};
externClass.cpp
/**************************************************************
* Filename: PKLog.cpp
* Copyright: Shanghai Baosight Software Co., Ltd.
*
* Description: create.
*
* @author: zhaohui
* @version 03/13/2008 zhaohui Initial Version
* @version 05/19/2008 zhaohui change PKLog from dll to lib .
* @version 05/26/2008 zhaohui add conf env read and log out.
* @version 05/30/2008 zhaohui change pugxml to tinyxml.
* @version 06/25/2008 caichunlei 添加LogErrMessage接口 .
* @version 09/28/2008 shenchunfeng 修正配置文件错误引起异常退出的缺陷,修正程序在调入Dll时引起的缺陷,增加能拆分多个日志文件的功能。
* @version 01/29/2010 chenzhiquan 将屏幕输出由std::cout改为printf,避免多条输出时发生混乱.
* @version 08/29/2010 chenzhiquan 初始化变量.
**************************************************************/
#include "pklog/pklog.h"
#include <ace/FILE.h>
#include <ace/Dirent.h>
#include <ace/Guard_T.h>
#include <ace/streams.h>
#include <ace/Thread_Mutex.h>
#include "ace/OS_NS_stdio.h"
#include "ace/OS_NS_sys_stat.h"
#include "ace/OS_NS_sys_time.h"
#include "tinyxml/tinyxml.h"
#include "log_to_file.h"
#include <sstream>
#include "PKLogImpl.h"
#define MAX_LOG_SIZE 4096
#define MAXLOGNUM 1000
#define PK_SHORTFILENAME_MAXLEN 260 // 文件全名称(不含路径)的最大长度
#define PK_LONGFILENAME_MAXLEN 1024 // 文件全路径的最大长度(实际长度受限于操作系统,windows下文件夹长度小于249,文件名全路径长度小于260)
#define DT_SIZE_TIMESTR 24 // strlen("2008-01-01 12:12:12.000") + 1
//CPkLog::CPkLog(CPkLogImp *imp): m_pPkLogImp(imp)
CPKLog::CPKLog()
{
m_pPKLogImp = new CPKLogImp();
}
CPKLog::~CPKLog()
{
if (m_pPKLogImp)
{
delete m_pPKLogImp;
m_pPKLogImp = NULL;
}
}
bool CPKLog::SetLogFileName( const char *szLogFileName )
{
return m_pPKLogImp->SetLogFileName(szLogFileName);
}
/*
int CPkLog::SwitchLogLevel( int nNewLevel )
{
//CV_PK_LOGLEVEL_ nOldLevel = m_pPkLogImp->m_nCurrentLogLV;
//m_pPkLogImp->m_nCurrentLogLV = nNewLevel;
if (NULL != m_pPkLogImp)
{
return m_pPkLogImp->SetLogRecordLevel(nNewLevel);
}
else
{
return PK_LOGLEVEL_CRITICAL;
}
}
*/
/*
void CPkLog::SetLogOnMonitor( bool bLogOnMonitor )
{
if (NULL != m_pPkLogImp)
{
m_pPkLogImp->m_bLogOnMonitor = bLogOnMonitor;//该值通过配置文件修改可以发生改变
m_pPkLogImp->m_bCancelLogOnMonitor = !bLogOnMonitor;
}
}
*/
//CPkLog PKLog(new CPkLogImp()); //PKLog使用的全局变量名字
/**
* 记录日志信息.
* the same as BBSERROR uses.
*
* @param -[in,out] int nLogLevel: [日志级别]
* @param -[in,out] const char* szFormat: [日志内容]
* @param -[in,out] ...: [不定参数]
* @return void.
*
* @version 03/13/2008 zhaohui Initial Version.
*/
void CPKLog::LogMessage(int nLogLevel, const char *szFormat, ... )
{
// 不是Notice级别,且或者级别不满足
if (nLogLevel != PK_LOGLEVEL_NOTICE && !(m_pPKLogImp->m_nCurrentLogBitMap & nLogLevel) )
return;
//ACE_Guard<ACE_Thread_Mutex> guard(m_pPKLogImp->m_mutex);
//日志内容
char* szbuf = NULL;
szbuf = new char[MAX_LOG_SIZE];
if (NULL == szbuf)
return;
szbuf[0] = '\0';
//定义变量指针
try
{
va_list ap;
//初始化ap
va_start(ap, szFormat);
int len = ACE_OS::vsnprintf(szbuf, MAX_LOG_SIZE, szFormat, ap);
va_end(ap);
}
catch (...)
{
strcpy(szbuf, "loging,Error Formatting Log Message! ");
}
m_pPKLogImp->WriteLog(nLogLevel, szbuf);
if (szbuf != NULL)
{
delete[] szbuf;
}
}
/**
* 记录日志信息.
* the same as BBSERROR uses.
*
* @param -[in,out] int nLogLevel: [日志级别]
* @param -[in,out] const char* szFormat: [日志内容]
* @param -[in,out] ...: [不定参数]
* @return void.
*
* @version 03/13/2008 zhaohui Initial Version.
*/
void CPKLog::LogErrMessage(int nErrCode, const char *szFormat, ... )
{
// 不是Notice级别,且或者级别不满足
int nLogLevel = PK_LOGLEVEL_ERROR;
if (nLogLevel != PK_LOGLEVEL_NOTICE && !(m_pPKLogImp->m_nCurrentLogBitMap & nLogLevel) )
return;
//ACE_Guard<ACE_Thread_Mutex> guard(m_pPKLogImp->m_mutex);
//日志内容
char* szbuf = NULL;
szbuf = new char[MAX_LOG_SIZE];
if (NULL == szbuf)
return;
szbuf[0] = '\0';
//定义变量指针
try
{
va_list ap;
//初始化ap
va_start(ap, szFormat);
int len = ACE_OS::vsnprintf(szbuf, MAX_LOG_SIZE, szFormat, ap);
va_end(ap);
}
catch (...)
{
strcpy(szbuf, "loging,Error Formatting Log Message! ");
}
m_pPKLogImp->WriteLog(nLogLevel, szbuf);
if (szbuf != NULL)
{
delete[] szbuf;
}
}