资料收集站

SDL

Tuesday
Jan 06th
Text size
  • Increase font size
  • Default font size
  • Decrease font size

编程资料

在C++ Builder中编写控制面板应用程序

---转载自《计算机世界日报》 (文/李进)

    当我们打开控制面板时,会看到一些控制面板项目,如“添加/删除程序”,“调制解调器”,“系统”等。我们经常需要通过这些项目来对Windows进行配置。使用C++Builder,能方便快速地开发出自己的控制面板应用程序。

---- 一:利用BCB的可视化编程和控制面板程序标准编程相结合,框架使用标准的控制面板程序编程,具体的功能和显示窗口直接利用C++Builder强大的可视化编程能力来生成。

---- 控制面板程序实际上就是一个DLL(动态链接库)文件,关键是它实现了CPlApplet函数。CPlApplet是一个回调(callback)函数,它处理所有发送给控制面板应用程序的消息。当控制面板应用程序运行起来时,调用它的程序从该控制面板应用程序中取得CPlApplet函数的地址,然后用该地址调用CPlApplet函数,并将消息传递给它。在处理消息时,必须按照一定的顺序进行。控制面板应用程序还有一个特点,就是一个DLL文件可以实现多个模块,每个模块可以有自己的图标、字符串资源,每一个模块对应于控制面板中的一个项目。

---- 首先我们来看看控制面板应用程序执行的过程。弄明白了这点,才能编写出正确的控制面板应用程序。

---- 由于控制面板应用程序是一个DLL文件,所以调用者必须使用LocaLibrary()函数来装载并执行。在控制面板应用程序装载时,CPlApplet 函数会接收到CPL_INIT消息,它表示控制面板应用程序正在初始化。这时CPlApplet 函数应该进行一些必要的初始化工作。如果初始化失败,CPlApplet 函数应该返回零值,通知调用者中止该控制面板应用程序的运行并释放占用的资源。该消息只发送一次。

---- 当CPlApplet 函数返回初始化成功的信息后,调用者会发送CPL_GETCOUNT消息给它,CPlApplet 函数应该返回控制面板应用程序中模块的个数。该消息只发送一次。

---- 随后,对于每个模块,CPlApplet 函数都会收到一条CPL_INQUIRE 和CPL_NEWINQUIRE消息。响应此消息,CPlApplet函数将填充CPLINFO或者NEWCPLINFO结构,其中存放了控制面板应用程序的名称、图标及描述信息。通常只需要处理CPL_INQUIRE消息,如果想更改图标和显示信息(如在不同的语言平台上显示不同的语言文字),则需要响应CPL_NEWINQUIRE消息。对于每个模块都会发送这两个消息一次。

---- 当CPlApplet 函数接收到CPL_DBLCLK消息时,表示将要运行相应的模块。这个消息可以多次发送,一般的,当使用者用鼠标双击某个图标时,就会发送一个CPL_DBLCLK消息。该消息包含了模块的识别号,因此CPlApplet 函数可以分辨出是需要执行那个模块的功能。

---- 在控制面板应用程序退出前,对于每一个模块CPlApplet函数都会接收到一条CPL_STOP消息,消息中包含该模块的识别号。此时CPlApplet函数需要针对不同的模块做不同的清理工作。

---- 在处理完最后一条CPL_STOP消息后,CPlApplet函数会收到CPL_EXIT消息,表示控制面板应用程序即将退出。在CPlApplet函数返回后,调用者将立即使用FreeLibrary()函数释放占用的资源。

---- 下面我们用C++ Builder来创建一个DLL文件,并编写CPlApplet函数代码。该控制面板应用程序包含一个模块,该模块的功能是显示一个窗口,窗口中显示“Hello World”。以下代码在BCB4专业版和BCB5企业版中调试通过。

  1. 首先选择菜单“File - > new ”,在出现的画面中选择“DLL Wizard”。
  2. 选择菜单中的“save project as”,保存项目文件和CPP文件到指定的目录下。项目文件名称使用默认project1。
  3. 定义控制面板应用程序在控制面板中显示的图标。这里我们使用RC资源文件。首先选择菜单中的“new- >Text file”,生成一个文本文件,然后将它另存为ico.rc文件。编辑该文件,在其中加入一行“ MYICON ICON icon1.ico”。

---- 将一个图标文件复制到程序所在的目录里,并将文件名改为 icon1.ico。或者用Image Editor创建一个。注意,这个图标大小应该为32X32(大图标)。

---- 在Project Manager中,将该RC文件加入到工程项目中。

  1. 加入一个新的Form,该Form将被作为窗口显示出来。

---- 选择菜单中的“file- >new”,从中选择Form。然后在Forn上加上一个Label对象,设置其Caption为“Hello World”。

  1. 编辑project1.cpp文件。

---- 首先需要包含头文件cpl.h,这样才可以使用一些常数,如CPL_INIT等消息。

---- 然后定义一个全局变量 gInstance。

---- 处理DLL的入口函数,将程序句柄保存到全局变量gInstance中。程序内容如下:

  #include  < vcl.h >
