HeQing博客之家

.Net、Delphi、VC技术交流

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
下载示范工程(Delphi和VC的源程序和可执行程序) - 264K

前言

    站长有个习惯,用 Delphi 作项目的公共对话框,因为 Delphi 的可视化编辑表现极佳,可以方便地显示真彩位图,或 Jpeg 格式图形,这在 VC 下是很麻烦的。

    在工程中引入一个动态库一般有两种方法,一种是隐式链接,VC 在使用隐式链接方式时,编译器需要 Lib 文件支持,但由 Delphi 编译生成的动态库并没有 Lib 文件,需要用 DUMPBIN 工具手工生成,随着你的工程的开发进展,这个动态可能会不断的增加新的对话框,你又分不断的用 DUMPBIN 工具重重复的劳动,我从不会作这种麻烦事的。而另一种是显式加载方式,显载加载方式就是在需要使用动态库中的导出函数的地方使用 LoadLibrary 函数显式加载该动态库到内存中,不再使用时可以用 FreeLibrary 函数释放动态库占用的内存,这种动态库的使用方式不需要 Lib 文件支持,很适合在 VC 中调用由 Delphi 生成的动态库,我一般都是使用这种方式的。

    这里以一个常用的用户登录的例子来示范如果在 VC 中使用由 Delphi 构造的公共对话框。

用 Delphi 创建基于对话框的动态库

    这篇文章不是教你如何用 Delphi 来创建动态库的,我假设你可以无障碍的在 Delphi 中创建动态库,所以这里要说的内容并不多。

    废话少说,现在启动 Delphi 的 IDE,创建一个动态库工程 CommonDlg,为该动态库工程新建一个窗口,如图所示:

    修改窗口的主要属性:Name=frmLoginDlg、BorderStyle=bsDialog、Position=poMainFormCenter;修改窗口中“确认”按钮的属性:Kind=bkOK;修改窗口中“取消”按钮的属性:Kind=bkCancel;在 LoginDlg 单元中添加一个动态库的导出 API 函数 ShowLoginDlg,它的定义原型和实现如下:

///////////////////////////////////////////////////////////////////////
// 导出 API 定义
// 返回按下那个键 IDOK 或 IDCANCEL
// szUser/szPassword必须由调用者分配足够的内存
// 一个大于32个字节的字符串空间是安全的
function ShowLoginDlg(hHandle: HWND;szUser: PChar;szPassword: PChar): Integer; stdcall;
var
  hTemp: HWND;
  frmDlg: TfrmLoginDlg;
begin
  Result :
= -1;
  hTemp :
= hHandle;
  
if hTemp = 0 then
    hTemp :
= GetActiveWindow;
  Application.handle :
= hTemp;
  
try
    frmDlg :
= TfrmLoginDlg.Create(Application);
    
try
      frmDlg.SetParams(szUser, szPassword);
      Result :
= frmDlg.ShowModal;
      
if Result = IDOK then
        frmDlg.GetParams(szUser, szPassword);
    
finally
      frmDlg.free;
    end
  except
    on E: Exception 
do
      Application.HandleException( E );
  end;
  Application.handle :
= 0;
end;


    其中有两个函数 SetParamsGetParams 的实现如下:

 

// 设置一个用户账号的原始值,比如指定一个默认的用户账号
procedure TfrmLoginDlg.SetParams(szUser: PChar; szPassword: PChar);
begin
  edtUser.Text :
= StrPas(szUser);
  edtPassword.Text :
= '';
end;

// 返回用户输入的用户账号和密码值
procedure TfrmLoginDlg.GetParams(szUser: PChar; szPassword: PChar);
begin
  StrCopy(szUser, PChar(edtUser.Text));
  StrCopy(szPassword, PChar(edtPassword.Text));
end;

    在动态库的工程 CommonDlg 中导出该 API 函数:

exports
  ShowLoginDlg;


    然后,编译……搞定!

动态库导出函数管理类

    为了方便调用由以上步骤生成的动态库,这里为你提供一个辅助类,该类封装动态库的加载和释放。创建该类这前首先为 CommonDlg.dll 的导出函数编写 VC 中的原型:

typedef int (__stdcall *PShowLoginDlg)(HWND, LPSTR szUser, LPSTR szPassword);

    创建辅助类 CCommonDlg 类:

 