#include  < cpl.h >
	HINSTANCE 		gInstance;int WINAPI DllEntryPoint(HINSTANCE hinst, 
unsigned long reason, void*)
{
if (reason==DLL_PROCESS_ATTACH) gInstance=hinst;//gInstance 中存放着程序的句柄。
    return 1;
}
5:继续编辑project1.cpp文件,完成CPlApplet函数。
//  由于CPlApplet函数需要在DLL外部使用,所以必须被输出。
extern "C" int __stdcall __declspec(dllexport)
CPlApplet(HWND HwControlPanel,  int Msg,int lParam1,   int lParam2)
{
  switch (Msg) {
    case CPL_INIT:
 return true; 	// 返回初始化成功的信息
    case CPL_GETCOUNT:
return 1;	// 告诉调用者该控制面板应用程序包含一个模块
    case CPL_NEWINQUIRE: {
// 在这里设置图标,名称等信息
NEWCPLINFO *Info=(NEWCPLINFO *)lParam2;
ZeroMemory(Info,sizeof(NEWCPLINFO));
Info- >dwSize=sizeof(NEWCPLINFO);
// 图标用资源文件ico.rc文件中定义的MYICON
Info- >hIcon=LoadIcon(gInstance,"MYICON");
strcpy(Info- >szName,"模块名称");// szName为控制面板中该模块的名称
strcpy(Info- >szInfo,"模块详细说明" ); 
// szInfo为对该模块的说明。在控制面板
//中使用列表方式查看时,左面是模块的名称,右面是模块的说明。
return 0;
}
    case CPL_DBLCLK:
// 在这里加入需要实现的功能。这里我们只是简单的显示Form。
try
{
  Application- >Initialize();
Application- >CreateForm(__classid(TForm1), &Form1);
  Application- >Run();
}
catch (Exception &exception)
{
Application- >ShowException(&exception);
}
return 0;
    }
}

---- 需要注意的是,在BCB4中,用DLL Wizard会生成Project1.cpp文件,新加入的Form为Form1,对应CPP文件为Unit1.cpp;而在BCB5中,不会生成project1.cpp,而是生成了Unit1.cpp,新加入的Form为Form2,对应CPP文件为Unit2.cpp。如果使用的是BCB5,则需要对上述代码做修改。

  1. 好了,现在我们来编译该程序,成功后会生成一个project1.dll文件。为了让该DLL文件作为控制面板应用程序来运行,我们必须用以下两种方式之一:

---- ①将其扩展名修改为CPL,并将文件复制到Windows的系统目录下。对于Win9x,复制到c:\windows\system目录下,对于Winnt,复制到c:\winnt\system32下。

---- ②将其扩展名修改为CPL,并在c:\windows\control.ini文件的[MMCPL]部分,加入该文件的路径,形式为 “name=CPL文件的全路径”。

---- 现在打开控制面板,就会多了一项“模块名称”。

---- 二:利用C++Builder本身提供的控制面板应用程序类TappletModule

---- BCB5中提供了直接生成控制面板应用程序的功能。下面的例子生成一个控制面板应用程序,拥有两个模块,就是说,在控制面板中,会出现两个新的项目。

  1. 运行C++ Builder5,执行菜单中的“File- >New”,选择“Control Panel Application”。
  2. 在默认情况下,BCB5已经有了一个模块TAppletModule1,我们需要再增加一个模块TAppletModule2。
    执行菜单中的“File- >New”,选择“Control Panel Module”。模块1和模块2的CPP文件分别为Unit1.cpp和Unit2.cpp。
  3. 在“Object Inspector”中分别设置这两个模块的图标,名称。这里将TAppletModule1的Caption设置为“模块1”,TAppletModule2的Caption设置为“模块2”,这将是模块显示在控制面板中的名称。点击AppletIcon属性,为它们选择不同的图标。
  4. 分别在两个模块中编写对消息的处理函数。

---- TAppletModule提供了7个事件。其中OnActivate对应于CPL_DBLCLICK消息,OnInquire对应于CPL_INQUIRE消息,OnNewInquire对应于NEWINQUIRE消息,OnStop对应于CPL_STOP消息,OnCreate对应于CPL_INIT消息,OnDestroy对应于CPL_EXIT消息。还有一个OnStartWParms事件,如果控制面板应用程序是由RunDll(包括Rundll.exe和Rundll32.exe)启动的,则此事件被触发。例如我想运行控制面板中的“调制解调器”,你可以在命令行上运行:rundll32 shell32.dll,Control_RunDll modem.cpl ,这时OnStartWParms事件会被触发。如果是进入控制面板来运行“调制解调器”, OnStartWParms事件就不会被触发。

---- 为了增加功能和实现窗口,在两个模块中分别加入Form。对于每个模块,可以作为一个标准的BCB窗口程序来编写。

  1. 至此,一个控制面板应用程序就已经编写好了。编译后,将生成的project1.cpl复制到c:\windows\system目录下。下图是控制面板中的显示。模块1和模块2实际上是由一个控制面板程序实现的。

---- 从以上步骤可以看出,用BCB5编写控制面板应用程序非常的方便,所有的框架BCB已经自动的为你编写好了,你只需要决定使用几个模块,并针对每个模块来编写功能代码,从而避免了繁琐的消息处理。

 

用C++Builder创建数字签名

                 
高建武

  如果你在网络上传递一份数据,但却存在着种种不安全的因素,使你对数据能否原封不动地到达目的地而心存疑惑,这时,你就可以给数据加上数字签名,从而使对方可以通过验证签名来检查你所传过去的数据是否已被他人修改。

  一、程序原理

  数字签名的工作原理还是比较简单的,它是根据你所提供的原始数据,经过复杂的算法,产生特定的数据签名,对方通过同样的过程也产生签名,如果数据已被修改,那么就不可能得到两份一模一样的签名,从而就可判断数据已被他人修改。编程人员利用Windows的CAPI接口,就可以实现数据的加密、解密和数字签名。  

  二、程序清单

  下面用C++ Builder的语句来看一下它的具体实现过程。

  先来创建数字签名,假定其数据来自于一个文件。

  //变量声明:

  HCRYPTPROV hProv;

  // CSP的句柄

  HCRYPTHASH hHash;

  // 散列的句柄

  const int BUFFER=4096;

  // 缓冲区大小常数

  BYTE pBuffer[BUFFER];    

  // 存放读文件内容的缓冲区

  BYTE pSignature[256];

  // 存放签名的缓冲区

  DWORD dSignatureLen=256;

  // 签名的长度

  TFileStream *sourceFile;

  // 一个文件流

  if(!CryptAcquireContext(&hProv,NULL,NULL,PROV—RSA—FULL,0))

  // 连接默认的CSP,接受它的句柄放入hProv

  {    

    // 错误处理

  }

  if(!CryptCreateHash(hProv,CALG—MD5,0,0,&hHash))

  // 创建一个散列对象,得到它的句柄放入hHash

  {

    // 错误处理

  }

  do

  {

   dReadLen=sourceFile->Read(pBuffer,BUFFER);

   if(!CryptHashData(hHash,pBuffer,dReadLen,0))

  // 根据文件的内容计算散列值

   {

    // 错误处理

   }

  }while(!(dReadLen<BUFFER));

  if(!CryptSignHash(hHash,AT—SIGNATURE,NULL,0,pSignature,&dSignatureLen))

  //使用私人密钥对散列值进行数字签名

  //签名数据放入pSignature,长度放入dSignatureLen

    // 错误处理

  }

  对基于文件的数据签名进行检验。

  //变量声明:

  HCRYPTPROV hProv;

  // CSP的句柄

  HCRYPTHASH hHash;

  // 散列的句柄

  HCRYPTKEY hPublicKey;    

  // 公共密钥的句柄

  const int BUFFER=4096;  

  // 缓冲区大小常数

  BYTE pBuffer[BUFFER];    

  // 存放读文件内容的缓冲区

  TFileStream *sourceFile; // 一个文件流

  BYTE pSignature[256];    

  // 上一段得到的签名的缓冲区

  DWORD dSignatureLen;    

  // 上一段得到的签名的长度

  if(!CryptAcquireContext(&hProv,NULL,NULL,PROV—RSA—FULL,0))

  // 连接默认的CSP,接受它的句柄放入hProv

  {

    // 错误处理

  }

  if(!CryptGetUserKey(hProv,AT_SIGNATURE,&hPublicKey); // 得到公共密钥的句柄

  {

    // 错误处理

  }

  if(!CryptCreateHash(hProv,CALG—MD5,0,0,&hHash)) // 创建一个散列对象,得到它的句柄放入hHash

  {

    // 错误处理

  }

  do

  {

   dReadLen=sourceFile->Read(pBuffer,BUFFER);

   if(!CryptHashData(hHash,pBuffer,dReadLen,0))

  // 根据文件的内容计算散列值

   {

    // 错误处理

   }

  }while(!(dReadLen<BUFFER));

  if(!CryptVerifySignature(hHash,pSignature,dSignatureLen,hPublicKey,NULL,0))

  {

    if(GetLastError()==NTE—BAD—SIGNATURE) ShowMessage(″文件已被修改″);

  }

  else

  {

   ShowMessage(″文件没被修改″);

  }

  以上是一个数字签名的简单实现,得到的签名数据可以单独保存,也可以分开保存。

转载自编程联盟

用C++Builder开发动画DLL

    作者:刘 学 平

我 们 在Windows98 环 境 下 执 行 拷 贝 文 件、 查 找 文 件 等 计 算 机 耗 时 较 长 的 操 作 时,Windows 会 显 示 一 个 小 小 的 动 画, 指 示 正 在 进 行 的 操 作, 与 死 板 的 静 止 图 像 相 比 增 色 不 少。 那 么 我 们 自 己 开 发 软 件 时, 能 否 也 显 示 一 个 这 样 的 动 画 提 示 呢 ? 笔 者 开 发 了 一 个 能 够 在PB 下 调 用 的 动 画DLL, 由 于 采 用 多 线 程 编 程,PB 调 用 的DLL 函 数 能 够 及 时 将 控 制 权 交 还 给PB, 不 影 响 应 用 系 统 的 运 转。

一、 代 码 与 编 译 选 项

  1. 在C + +Builder 中 创 建 一 个 空 白 的DLL 项 目。
  2. 创 建 一 个 空 白 的Form, 修 改 它 的 属 性 为:

           BorderStyle=bsDialog
            BorderIcons 的 子 属 性 均为False
            FormStyle=fsStayOnTop
            Position= poScreenCenter
            Name=StatusForm

  3. 在Form 上 添 加 一 个Win32 下 的Animate 控 件Animate1, 修 改 它 的 属 性 为

           Align=alTop

  4. 在Form 上 添 加 一 个Standard 下 的Button 控 件Button_Cancel, 再 添 加System 下 的Timer 控 件Timer1, 设 置 定 时Interval 时 间 位250, 较 快 响 应 用 户 的 取 消 请 求。

----因 为PB 应 用 系 统 与 动 画 窗 体 代 码 分 别 属 于 两 个 线 程, 不 能 采 用PB 线 程 直 接 关 闭 动 画 窗 体 线 程 的 窗 口, 否 则 会 引 起 系 统 运 行 不 正 常, 因 此 采 用PB 线 程 设 置 关 闭 标 志, 而 动 画 线 程 采 用Timer 控 件 定 时 检 查 标 志, 一 旦 检 测 到 关 闭 标 志, 就 关 闭 窗 口, 清 除 线 程 标 志, 结 束 动 画 线 程。

----5. 下 面 给 出 编 码 及 编 码 原 理:

----(1)DLL 主 体 代 码:

 / *DLL 主 体 代 码
  * 定 义DLL 公 用 变 量
   *g_CommonAVI  对Animate 控 件
       动 画 类 型 索 引
  *gi_Canceled    Button_Cancel 
      按 钮 是 否 被 选 择 过
  *gi_AVIType     要 显 示 的 动 画 类 型,
      由DLL 输 出 函 数 做 为 参 数 输 入
  *gi_RequestClose  请 求 动 画 线 程 关 闭 标 志
  *gi_WindowActive  动 画 窗 口 所 处 的 状 态
  *lpsWinTitle    动 画 窗 体 的 标 题,
      由DLL 输 出 函 数 做 为 参 数 输 入
  */

    TCommonAVI g_CommonAVI[]={
              aviNone, aviFindFolder,
              aviFindFile, aviFindComputer,
              aviCopyFiles, aviCopyFile,
               aviRecycleFile, aviEmptyRecycle,
              aviDeleteFile
    };
    int gi_Canceled=0,gi_AVIType=0;
    int gi_RequestClose=0,gi_WindowActive=0;
    char lpsWinTitle[256];
    HWND hWndParent=NULL;

    / * 定 义DLL 输 出 函 数 */
    extern “C" __declspec(dllexport) int pascal Dll 
     EntryPoint(HINSTANCE hinst, unsigned 
                 long reason, void *);
    extern “C" __declspec(dllexport) int pascal
 ShowStatus Window
 (int AVIType,LPSTR WinTitle,long hWnd);
    extern “C" __declspec(dllexport) 
            int pascal GetStatus(int ai_CloseWin);
    extern “C" __declspec(dllexport)
            int pascal CloseStatusWindow();

     / * 定 义 线 程TformThread: */
    class TFormThread : public TThread{
    public:           // User declarations
    __fastcall TFormThread(bool CreateSuspended);
    void __fastcall Execute(void);
    };
    __fastcall TFormThread::
      TFormThread(bool CreateSuspended):
      TThread(CreateSuspended){
    }
 / * 动 画 线 程 执 行 代 码,
     动 画 窗 体 的 定 时 器 控 件 会 关 闭 它,
     清 除 窗 体 存 在 标 志 后 结 束 线 程 的 运 行
 */
    void __fastcall TFormThread::Execute(void){
        gi_WindowActive=1;
         StatusForm=new TStatusForm(NULL);

         StatusForm ->Caption=lpsWinTitle;
        StatusForm ->ShowModal();
         gi_WindowActive=0;
        delete StatusForm;
         gi_RequestClose=0;
    }
    / * 定 义 一 个 线 程 实 例 指 针 */
    TFormThread *FormThread;
     / * 输 出 函 数 代 码 实 现 部 分
     *  DllEntryPoint       32 位DLL 入 口
     *  ShowStatusWindow  显 示 动 画 窗 口,
           它 通 过 创 建 一 个 线 程 来 创 建 窗 口,
           避 免 由 于 窗 口 的MODAL 属 性 而 使
           控 制 权 不 能 及 时 的 返 还 给 调 用 者
     *  GetStatus            取 得“ 取 消” 状 态,
           即 用 户 有 没 有 选 择“ 取 消” 按 钮
     *  CloseStatusWindow   关 闭 动 画 窗 口,
     */
    __declspec(dllexport) int WINAPI DllEntryPoint 
      (HINSTANCE hinst, unsigned long reason, void *)
    {
       return 1;
    }

  __declspec(dllexport) int pascal ShowStatusWindow
    (int AVIType,LPSTR WinTitle,long hWnd){
 hWndParent=(HWND)hWnd;
   memset(lpsWinTitle,0,sizeof(lpsWinTitle));
 strncpy(lpsWinTitle,WinTitle,sizeof(lpsWin Title) -1);
   if (AVIType>0 & & AVIType<=8) gi_AVIType="AVIType;"     FormThread="new" TFormThread(true);       FormThread ->Priority = tpNormal;
        FormThread ->Resume();
    }

  __declspec(dllexport) int pascal GetStatus
    (int ai_CloseWin){
       if (gi_Canceled)
          if (gi_WindowActive){
             gi_RequestClose=1;
               while(gi_RequestClose);
          }

        return gi_Canceled;
    }

    __declspec(dllexport) int pascal CloseStatusWindow(){
       if (gi_WindowActive){
          gi_RequestClose=1;
           while(gi_RequestClose);
       }

        return gi_Canceled;
    }

----(2) 窗 体StatusForm 的 代 码:

    TStatusForm *StatusForm;
    extern int gi_Canceled;
    extern int gi_AVIType;
    extern TCommonAVI g_CommonAVI[];
    __fastcall TStatusForm::TStatusForm
      (HWND ParentWindow)
        : TForm(ParentWindow)
    {
      gi_Canceled=0;
    }
    // 取 消 按 钮 并 不 直 接 关 闭 窗 体,
     而 指 示 设 置 取 消 标 志, 供 调 用 者 查 看
    void __fastcall TStatusForm::Button_CancelClick
       (TObject *Sender)
    {
       gi_Canceled=1;
    //    ModalResult=mrCancel;
    }
      // 激 活 动 画, 在FORMCREATE 事 件 中
    void __fastcall TStatusForm::FormCreate
       (TObject *Sender)
    {
      Animate1 ->CommonAVI=g_CommonAVI[gi_AVI 
Type];
     Animate1 ->Active = true;
    }
  
  extern int gi_RequestClose;
  // 定 时 器 事 件 检 测 到 结 束 标 志 关 闭 窗 体
  void __fastcall TStatusForm::Timer1Timer
    (TObject *Sender)
    {
       if (gi_RequestClose){
         ModalResult=mrOk;
       }
    }

v6. 设 置 编 译 选 项: 打 开Project Options 对 话 框, 清 除Linker 属 性 页 中 的 Use Dynamic RTL 标 志, 清 除Packages 属 性 页 中 的Build with runtime packages。 这 样 只 要 单 个DLL 就 可 以 运 行 了, 而 不 必 安 装 一 些 动 态 连 接 运 行 时 间 库。

二、 使 用 动 画DLL

----1. 定 义:

    //Declare -> Global External Functions
    FUNCTION Long ShowStatusWindow
      (Long  AVIType,String WinTitle,long hWnd)
      &LIBRARY “STATWIN.DLL" ALIAS FOR
     “Show StatusWindow"

    FUNCTION Long GetCancelStatus
      (Long  CloseWindow) &LIBRARY
     “STATWIN.DLL" ALIAS FOR “GetStatus"

    FUNCTION Long CloseStatusWindow() &
  LIBRARY “STATWIN.DLL" ALIAS FOR
   “CloseStatusWindow"

----2. 调 用:

    long ll_EndTime
    // 显 示 查 找 文 件 夹 动 画
    ShowStatusWindow(2)
    setpointer(HourGlass!)

    ll_EndTime = Cpu() + 10 * 1000
    DO
      if GetCancelStatus(0)=1 then
        exit
     end if
      // 做 想 做 的 事 情
    LOOP UNTIL cpu() > ll_EndTime

    CloseStatusWindow()

转载自中国程序员网站

C++Builder 中动态库的链接问题

转载自《电脑报》 (文/)

    动态库链接到应用程序中主要有两种方式:隐式链接和显式链接。隐式链接是常用方式。

  如果应用程序和动态库是分别在不同开发平台上编制的,动态库的导入库(lib文件)可能会与应用程序的开发平台所要求的导入库格式不相容,从而在应用程序与动态库隐式链接时,出现程序链接错误:contains invalid OMF record。例如在C++Builder开发平台上链接Visual C++制作的动态库时,就会出现上述的错误。解决这一问题,可以采用两种方法:显式连接法和使用C++Builder中提供的导入库生成工具。

  显式连接:显式连接不需要在工程中加入导入库和相应的头文件,只需要把动态库放入指定的目录下。在应用程序中通过函数调用显式的装载和卸掉DLL,通过函数指针来调用DLL的导出函数。

  步骤:

  1. 调用LoadLibrary函数装载DLL并得到模块句柄;

  2. 调用GetProcAddress函数获取指定导出函数的指针;

  3. 用这个函数的指针调用该函数;

  4. 使用完毕后,用FreeLibrary释放DLL。

  例子:

  用VC++制作一个动态库:

  选new→Project→Win32—Dynamic—Link Library,加入addit.cpp

  extern ″C″

  { void —declspec( dllexport ) addit(int a, int b, int *c)

  { *c = a + b;

   }

  }

  addit.h

  extern ″C″

  {void addit(int a, int b, int *c);}

  编译链接生成addit.dll和addit.lib。在C++Builder程序中调用addit函数。

  C++Builder程序中:

  {HINSTANCE handle; //DLLa模块的句柄

   FARPROC lpFarProc;

  void (*lpaddit)(int,int,int *);

//指向addit函数的指针

  int ntemp;

  handle = LoadLibrary(″addit.dll″);

  //装载addit.dll, 得到该库句柄

  //addit.dll位于当前目录下

  lpFarProc = GetProcAddress(handle,″addit″);

  //得到指向函数addit的指针

  lpaddit = (void(—cdecl *)(int, int, int *))lpFarProc; //指针类型转换

  lpaddit(2,3,&ntemp); //使用addit函数

  FreeLibrary(handle);

  //将addit.dll从程序中卸掉

  }

  此程序在VC++ 5.0 和C++Builder 3.0下通过。

  使用C++Builder中提供的导入库生成工具:先预处理,后隐式链接。

  步骤:

  1.用C++Builder提供的implib.exe工具重新生成该动态库(xxx.dll)的导入库(xxx.lib)。命令如下:

  implib addit.lib addit.dll。

  addit.dll为已有动态库,addit.lib为要生成的导入库。由此生成的导入库addit.lib格式与C++Builder开发平台是相容的;

  2.在动态库的头文件addit.h中,对其输出函数重新说明,语句如下:

   extern —stdcall void addit( int a, int b, int *c);

  3.然后采用隐式链接法,将重新生成的导入库(addit.lib)和重新说明的头文件(addit.h)加入到C++Builder应用程序的工程项目中,进行编译和连接。

  addit.dll按上述步骤操作,在VC++ 5.0 和C++Builder 3.0下通过。

用C++ Builder编写Tray程序

  Tray(托盘)是Windows9x任务条上的一个特殊区域,它的技术名称为“任务栏布告区”,一些软件(如金山词霸Ⅲ)运行时会在托盘上放置一个图标,使用户一眼就能知道这个程序正在后台运行,要想激活它也很容易,通常只需单击一下这个图标即可,非常方便。   

  Tray的编程比较特殊,但并不难,主要包括图标、工具提示和消息等三个方面,它是Shell编程的一部分。ShellAPI提供了Shell—NotifyIcon函数,用它可以增加、删除或者修改托盘中的图标,在托盘上放置图标后,Windows Shell会负责把发生在图标上的鼠标事件通知应用程序。Shell—NotifyIcon函数定义如下:   

  WINSHELLAPI BOOL WINAPI Shell—NotifyIcon(DWORD dwMessage,PNOTIFYICONDATA pnid);   

  dwMessage表示要完成的操作:NIM—ADD(增加图标)、NIM—DELETE(删除图标)、NIM—MODIFY(修改图标或提示文本),pnid是一个指向NOTIFYICONDATA结构的指针,结构的定义如下:   

  typedef struct —NOTIFYICONDATA{  

  DWORD cbSize;//结构所占的字节数,必须用结构的大小来初始化。   

  HWND hWnd;//接受Tray图标消息的窗口句柄  

  UINT uID;//由应用程序定义的图标ID  

  UINT uFlags;//用来鉴别那些需要改变其值的域,NIF_ICON表示hIcon有效,可用来修改图标,NIF_MESSAGE表示uCallbackMessage有效,用来定义消息,NIF—TIP表示szTip参数有效,可修改工具提示。   

  UINT uCallbackMessage;//应用程序定义的消息  

  HICON hIcon;//Tray图标的句柄  

  char szTip[64];//工具提示的文本  

  }NOTIFYICONDATA;  

  下面我们就通过一个具体例子来说明实现方法,程序运行时不会显示主窗体,只在托盘上增加一个图标,双击图标可关闭程序。   

  程序运行时托盘区显示如下:  

  新建一个工程,放置一个Timer控件到窗体上。打开unit1.h文件,增加头文件说明#include <shellapi.h>,在TForm1定义的private段增加一些数据成员和方法的声明:   

  unsigned int iconmessage;//定义的消息  

  void AddTrayIcon();//在托盘上增加图标  

  void RemoveTrayIcon();//从托盘中删除图标  

  由于要增加对自定义消息的处理,所以必须重载窗口过程函数WndProc,在TForm1的定义中增加protected段:virtual void ——fastcall WndProc(Messages::Tmessage& Message);  

  在unit1.cpp中定义相应的成员函数:  

  void TForm1::AddTrayIcon()  

  {  

  NOTIFYICONDATA icondata;  

  memset(&icondata,0,sizeof(icondata));  

  //将结构icondata的各域初始化为0  

  icondata.cbSize=sizeof(icondata);  

  icondata.hWnd=Handle;  

  strncpy(icondata.szTip,″未知状态″,sizeof(icondata.szTip));  

  icondata.hIcon=Application->Icon->Handle;  

  icondata.uCallbackMessage=iconmessage;  

  icondata.uFlags=NIF—MESSAGE|NIF—ICON|NIF—TIP;  

  Shell—NotifyIcon(NIM—ADD,&icondata);  

  }  

  void TForm1::RemoveTrayIcon()  

  {  

  NOTIFYICONDATA icondata;  

  memset(&icondata,0,sizeof(icondata));  

  icondata.cbSize=sizeof(icondata);  

  icondata.hWnd=Handle;  

  Shell—NotifyIcon(NIM—DELETE,&icondata);  

  }  

  重载TForm1的WndProc函数,加入对自定义消息的处理代码,这其实相当于创建了TForm类的子类。   

  void __fastcall TForm1::WndProc(Messages::TMessage& Message)  

  {  

  if(Message.Msg==iconmessage)  

  {  

   if(Message.LParam==WM—LBUTTONDBLCLK)  

   {  

   Application->Terminate();  

  //如果双击图标,则关闭应用程序  

   }  

   return;  

  }  

  TForm::WndProc(Message);//对于其他的消息,调用基础类的WndProc函数让Windows进行缺省处理。   

  }  

  创建窗体的OnCreate事件句柄:  

  void ——fastcall TForm1::FormCreate(TObject *Sender)  

  {  

  iconmessage=RegisterWindowMessage(″IconNotify″);  

  AddTrayIcon();  

  }  

  这里通过调用RegisterWindowMessage函数来定义一个用户消息,也可以通过WM_USER+n来获得一个系统没有使用的消息编号。   

  void ——fastcall TForm1::FormDestroy(TObject *Sender)  

  {  

  RemoveTrayIcon();  

  //窗体在关闭时删除托盘中的图标  

  }  

  编写Timer1的Timer事件代码,当用户将鼠标停留在图标上时,显示提示文本:   

  void ——fastcall TForm1::Timer1Timer(TObject *Sender)  

  {  

  NOTIFYICONDATA icondata;  

  memset (&icondata, 0, sizeof (icondata));  

  icondata.cbSize = sizeof (icondata);  

  icondata.hWnd = Handle;  

  String s=″我的图标!″;//定义提示文本  

  strncpy (icondata.szTip, s.c_str(), sizeof (icondata.szTip));  

  icondata.uFlags = NIF—TIP ;  

  Shell—NotifyIcon (NIM—MODIFY,&icondata);  

  }  

  程序运行时不显示主窗体,只在托盘上放置相应的程序图标,从C++ Builder主选单中选择View|Project Source,在WinMain函数的Application→Initialize()语句后增加代码:   

  ShowWindow(Application→Handle,SW—HIDE);  

  Application→ShowMainForm=false;  

   按F9编译并运行程序,托盘上就会出现相应的图标。以上代码在C++ Builder3、Pwin98环境下编译、运行通过。 

用C++ Builder创建基于Internet的点对点Chat

  建基于Internet的应用程序,你也许会想到复杂的WinSock编程。不过,C++Builder3提供了新的WebBroker的Internet套件,其中的TClientSocket和TServerSocket组件封装了Windows的有关API,大大简化了WinSock编程。要通过Internet传输数据,至少需要一对Socket,一个Socket在客户端,另一个Socket在服务器端。其实TClientSocket、TServerSocket组件并不是Socket对象,其属性Socket将返回各自的Socket对象。TClientSocket用来处理客户端到服务器端之间的socket连接,TServerSocket用来处理由客户端发来的socket连接,一旦客户端和服务器端都接通了socket,客户端和服务器端就可以相互通信了。

  建立一新项目,创建应用程序的用户界面:

  1.将组件页切换到Internet页,放一个TServerSocket组件和一个TClientSocket组件到窗体上,这样应用程序既可以是TCP/IP服务器,也可以是TCP/IP客户。将Port属性都设为同一个值(如1000),确定Socket之间的连接类型为NonBlocking(非阻塞方式)。

  2.放两个TMemo组件到窗体上,用来分别显示双方的谈话内容,将Memo2的ReadOnly属性设为True。

  3.在窗体的顶部放上一个Panel组件,在其上放三个按钮:监听(btnlisten)、连接(btnconnect)、断开(btndisconnect),用来启动相应的操作。

  4.在窗体底部放一个StatusBar组件,将其SimplePanel属性设为True,在相应的事件处理程序中改变状态条信息,让用户随时了解连接状态。

  打开头文件,在窗体类的Private段添加两个私有成员: bool IsServer;String Server。双方通信时需同时运行Chat程序,IsServer用来确定哪个Chat程序处于服务器端,Server用来存放服务器的主机名。建立窗体类的构造器如下:

__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
IsServer=false;
Server="localhost";
}

  这里Server被缺省设为localhost,这样程序可以在没有连入Internet的单机上进行调试。在Windows子目录下你可以找到hosts.sam文件中,在该文件中已经将本机IP地址127.0.0.1定义了主机名:localhost。