class CCommonDlg
{
public:
  CCommonDlg(LPCTSTR szDllFileName 
= NULL);
  
virtual ~CCommonDlg();

public:
  HINSTANCE GetHandle() 
const// 访问受保护的动态库句柄

public:
  
int ShowLoginDlg(CString &szUser, CString &szPasswd);

protected:
  
void LoadProcAddress(); // 用于加载动态库的所有引出函数

protected:
  
static long m_lRef; // 当前动态库正在被使用的数目
  static HINSTANCE m_hLib; // 动态库的加载句柄
  static PShowLoginDlg m_pShowLoginDlg; // 引出函数
}
;


    这个辅助类中的数据成员全部定义成 static 类型,可以保证动态库不会被多次加载,m_lRef 用于作使用计数,当该辅助类被实例化时,m_lRef 累加一,当 m_lRef 等于一时,表示这是创建的第一个实例,这时需要调用 LoadLibrary 来加载动态库;而每释放一个实例,m_lRef 减一,当减到零时,表示该动态库已经不再被使用了,调用 FreeLibrary 函数释放动态库占用的内存。

    以上通过 CCommonDlg 类的构造和析构函数来实现:

// 构造函数
// 该对象在第一次被实例化时将加载动态库
CCommonDlg::CCommonDlg(LPCTSTR szDllFileName /*= NULL*/)
{
  
if (1 == InterlockedIncrement(&m_lRef))
  
{
    
if (NULL == szDllFileName)
      m_hLib 
= LoadLibrary(COMMONDLG_DLL_FILENAME); // 加载默认动态库
    else
      m_hLib 
= LoadLibrary(szDllFileName);

    
if (NULL != m_hLib) // 是否加载成功
    {
       LoadProcAddress();
    }

  }

}


// 析构函数
// 当使用者为零时释放动态库
CCommonDlg::~CCommonDlg()
{
  
if (InterlockedDecrement(&m_lRef) == 0)
  
{
    
if (m_hLib)
      FreeLibrary( m_hLib );

    m_hLib 
= 0;
  }

}


使用

    在需要使用动态库中的对话框的地方创建一个 CCommonDlg 实例,就可以像一般的类那样使用动态库中定义的导出函数,而不必关心什么时候要加载该动态,什么时候释放动态。下面是该类的使用示范:

 

void CVC_AppDlg::OnButtonLoginClicked() 
{
  
// TODO: Add your control notification handler code here
  CCommonDlg dlg; // 动态加载动态库

  CString szUser, szPasswd;

  
int ref = dlg.ShowLoginDlg(szUser, szPasswd);

  
if (IDOK == ref)
  
{
    CString szt;
    szt.Format(
"你输入的用户账号:%s,密码:%s!", szUser, szPasswd);
    AfxMessageBox(szt);
  }

  
else if (-1 == ref)
    AfxMessageBox(
"动态库加载失败!");
  
else {
    AfxMessageBox(
"你放弃了用户登录操作!");
  }

}



总结

    CCommonDlg 类有一定的通用性,可广泛适用于其他动态库导出函数的封装。而你要作的只是修改默认动态库定义:

#define COMMONDLG_DLL_FILENAME "CommonDlg.dll" // 动态库的文件名

    再如下面的形式为你的动态定义导出函数的原型:

typedef int (__stdcall *PShowLoginDlg)(HWND, LPSTR szUser, LPSTR szPassword);

    并在CCommonDlg类定义中增加一个 static 类型的函数指针数据成员:

protected
  
static PShowLoginDlg m_pShowLoginDlg; // 引出函数


        示范例子中将该函数定义在 protected 区域中,并对该函数进行二次封装成 ShowLoginDlg 调用,当然,如果没有什么必要,你也可以将它定义在 public 区域中,我们可以直接访问该导出函数了,如:

public:
  
static PShowLoginDlg ShowLoginDlg;

        然后在 LoadProcAddress 函数的实现中编写该函数的导出代码:

// 用于加载动态库的所有引出函数
// 在多个对象的实例中,该函数仅被调用一次
void CCommonDlg::LoadProcAddress()

  m_pShowLoginDlg 
= (PShowLoginDlg)GetProcAddress(m_hLib, "ShowLoginDlg");
}


    要更详细的了解其他细节问题,请下载示范工程,都在我编写的例子中。不多说了,睡觉……呼……呼……

 

posted on 2004-10-14 21:08  HeQing博客之家  阅读(1116)  评论(1编辑  收藏  举报