void __fastcall TForm1::FormCreate(TObject *Sender)
{
btndisconnect- >Enabled=false;
}

  程序运行后,如果用户按下"监听"钮,则将该程序设为服务器端,这时应将TServerSocket的Active属性设为True,使服务器自动进入监听状态。
void __fastcall TForm1::btnlistenClick(TObject *Sender)
{
ClientSocket1- >Active=false;
ServerSocket1- >Active=true;
StatusBar1- >SimpleText="正在监听...";
btnlisten- >Enabled=false;
btnconnect- >Enabled=false;
}

  当用户按下"连接"钮后,程序会弹出一个询问框,要求用户输入要连接的服务器的主机名,然后建立连接。
void __fastcall TForm1::btnconnectClick(TObject *Sender)
{
if(InputQuery("连接到服务器","输入服务器地址:",Server)){
if(Server.Length() >0){
ClientSocket1- >Host=Server;
ClientSocket1- >Active=true;
btnlisten- >Enabled=false;
btnconnect- >Enabled=false;
btndisconnect- >Enabled=true;
}
}
}

  当用户提出连接请求后,客户端会触发OnCreate事件,程序先在状态条中显示连接信息,然后将显示对方谈话内容的Memo2清空,准备开始交谈。
void __fastcall TForm1::ClientSocket1Connect(TObject *Sender,
TCustomWinSocket *Socket)
{
StatusBar1- >SimpleText="连接到:"+Server;
Memo2- >Lines- >Clear();
}

  在服务器接受了客户的请求后会触发OnAccept事件,在这个事件处理程序中将标志服务器端的变量IsServer设为True,并准备开始交谈。
void __fastcall TForm1::ServerSocket1Accept(
TObject *Sender,
TCustomWinSocket *Socket)
{
Memo2- >Lines- >Clear();
IsServer=true;
StatusBar1- >SimpleText="连接到:"
+Socket- >RemoteAddress;
}

  在建立连接后,双方就可以在Memo1中输入谈话内容开始进行交谈了,按下Enter键后,将所在行的文本发送出去。服务器端的Socket的Connections属性返回一个数组,该数组由服务器当前活动的连接组成。
void __fastcall TForm1::Memo1KeyDown(
TObject *Sender, WORD &Key,
TShiftState Shift)
{
if(Key==VK_RETURN){
if(IsServer)
ServerSocket1- >Socket- >Connections[0]- >SendText(
Memo1- >Lines- >Strings[Memo1- >Lines- >Count-1]);
else
ClientSocket1- >Socket- >SendText(
Memo1- >Lines- >Strings[Memo1- >Lines- >Count-1]);
}
}

  在本例中我们采用非阻塞传输方式,当其中的一方进行写操作时,另一方会触发OnRead事件(客户端)或OnClientRead事件(服务器端),这两个事件的处理程序只是将接收到的内容添加到Memo2的后面。
Memo2- >Lines- >Add(Socket- >ReceiveText());

  如果在用户建立连接后单击"断开"钮,将断开客户端与服务器的连接,服务器端将触发OnClientDisconnect事件,而客户端则会触发OnDisconnect事件,这时服务器端应回到监听状态,等待用户的连接;而客户端将返回到连接前的状态,等待用户再次建立连接,如果有不止一个服务器的话,可以选择连接到其他的服务器上。
void __fastcall TForm1::btndisconnectClick(
TObject *Sender)
{
ClientSocket1- >Close();
}
void __fastcall TForm1::ServerSocket1ClientDisconnect(
TObject *Sender,
TCustomWinSocket *Socket)
{
StatusBar1- >SimpleText="正在监听...";
}
void __fastcall TForm1::ClientSocket1Disconnect(
TObject *Sender, TCustomWinSocket *Socket)
{
btnlisten- >Enabled=true;
btnconnect- >Enabled=true;
btndisconnect- >Enabled=false;
StatusBar1- >SimpleText="";
}

  此外在客户端还应该增加错误捕获机制,当用户输入无效的服务器名或服务器端没有处于监听状态时能够及时给用户反馈信息。
void __fastcall TForm1::ClientSocke
t1Error(TObject *Sender,
TCustomWinSocket *Socket,
TErrorEvent ErrorEvent, int &ErrorCode)
{
StatusBar1- >SimpleText="无法连接到:
"+Socket- >RemoteHost;
ErrorCode=0;
}
  以上代码在C++ Builder3 C/S版中编译、运行通过。

C++BUILDER让你的任务栏图标动起来

 

 在windows环境下上网时,你有没有注意到在屏幕的右下脚的任务栏上有一个动画图标呢?它一闪一闪的,形象的表示出网络此时正在传输数据。关于任务栏图标编程的文章有不少,可是如何才能编制出动态图标呢?在C++Builder中可以比较方便的实现。

  其基本编程思路是:通过设置Timer时钟控件使应用程序在规定的时间间隔内发送特定的消息,使任务栏图标不断更改,从而形成动画效果。实现方法为在应用程序的表单中加载几个Image控件,使他们装载相应的图画,几幅图画按顺序连续的被显示,就形成了动画。

  在这里,我们用一个门的开关动画来做例子,在表单上放置一个Timer控件,两个Image,分别装载“开门”和“关门”两幅图。开始加入代码。

  应用程序必须用发送消息的办法通知任务栏增加,删除,和修改图标。发送消息必须
调用Shell_NotifyIcon。它的原形为:

  WINSHELLAPI BOLL WINAPI Shell_NotifyIcon(
  DWORD dwMessage, POINTIFYCONDATA pnid);

  第一个参数 dwMessage是发送消息的标志,可以选
  NIM_ADD // 往任务栏通知区添加图标
  NIM_DELETE //往任务栏通知区删除图标
  NIM_MODIFY //通知任务栏通知区修改图标

编制消息发送函数TrayMessage

bool __fastcall TForm1::TrayMessage(DWORD dwMessage)
{
NOTIFYICONDATA tnd;
PSTR pszTip;
pszTip = TipText();
tnd.cbSize= sizeof(NOTIFYICONDATA);
//结构的大小
tnd.uCallbackMessage    = MYWM_NOTIFY;
//自定义回调消息,在头文件中声明
tnd.hWnd= Handle;
//接受回调消息的窗口句柄
tnd.uID = IDC_MYICON;
//图标标志号
tnd.uFlags= NIF_MESSAGE | NIF_ICON | NIF_TIP;
//指定以下三个参数哪个包含有效数据
if (dwMessage == NIM_MODIFY)
{
tnd.hIcon        =
(HICON)IconHandle(); //取得图标句柄
if (pszTip)
lstrcpyn(tnd.szTip, pszTip,
sizeof(tnd.szTip));
    else
tnd.szTip[0] = '\0';
}
else
{
tnd.hIcon = NULL;
tnd.szTip[0] = '\0';
}
return (Shell_NotifyIcon(dwMessage, &tnd));
}
编制取得图标句柄的函数
HICON __fastcall TForm1::IconHandle(void)
{
if (n==1)
{ return (Image1- >Picture->Icon- >Handle);
//n是全局变量,1为显示Image1,0为Image2
}
else
{ return (Image2- >Picture- >Icon- >Handle);
}
}
编制图标状态转换函数

void __fastcall TForm1::ToggleState(void)
{
if (n==1) //n为图标句柄锁,是全局变量,
1为显示Image1,0为Image2
{
n=n-1;
}
else
{
n=n+1;
}
TrayMessage(NIM_MODIFY);
//发送图标变换消息
}


  对Timer控件编制代码,设它的Interval属性为1000,即定时器每一秒响应一次。为 Ontimer事件键入代码:

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{ ToggleState( );
}

  由于篇幅有限,以上只列出了基本部分的代码,其他功能的实现,如关闭程序,打开窗口等,比较简单,不在赘述。程序运行时,你将看到在屏幕的右下角任务栏有一扇门打开又关闭的动画图标。是不是很有趣,快编一个你喜欢的吧。
转载自C++Builder园地

  • «
  •  Start 
  •  Prev 
  •  1 
  •  2 
  •  Next 
  •  End 
  • »
Page 1 of 2

Google 搜索

在线用户

We have 33 guests online