CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
不看会后悔的Windows XP之经验谈 简单快捷DIY实用家庭影院
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  VC/MFC >  基础类

常见问题总结4

楼主yintongshun(左岸思雨)2003-12-02 11:59:34 在 VC/MFC / 基础类 提问

 
  在VC中如何将数据导入Word中  
     
  [03-9-10   10:36]     作者:何鸿鹏      
        在给企业开发的项目中,客户经常要求将数据汇总显示并打印。站在程序员的立场上,简单数据的汇总打印可以采用VB自带报表,对复杂的数据用水晶报表或第三方打印控件。在企业中,汇总数据另一个目的是为了便于交流和共享资源,报表的格式通常固定,内容可以自己调整,他们对   Excel   和   Word   情有独钟。  
   
    刚完成的项目中,客户要求将汇总的数据以三种方式进行显示和打印:AutoCad、Excel   和   Word   文档中。前两种方式网上资源很多,对   Word   文档我找到例子不是很多。由于需要和   AutoCad   开发程序结合,不能将   Word   文档以   OLE   的方式   嵌入到程序中,文档只能在内存中形成,这将占用大量的CPU资源,另开一个线程进行处理,线程间以发送消息的方式进行通信。  
   
    首先在VC引入Word,   对格式相同的地方一定使用模版的方式进行处理,这可以大大的提高运行速度,Word中的模版为*.dot。加载模版的代码为:    
   
    sDocs=sApp.GetDocuments();  
    sDocs.AttachDispatch(sApp.GetDocuments(),true);  
    COleVariant   vFalse((long)0),vTrue((long)1);    
    sDoc.AttachDispatch(sDocs.Add(COleVariant(_variant_t(strFileName)),   vFalse,vFalse,   vTrue));    
   
    需要注意的是,如果Word中使用到表格,VBA录制的全是Selection对象的处理方法,在VC中使用Selection对象的MoveUp和MoveDown函数时会出现不确定现象(如果你知道为什么的话请告诉我)。如果你需要绘制表格,不要用程序来动态形成,绘制表格的代码是很繁琐的,而且在页眉中绘制单元格是很难控制的。可以先在模版中绘出你要的表格格式,然后用程序复制单元格或者直接使用模版中的单元格。由于我做的项目表格的行数不确定,我采用的是复制单元格的方式(速度比较慢,谁有更好的方法?)。  
   
    剩下的处理流程,就是你想做什么操作,先在Word中录制一段宏,然后查看其VBA代码,并将其转化到Vc程序中。这就看你Word使用的熟练程度了,例如如何把不同页的页眉页脚设置不同,如何得到当前页的页数以及总的文档的页数,具体可以参考程序。  
   
    VC中也可以像Excel中调用模版中存在的宏,代码如下:  
    VARIANT   vtMissing   ;vtMissing.vt   =VT_ERROR;    
    vtMissing.scode   =DISP_E_PARAMNOTFOUND;  
    sApp.Run("AddCell",&vtMissing,&vtMissing,&vtMissing,    
    &vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,    
    &vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,    
    &vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,    
    &vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing,&vtMissing);  
   
   
  问题点数:0、回复次数:10Top

1 楼yintongshun(左岸思雨)回复于 2003-12-02 12:00:33 得分 0

控制远程计算机拨号上网并获取其IP地址  
   
  ----   本   文   提   出   采   用Win98   拨   号   网   络   服   务   器、FTP   协   议   和   免   费   个   人   主   页   空   间   站   点   实   现   控   制   远   程   计   算   机   拨   号   上   网   并   获   取   其IP   地   址   的   方   法,   该   方   法   在   实   施   远   程   维   护   等   方   面   具   有   较   高   的   实   用   价   值。    
   
  一、   问   题   的   提   出    
  ----许   多   计   算   机   系   统   需   要7   ×24   小   时   稳   定   可   靠   运   行,   当   出   现   突   发   事   件   时,   要   求   能   对   系   统   实   施   远   程   系   统   维   护   以   便   排   除   故   障,   此   时   就   需   要   本   地   计   算   机   和   远   程   系   统   建   立   通   信   连   接,   通   过   相   应   的   工   具   软   件   实   施   远   程   维   护。   在   建   立   远   程   通   信   连   接   时,   通   常   采   用Modem   拨   号   方   法   将   远   端Modem   置   为   自   动   应   答   方   式,   由   本   地Modem   通   过   公   用   电   话   网   发   起   呼   叫   来   建   立   通   信   连   接。   当   主、   被   叫Modem   所   在   地   之   间   的   长   途   费   率   较   高   时,   经   常   的   远   程   维   护   将   带   来   较   高   的   长   途   话   费   开   支。   本   文   提   出   通   过Internet   建   立   两   台   计   算   机   之   间   通   信   的   方   法。    
   
  二、   通   知   远   程   计   算   机   拨   号   上   网    
  ----远   程   计   算   机   安   装Win98   操   作   系   统,   在   安   装   了   拨   号   网   络   服   务   器   组   件   后,   便   可   配   置   为   一   台   拨   号   网   络   服   务   器,   供   本   地   计   算   机   通   过   电   话   网   拨   号   建   立   通   信   连   接,   并   访   问   远   程   计   算   机   上   的   共   享   资   源。   在   拨   号   通   信   链   路   上   可   绑   定TCP/IP   等   通   信   协   议,Win98   拨   号   服   务   器   给   每   个   拨   入   计   算   机   分   配   一   个   独   立   的IP   地   址,   同   时   也   给   自   身   分   配   一   个IP   地   址,   这   些IP   地   址   具   有   相   同   的   网   络   编   号,   同   属   于   一   个   通   信   子   网,   服   务   器   的   主   机   编   号   为1,   拨   入   计   算   机   的   主   机   编   号   依   次   从2   开   始   编   起。   另   外,Win98   拨   号   服   务   器   还   提   供   了   密   码   保   护   功   能,   拨   入   计   算   机   只   有   在   提   供   正   确   的   密   码   情   况   下,   才   能   成   功   建   立   起   拨   号   连   接。    
   
  ----当   本   地   计   算   机   要   通   知   远   程   计   算   机   拨   号   登   录Internet   时,   首   先   通   过   电   话   拨   号   和   远   程   计   算   机   建   立   拨   号   连   接,   获   取   远   程   计   算   机   分   配   给   本   机   的IP   地   址,   并   经   处   理   得   到   远   程   计   算   机   的IP   地   址,   然   后,   和   远   程   计   算   机   建   立TCP/IP   连   接,   并   送   出   命   令   通   知   远   程   计   算   机   准   备   断   开   拨   号   连   接,   登   录Internet,   当   本   地   计   算   机   收   到   远   程   计   算   机   确   认   信   息   后,   便   可   中   断   和   远   程   计   算   机   的   拨   号   连   接,   也   准   备   登   录Internet。    
   
  三、   获   取   远   程   计   算   机Internet   IP   地   址    
  ----在   取   得   远   程   计   算   机Internet   IP   地   址   前,   本   地   计   算   机   是   无   法   通   过Internet   和   远   程   计   算   机   进   行   实   时   通   信   的。   远   程   计   算   机   登   录Internet,   获   取   动   态IP   地   址   后,   可   通   过   发   电   子   邮   件   方   式   将   动   态IP   地   址   通   知   本   地   计   算   机,   也   可   通   过   将   动   态IP   地   址   保   存   在   远   程、   本   地   计   算   机   都   可   访   问   到   的FTP   服   务   器   文   件   中,   供   本   地   计   算   机   读   取。   本   文   采   用   后   一   种   方   法,   若   没   有   合   适   的FTP   服   务   器,   可   到   提   供   免   费   主   页   空   间   允   许   以FTP   方   式   维   护   的Web   站   点   上   申   请   一   块   空   间,   供   远   程、   本   地   计   算   机   共   同   访   问。    
   
  ----本   地   计   算   机   登   录Internet   后,   用FTP   协   议   读   取   指   定FTP   服   务   器   上   含   有   远   程   计   算   机   动   态IP   地   址   的   文   本   文   件,   从   而   取   得   远   程   计   算   机   的Internet   IP   地   址,   然   后,   本   地   计   算   机   便   可   通   过   Internet   和   远   程   计   算   机   启   动   任   何   基   于TCP/IP   连   接   的   通   信   应   用   程   序,   如   利   用Pcanywhere   控   制   远   程   计   算   机,   并   可   通   过   远   程   计   算   机   访   问   与   远   程   计   算   机   相   连   的   整   个   网   络   资   源,   达   到   对   远   程   计   算   机   系   统   进   行   维   护   的   目   的。    
   
  四、   通   知   远   程   计   算   机   断   开Internet   连   接    
  ----   当   本   地   计   算   机   和   远   程   计   算   机   通   信   结   束   后,   便   可   通   知   远   程   计   算   机   断   开Internet   连   接。   利   用   远   程   计   算   机   的   Internet   IP   地   址,   本   地   计   算   机   和   远   程   计   算   机   建   立TCP/IP   连   接,   送   出   断   开Internet   指   令,   远   程   计   算   机   收   到   指   令   后,   回   送   确   认   消   息,   断   开   Internet   连   接,   等   待   下   一   个   命   令   的   到   来。    
   
  五、   程   序   实   现    
  ----本   地、   远   程   计   算   机   运   行   的   是   同   一   道   程   序,   通   过   不   同   的   命   令   按   钮   来   激   活   本   地、   远   程   计   算   机   程   序   所   具   有   的   不   同   功   能。   程   序   采   用VC   +   +6.0   编   写,   在Win98   环   境   下   调   试   运   行   通   过。   限   于   篇   幅,   下   面   只   给   出   程   序   的   主   要   部   分   及   注   释   说   明,   并   省   去   了   一   些   出   错   处   理   环   节。    
   
  //   宏   定   义  
    #define   MAX_PENDING_CONNECTS   2  
    #define   NO_FLAGS_SET                   0      
    #define   MY_MSG_LENGTH               100    
  //   全   局   变   量  
  HRASCONN hCon; //RAS   连   接   句   柄  
  HWND hWin;  
  HINSTANCE hInst;  
  DWORD ThreadId=0;  
  HANDLE hThread=NULL;  
  char   cRemoteIP[50]; //remote   IP   地   址  
  SOCKET   serv_sock,rsock; //   服   务   端  
  //   建   立   拨   号   连   接   函   数,    
  成   功   返   回   TRUE   else   FALSE        
  //   szEntry   拨   号   连   接   名   szPhone    
  电   话   号   码   szUser   和szPassword    
  分   别   为internet   的   用   户   名   和   口   令  
  BOOL   StartCon(   HWND   hWnd,char   *   szEntry,char  
    *   szUser,char   *   szPassword,char   *   szPhone   )  
  {  
          RASDIALPARAMS   rdParams;  
          DWORD   dwRet;  
              char   szBuf[300];  
          rdParams.dwSize   =   sizeof(RASDIALPARAMS);    
          lstrcpy(rdParams.szEntryName,szEntry);  
          strcpy(rdParams.szPhoneNumber,szPhone);    
          rdParams.szCallbackNumber[0]   =   ‘\0';  
          strcpy(rdParams.szUserName,szUser);    
          strcpy(rdParams.szPassword,szPassword);  
            rdParams.szDomain[0]   =   ‘\0';  
          //   以   下   开   始   同   步   拨   叫   网   络  
  dwRet   =   RasDial(   NULL,   NULL,    
  &rdParams,   0L,   NULL,   &hCon   );  
          return   TRUE;  
  }  
  //   通   过FTP   协   议   读   写FTP   服   务   器   上  
    文   件   的   函   数   cFlag   =0   写   =1   读,   读   写    
  成   功   时   返   回   所   读   写   的   字   节   数  
  DWORD   FtpWriteRead(char   *   cFtpHost,char   *    
  cFile,DWORD   dwLen,char   *   buf,char   cFlag)  
    {  
          HINTERNET   hInternet,hHost,hFile;  
          DWORD dwLength,dwError;  
  hInternet=InternetOpen(“FZYXB",LOCAL  
  _INTERNET_ACCESS,  
  NULL,0,0);  
        hHost=InternetConnect(hInternet,cFtpHost,  
  INTERNET_INVALID_PORT_NUMBER,  
  “fzxucm",“abc505",  
  INTERNET_SERVICE_FTP,INTERNET  
  _FLAG_PASSIVE,0);  
        if   (   cFlag   ==   0   )   //   写  
        hFile=FtpOpenFile(hHost,cFile,GENERIC_WRITE,  
        FTP_TRANSFER_TYPE_BINARY,0);  
        else  
      hFile=FtpOpenFile(hHost,cFile,GENERIC_READ,  
      FTP_TRANSFER_TYPE_BINARY,0);  
        if(   !hFile)  
        {  
              if   (   (dwError=GetLastError())   ==   12003   )  
              ShowMsg(hWin,“   文   件   不   存   在");  
              InternetCloseHandle(hHost);  
              InternetCloseHandle(hInternet);return   0L;  
        }  
        if   (   cFlag   ==   0   )  
              InternetWriteFile(hFile,buf,dwLen,   &dwLength);  
      else  
              InternetReadFile(hFile,buf,dwLen,   &dwLength);  
              InternetCloseHandle(hFile);InternetCloseHandle(hHost);  
              InternetCloseHandle(hInternet);  
  return   dwLength;  
    }  
  //   通   过FTP   协   议   删   除FTP   服   务   器   上  
  文   件   的   函   数cFtpHost   主   机   地   址cFile   文   件   名  
  BOOL     MyFtpDeleteFile(char   *   cFtpHost,char   *   cFile)  
  {  
        HINTERNET   hInternet,hHost;  
        hInternet=InternetOpen(“FZYXB",  
          LOCAL_INTERNET_ACCESS,  
  NULL,0,0);  
    hHost=InternetConnect(hInternet,cFtpHost,  
  INTERNET_INVALID_PORT_NUMBER,“fzabc",  
  “b505",INTERNET_SERVICE_FTP,INTERNET  
  _FLAG_PASSIVE,0);  
        FtpDeleteFile(hHost,cFile);  
      InternetCloseHandle(hHost);InternetCloseHandle(hInternet);  
      return   TRUE;  
  }  
  //   本   地   计   算   机   运   行   线   程   函   数   pp   功   能   代   码  
  void   CallThread(void   *   pp   )  
  {  
        DWORD   *   dwId,dwLen,   ret,ll;  
        RASPPPIP   rip;  
        char   szIp[50],   *pdest;  
        dwId=(DWORD   *)pp;  
        switch(   *   dwId   )  
        {  
  case   IDC_CALL:  
   
   
  Top

2 楼yintongshun(左岸思雨)回复于 2003-12-02 12:01:12 得分 0

枚举本地-远程NT系统进程    
      Windows2000中有个工具taskmgr.exe就可以比较详细的查看当前系统进程信息,但是那是Windows   GUI程序,有时候是不是觉得命令行下的东西更方便呢?其实已经有不少命令行下的枚举系统进程的工具了,M$的Resource   Kit中好象也有,但去了解他们是怎么实现的,自己动手做出来,是不是更有意思呢:)  
   
      进程通常被定义为一个正在运行的程序的实例,它由两部分组成:  
   
      <1>操作系统用来管理进程的内核对象。内核对象也是系统用来存放关于进程的统计信息的地方。  
   
      <2>地址空间。它包含所有可执行模块或DLL模块的代码和数据。它还包含动态内存分配的空间,如线程的堆栈和堆分配空间。  
   
      枚举系统进程的实现方法大概有四种,其中有一种可以用来枚举远程NT系统的进程,前提是有远程系统的管理员权限。  
   
   
   
  <<第一部分:调用PSAPI函数枚举系统进程>>  
   
    M$的Windows   NT开发小组开发了自己Process   Status函数,包含在PSAPI.DLL文件中,这些函数只能在高于NT4.0以后的版本中使用。PSAPI一共有14个函数[实际PSAPI.DLL输出函数有19个,但其中有5个函数有两个版本,分别是ANSI和Unicode版本],通过调用这些函数,我们可以很方便的取得系统进程的所有信息,例如进程名、进程ID、父进程ID、进程优先级、映射到进程空间的模块列表等等。为了方便起见,以下的例子程序只获取进程的名字和ID。  
   
    简单的程序如下:  
   
  /*************************************************************************  
   
  Module:ps.c  
   
  说明:调用PSAPI函数枚举系统进程名和ID,Only   for   NT/2000  
   
  *************************************************************************/  
   
  #include    
   
  #include    
   
  #include   "psapi.h"  
   
   
   
  #pragma   comment(lib,"psapi.lib")  
   
   
   
  void   PrintProcessNameAndID(   DWORD   processID   )  
   
  {  
   
    char   szProcessName[MAX_PATH]   =   "unknown";  
   
    //取得进程的句柄  
   
    HANDLE   hProcess   =   OpenProcess(   PROCESS_QUERY_INFORMATION   |  
   
                      PROCESS_VM_READ,  
   
                        FALSE,   processID   );  
   
    //取得进程名称  
   
    if   (   hProcess   )  
   
    {  
   
      HMODULE   hMod;  
   
        DWORD   cbNeeded;  
   
      if   (   EnumProcessModules(   hProcess,   &hMod,   sizeof(hMod),   &cbNeeded)   )  
   
          GetModuleBaseName(   hProcess,   hMod,   szProcessName,  
   
  sizeof(szProcessName)   );  
   
    }  
   
    //回显进程名称和ID  
   
    printf(   "\n%-20s%-20d",   szProcessName,   processID   );  
   
    CloseHandle(   hProcess   );  
   
  }  
   
   
   
  void   main(   )  
   
  {  
   
    DWORD   aProcesses[1024],   cbNeeded,   cProcesses;  
   
    unsigned   int   i;  
   
    //枚举系统进程ID列表  
   
    if   (   !EnumProcesses(   aProcesses,   sizeof(aProcesses),   &cbNeeded   )   )  
   
      return;  
   
    //   Calculate   how   many   process   identifiers   were   returned.  
   
    //计算进程数量  
   
    cProcesses   =   cbNeeded   /   sizeof(DWORD);  
   
    //   输出每个进程的名称和ID  
   
    for   (   i   =   0;   i   <   cProcesses;   i++   )  
   
        PrintProcessNameAndID(   aProcesses[i]   );  
   
    return;  
   
  }  
   
   
   
  <<第二部分:调用ToolHelp   API枚举本地系统进程>>  
   
      在第一部分提到的PSAPI函数只能枚举NT系统的进程,在Windows9x环境下我们可以通过调用ToolHelp   API函数来达到枚举系统进程的目的。M$的Windows   NT开发小组因为不喜欢ToolHelp函数,所以没有将这些函数添加给Windows   NT,所以他们开发了自己的Process   Status函数,就是第一部分提到的PSAPI了。但是后来M$已经将ToolHelp函数添加给了Windows   2000。ToolHelp共有12个函数,通过调用这些函数可以方面的取得本地系统进程的详细信息,以下这个简单的例子只调用了三个函数,获取我们所需要系统进程名字和进程ID。程序如下:  
   
  /**********************************************************************  
   
  Module:ps.c  
   
  说明:调用ToolHelp函数枚举本地系统进程名和ID,Only   for   9x/2000  
   
  **********************************************************************/  
   
  #include    
   
  #include    
   
  #include    
   
   
   
   
   
  int   main()  
   
  {  
   
      HANDLE       hProcessSnap   =   NULL;  
   
    PROCESSENTRY32   pe32   =   {0};  
   
    hProcessSnap   =   CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,   0);  
   
    if   (hProcessSnap   ==   (HANDLE)-1)  
   
    {  
   
        printf("\nCreateToolhelp32Snapshot()   failed:%d",GetLastError());  
   
    return   1;  
   
  }  
   
    pe32.dwSize   =   sizeof(PROCESSENTRY32);  
   
      printf("\nProcessName       ProcessID");  
   
    if   (Process32First(hProcessSnap,   &pe32))  
   
    {  
   
      do  
   
      {  
   
  printf("\n%-20s%d",pe32.szExeFile,pe32.th32ProcessID);  
   
         }while   (Process32Next(hProcessSnap,   &pe32));  
   
    }  
   
    else  
   
    {  
   
        printf("\nProcess32Firstt()   failed:%d",GetLastError());  
   
    }  
   
    CloseHandle   (hProcessSnap);  
   
  return   0;  
   
  }  
   
   
   
  <<第三部分:调用NTDLL.DLL中未公开API枚举本地系统进程>>  
   
   
   
   
   
     第一部分和第二部分说的是调用MS公开的API来枚举系统进程,在NTDLL.DLL中其实有一个未公开API,也可以用来枚举系统进程。此方法是从别处看来的,我可没这本事自己发现哦,出处记不清楚了,好像是pwdump2   中的源代码中的一部分吧。  
   
        OK!那个未公开API就是NtQuerySystemInformation,使用方法如下:  
   
  ////////////////////////////////////////////////////////////////////////////////  
   
  #include    
   
  #include    
   
  #include    
   
   
   
  typedef   unsigned   long   NTSTATUS;  
   
  typedef   unsigned   short   USHORT;  
   
  typedef   unsigned   long ULONG;  
   
  typedef   unsigned   long DWORD;  
   
  typedef   long   LONG;  
   
  typedef   __int64   LONGLONG;  
   
  typedef   struct   {  
   
    USHORT   Length;  
   
    USHORT   MaxLen;  
   
    USHORT   *Buffer;  
   
  }   UNICODE_STRING;  
   
   
   
  struct   process_info   {  
   
    ULONG   NextEntryDelta;  
   
    ULONG   ThreadCount;  
   
    ULONG   Reserved1[6];  
   
    LARGE_INTEGER   CreateTime;  
   
    LARGE_INTEGER   UserTime;  
   
    LARGE_INTEGER   KernelTime;  
   
    UNICODE_STRING   ProcessName;  
   
    ULONG   BasePriority;  
   
    ULONG   ProcessId;  
   
  };  
   
   
   
  typedef   NTSTATUS   (__stdcall   *NtQuerySystemInformation1)(  
   
      IN   ULONG   SysInfoClass,  
   
  IN   OUT   PVOID   SystemInformation,  
   
      IN   ULONG   SystemInformationLength,  
   
        OUT   PULONG   RetLen  
   
            );  
   
   
   
  int   main()  
   
  {  
   
    HINSTANCE   hNtDll;  
   
    NtQuerySystemInformation1   NtQuerySystemInformation;  
   
    NTSTATUS   rc;  
   
    ULONG   ulNeed   =   0;  
   
    void   *buf   =   NULL;  
   
    size_t   len   =   0;  
   
    struct   process_info   *p   ;  
   
    int   done;  
   
   
   
    hNtDll   =   LoadLibrary   ("NTDLL");  
   
    if   (!hNtDll)  
   
      return   0;  
   
    NtQuerySystemInformation   =   (NtQuerySystemInformation1)GetProcAddress   (hNtDll,  
   
  "NtQuerySystemInformation");  
   
      if   (!NtQuerySystemInformation)  
   
          return   0;  
   
   
   
    do   {  
   
      len   +=   0x1000;  
   
      buf   =   realloc   (buf,   len);  
   
      if   (!buf)  
   
          return   0;  
   
      rc   =   NtQuerySystemInformation   (5,   buf,   len,   &ulNeed);  
   
    }   while   (rc   ==   0xc0000004); //   STATUS_INFO_LEN_MISMATCH  
   
   
   
    if   (rc   <0)   {  
   
      free   (buf);  
   
      return   0;  
   
    }  
   
   
   
      printf("\nProcessName       ProcessID");  
   
    p   =   (struct   process_info   *)buf;  
   
    done   =   0;  
   
   
   
    while   (!done)   {  
   
      if   ((p->ProcessName.Buffer   !=   0))  
   
      {  
   
          printf("\n%-20S%d",p->ProcessName.Buffer,p->ProcessId);  
   
   
   
      }  
   
      done   =   p->NextEntryDelta   ==   0;  
   
      p   =   (struct   process_info   *)(((char   *)p)   +   p->NextEntryDelta);  
   
    }  
   
    free   (buf);  
   
    FreeLibrary   (hNtDll);  
   
    return   0;  
   
  }  
   
  Top

3 楼yintongshun(左岸思雨)回复于 2003-12-02 12:01:26 得分 0

 
   
   
   
   
  <<第四部分:从PDH中取得本地/远程系统进程信息>>  
   
   
   
      前面说的三种方法都只能枚举本地的系统进程,如何枚举远程系统的进程呢?目前我只知道从PDH中取得进程信息。  
   
      OK!我先简单的说说PDH是什么东西,hoho~难的偶也不会。PDH是英文Performance   Data   Helper的缩写,Windows   NT一直在更新这个称为Performance   Data的数据库,这个数据库包含了大量的信息,例如CPU使用率,内存使用率,系统进程信息等等一大堆有用的信息,可以通过注册表函数来访问。注意哦,Windows   9x中并没有配置这个数据库。但是,这个数据库中的信息布局很复杂,很多人并不愿意使用它,包括我。而且刚开始的时候,它也没有自己特定的函数,只能通过现有的注册表函数来操作。后来,为了使该数据库的使用变得容易,MS开发了一组Performance   Data   Helper函数,包含在PDH.DLL文件中。  
   
  Windows   2000默认是允许远程注册表操作的,所以我们就可以通过连接远程系统的注册表,从它的PDH中取得我们所需要的系统进程信息了,当然这需要远程系统的Admin权限。  
   
  OK!我们下面所举的例子是直接利用注册表函数来从本地/远程系统的PDH数据库中取得我们所需要的数据的,我们并没有利用PDH   API。  
   
   
   
        程序代码如下:  
   
  /**************************************************************************  
   
  Module:ps.c  
   
  Author:mikeblas@nwlink.com  
   
  Modify:ey4s  
   
  Http://www.ey4s.org  
   
  Date:2001/6/23  
   
  **************************************************************************/  
   
  #include    
   
  #include    
   
  #include    
   
   
   
  #define   INITIAL_SIZE      51200  
   
  #define   EXTEND_SIZE         12800  
   
  #define   REGKEY_PERF       "software\\microsoft\\windows   nt\\currentversion\\perflib"  
   
  #define   REGSUBKEY_COUNTERS "Counters"  
   
  #define   PROCESS_COUNTER     "process"  
   
  #define   PROCESSID_COUNTER    "id   process"  
   
  #define   UNKNOWN_TASK      "unknown"    
   
  #define   MaxProcessNum        52//最大进程数量  
   
   
   
  #pragma   comment(lib,"mpr.lib")  
   
   
   
  typedef   struct   ProcessInfo  
   
  {  
   
  char   ProcessName[128];  
   
  DWORD   dwProcessID;  
   
  }pi;  
   
   
   
  void   banner();  
   
  int   ConnIPC(char   *,char   *,char   *);  
   
  DWORD   GetProcessInfo(pi   *,char   *,char   *,char   *);  
   
   
   
  int   main(int   argc,char   **argv)  
   
  {  
   
  int   i,iRet;  
   
  pi   TaskList[MaxProcessNum];  
   
  banner();  
   
  if(argc==1)  
   
  {  
   
  iRet=GetProcessInfo(TaskList,NULL,NULL,NULL);  
   
    printf("\nProcess   Info   for   [LOCAL]:");  
   
  }  
   
  else   if(argc==4)  
   
  {  
   
  iRet=GetProcessInfo(TaskList,argv[1],argv[2],argv[3]);  
   
  printf("\nProcess   Info   for   [%s]:",argv[1]);  
   
  }  
   
  else  
   
  {  
   
  printf("\nUsage:%s   ",argv[0]);  
   
  return   1;  
   
  }  
   
  if(iRet>0)     
   
  for(i=0,printf("\nProcessName       ProcessID");  
   
  i    
  printf("\n%-20s   %d",TaskList[i].ProcessName,TaskList[i].dwProcessID),i++);     
   
    return   0;  
   
  }  
   
   
   
  DWORD   GetProcessInfo(pi   *ProList,char   *ip,char   *user,char   *pass)  
   
  {  
   
    DWORD   rc,dwType,dwSize,i,dwProcessIdTitle,dwProcessIdCounter,dwRet=-1;  
   
      HKEY               hKeyNames;  
   
      LPSTR              buf   =   NULL,p,p2;  
   
      CHAR               szSubKey[1024],szProcessName[MAX_PATH];  
   
      PPERF_DATA_BLOCK         pPerf;  
   
      PPERF_OBJECT_TYPE        pObj;  
   
    PPERF_INSTANCE_DEFINITION    pInst;  
   
      PPERF_COUNTER_BLOCK       pCounter;  
   
      PPERF_COUNTER_DEFINITION     pCounterDef;  
   
    HKEY        ghPerfKey   =NULL, //   get   perf   data   from   this   key  
   
  ghMachineKey   =   NULL; //   get   title   index   from   this   key  
   
    BOOL   bRemote=FALSE;  
   
   
   
    //   Look   for   the   list   of   counters.   Always   use   the   neutral  
   
    //   English   version,   regardless   of   the   local   language. We  
   
    //   are   looking   for   some   particular   keys,   and   we   are   always  
   
    //   going   to   do   our   looking   in   English.   We   are   not   going  
   
    //   to   show   the   user   the   counter   names,   so   there   is   no   need  
   
    //   to   go   find   the   corresponding   name   in   the   local   language.  
   
   
   
        __try  
   
        {  
   
           if((ip)&&(user)&&(pass))  
   
           {  
   
                 if(ConnIPC(ip,user,pass)!=0)  
   
               {  
   
                  printf("\nConnect   to   %s   failed.",ip);  
   
                  __leave;  
   
               }  
   
                 else  
   
                  bRemote=TRUE;  
   
          }  
   
           //连接本地or远程注册表  
   
           if(RegConnectRegistry(ip,HKEY_PERFORMANCE_DATA,  
   
                 &ghPerfKey)!=ERROR_SUCCESS)  
   
           {  
   
                 printf("\nRegConnectRegistry()   1   failed:%d",GetLastError());  
   
                 __leave;  
   
           }  
   
  `     if(RegConnectRegistry(ip,HKEY_LOCAL_MACHINE,&ghMachineKey)!=ERROR_SUCCESS)  
   
           {  
   
                 printf("\nRegConnectRegistry()   2   failed:%d",GetLastError());  
   
                 __leave;  
   
           }  
   
   
   
  sprintf(   szSubKey,   "%s\\%03x",   REGKEY_PERF,MAKELANGID(   LANG_ENGLISH,   SUBLANG_NEUTRAL));  
   
   
   
  if(RegOpenKeyEx(ghMachineKey,szSubKey,0,KEY_READ,&hKeyNames)!=ERROR_SUCCESS)  
   
                 __leave;  
   
   
   
           //   从counter   names取得需要的缓冲区大小  
   
   
  Top

4 楼yintongshun(左岸思雨)回复于 2003-12-02 12:01:43 得分 0

问题:   我在试图做一个dll时,不用def文件不行吗?再一个EXPORTS下面的输出函数还用给它们分别给一个数吗?  
  回答:    
   
          在VC++中,如果生成DLL可以不使用.def文件。你只需要在VC++的函数定义前要加__declspec(dllexport)修饰就可以了。但是使用__declspec(dllexport)和使用.def文件是有区别的。如果你的DLL是提供给VC++用户使用的,你只需要把编译DLL时产生的.lib提供给用户,它可以很轻松地调用你的DLL。但是如果你的DLL是供VB、PB、Delphi用户使用的,那么会产生一个小麻烦。因为VC++对于__declspec(dllexport)声明的函数会进行名称转换,如下面的函数:    
          __declspec(dllexport)   int   __stdcall   IsWinNT()    
          会转换为IsWinNT@0,这样你在VB中必须这样声明:    
          Declare   Function   IsWinNT   Lib   "my.dll"   Alias   "IsWinNT@0"   ()   As   Long    
          @的后面的数由于参数类型不同而可能不同。这显然不太方便。所以如果要想避免这种转换,就要使用.def文件方式。   这样在VB中用的时候就可以如下:  
        Declare   Function   IsWinNT   Lib   "my.dll"   Alias   "IsWinNT0"   ()   As   Long  
          EXPORTS后面的数可以不给,系统会自动分配一个数。对于VB、PB、Delphi用户,通常使用按名称进行调用的方式,这个数关系不大,但是对于使用.lib链接的VC程序来说,不是按名称进行调用,而是按照这个数进行调用的,所以最好给出。    
   
   
  Top

5 楼yintongshun(左岸思雨)回复于 2003-12-02 12:02:10 得分 0

const传奇    
  译者声明:有些地方按原文解释不通,译者根据自己的理解作了适当修改。如有不妥之处,请告知coolgrass@sina.com或参考原文。    
   
  当我自己写程序需要用到const的时候,或者是读别人的代码碰到const的时候,我常常会停下来想一会儿。许多程序员从来不用const,理由是即使没用const他们也这么过来了。本文仅对const的用法稍作探讨,希望能够对提高软件的源代码质量有所帮助。    
     
  变量用const修饰,其值不得被改变。任何改变此变量的代码都会产生编译错误。Const加在数据类型前后均可。    
   
  例如:    
   
  [code:1:288c1261d6]void   main(void)    
   
  {    
   
          const   int   i   =   10;         //i,j都用作常变量    
   
          int   const   j   =   20;    
   
          i   =   15;                         //错误,常变量不能改变    
   
          j   =   25;                         //错误,常变量不能改变    
   
  }[/code:1:288c1261d6]    
       
   
  [b:288c1261d6]常指针[/b:288c1261d6]    
   
  Const跟指针一起使用的时候有两种方法。    
   
       
   
  const可用来限制指针不可变。也就是说指针指向的内存地址不可变,但可以随意改变该地址指向的内存的内容。    
   
  [code:1:288c1261d6]void   main(void)    
   
  {    
   
          char*   const   str   =   "Hello,   World";         //常指针,指向字符串    
   
          *str   =   'M';                         //可以改变字符串内容    
   
          str   =   "Bye,   World";                 //错误,如能改变常指针指向的内存地址    
   
  }[/code:1:288c1261d6]    
   
       
   
  const也可用来限制指针指向的内存不可变,但指针指向的内存地址可变。    
   
  [code:1:288c1261d6]void   main(void)    
   
  {    
   
          const   char*   str   =   "Hello,   World";         //指针,指向字符串常量    
   
          *str   =   'M';                 //错误,不能改变字符串内容    
   
          str   =   "Bye,   World";         //修改指针使其指向另一个字符串    
   
          *str   =   'M';                 //错误,仍不能改变字符串内容    
   
  }[/code:1:288c1261d6]    
   
  看完上面的两个例子,是不是糊涂了?告诉你一个诀窍,在第一个例子中,const用来修饰指针str,str不可变(也就是指向字符的常指针);第二个例子中,const用来修饰char*,字符串char*不可变(也就是指向字符串常量的指针)。    
   
       
   
  这两种方式可以组合起来使用,使指针和内存内容都不可变。    
   
  [code:1:288c1261d6]void   main(void)    
   
  {    
   
          const   char*   const   str   =   "Hello,   World";                 //指向字符串常量的常指针    
   
          *str   =   'M';                         //错误,不能改变字符串内容    
   
          str   =   "Bye,   World";                 //错误,不能改变指针指向的地址    
   
  }[/code:1:288c1261d6]    
   
       
   
  [b:288c1261d6]Const和引用[/b:288c1261d6]    
   
  引用实际上就是变量的别名,这里有几条规则:    
   
  声明变量时必须初始化    
   
  一经初始化,引用不能在指向其它变量。    
   
  任何对引用的改变都将改变原变量。      
   
  引用和变量本身指向同一内存地址。    
   
       
   
  下面的例子演示了以上的规则:    
   
  [code:1:288c1261d6]void   main(void)    
   
  {    
   
          int   i   =   10;                                         //i和j是int型变量    
   
          int   j   =   20;                    
   
          int   &r   =   i;                                         //r   是变量i的引用    
   
          int   &                                                 //错误,声明引用时必须初始化    
   
          i   =   15;                                                 //i   和   r   都等于15    
   
          i++;                                                 //i   和   r都等于16    
   
          r   =   18;                                                 //i   和r   都等于18    
   
          printf("Address   of   i=%u,   Address   of   r=%u",&i,&r);         //内存地址相同    
   
          r   =   j;                                                 //i   和   r都等于20,但r不是j的引用    
   
          r++;                                                 //i   和   r   都等于21,   j   仍等于20    
   
  }[/code:1:288c1261d6]    
       
   
  用const修饰引用,使应用不可修改,但这并不耽误引用反映任何对变量的修改。Const加在数据类型前后均可。    
   
  例如:    
   
  [code:1:288c1261d6]void   main(void)    
   
  {    
   
          int   i   =   10;    
   
          int   j   =   100;    
   
          const   int   &r   =   i;    
   
          int   const   &s   =   j;    
   
          r   =   20;                     //错,不能改变内容    
   
          s   =   50;                     //错,不能改变内容    
   
          i   =   15;                     //   i和r   都等于15    
   
          j   =   25;                     //   j和s   都等于25    
   
  }[/code:1:288c1261d6]    
   
       
   
  [b:288c1261d6]Const和成员函数[/b:288c1261d6]    
   
  声明成员函数时,末尾加const修饰,表示在成员函数内不得改变该对象的任何数据。这种模式常被用来表示对象数据只读的访问模式。例如:    
   
  [code:1:288c1261d6]class   MyClass    
   
  {    
   
          char   *str   ="Hello,   World";    
   
          MyClass()    
   
          {    
   
                  //void   constructor    
   
          }    
   
             
   
          ~MyClass()    
   
          {    
   
                  //destructor    
   
          }    
   
       
   
          char   ValueAt(int   pos)   const         //const   method   is   an   accessor   method    
   
          {    
   
                  if(pos   >=   12)    
   
                                return   0;    
   
                              *str   =   'M';                     //错误,不得修改该对象    
   
                  return   str[pos];                     //return   the   value   at   position   pos    
   
          }    
   
  }[/code:1:288c1261d6]    
   
       
   
  [b:288c1261d6]Const和重载[/b:288c1261d6]    
   
  重载函数的时候也可以使用const,考虑下面的代码:    
   
  [code:1:288c1261d6]class   MyClass    
   
  {    
   
          char   *str   ="Hello,   World";    
   
          MyClass()    
   
          {    
   
                  //void   constructor    
   
          }    
   
             
   
          ~MyClass()    
   
          {    
   
                  //destructor    
   
          }    
   
       
   
          char   ValueAt(int   pos)   const         //const   method   is   an   accessor   method    
   
          {    
   
                  if(pos   >=   12)    
   
                                return   0;    
   
                  return   str[pos];         //return   the   value   at   position   pos    
   
          }    
   
             
   
          char&   ValueAt(int   pos)                 //通过返回引用设置内存内容    
   
          {    
   
                  if(pos   >=   12)    
   
                                return   NULL;    
   
                  return   str[pos];    
   
          }    
   
  }[/code:1:288c1261d6]    
   
       
   
  在上面的例子中,ValueAt是被重载的。Const实际上是函数参数的一部分,在第一个成员函数中它限制这个函数不能改变对象的数据,而第二个则没有。这个例子只是用来说明const可以用来重载函数,没有什么实用意义。    
   
  Top

6 楼yintongshun(左岸思雨)回复于 2003-12-02 12:02:24 得分 0

 
       
   
  实际上我们需要一个新版本的GetValue。如果GetValue被用在operator=的右边,它就会充当一个变量;如果GetValue被用作一元操作符,那么返回的引用可以被修改。这种用法常用来重载操作符。String类的operator[]是个很好的例子。(这一段译得很烂,原文如下:In   reality   due   to   the   beauty   of   references   just   the   second   definition   of   GetValue   is   actually   required.   If   the   GetValue   method   is   used   on   the   the   right   side   of   an   =   operator   then   it   will   act   as   an   accessor,   while   if   it   is   used   as   an   l-value   (left   hand   side   value)   then   the   returned   reference   will   be   modified   and   the   method   will   be   used   as   setter.   This   is   frequently   done   when   overloading   operators.   The   []   operator   in   String   classes   is   a   good   example.)    
   
       
   
  [code:1:288c1261d6]class   MyClass    
   
  {    
   
          char   *str   ="Hello,   World";    
   
          MyClass()    
   
          {    
   
                  //void   constructor    
   
          }    
   
             
   
          ~MyClass()    
   
          {    
   
                  //destructor    
   
          }    
   
       
   
          char&   operator[](int   pos)                 //通过返回引用可用来更改内存内容    
   
          {    
   
                  if(pos   >=   12)    
   
                                return   NULL;    
   
                  return   str[pos];    
   
          }    
   
  }    
   
       
   
  void   main(void)    
   
  {    
   
          MyClass   m;    
   
          char   ch   =   m[0];                 //ch   等于   'H'    
   
          m[0]   =   'M';                 //m的成员str变成:Mello,   World    
   
  }[/code:1:288c1261d6]    
   
       
   
  [b:288c1261d6]Const的担心[/b:288c1261d6]    
   
  C/C++中,数据传递给函数的方式默认的是值传递,也就是说当参数传递给函数时会产生一个该参数的拷贝,这样该函数内任何对该参数的改变都不会扩展到此函数以外。每次调用该函数都会产生一个拷贝,效率不高,尤其是函数调用的次数很高的时候。    
   
  例如:    
   
       
   
  [code:1:288c1261d6]class   MyClass    
   
  {    
   
  public:    
   
          int   x;            
   
          char   ValueAt(int   pos)   const         //const   method   is   an   accessor   method    
   
          {    
   
                  if(pos   >=   12)    
   
                                return   0;    
   
                  return   str[pos];         //return   the   value   at   position   pos    
   
          }    
   
          MyClass()    
   
          {    
   
                  //void   constructor    
   
          }    
   
          ~MyClass()    
   
          {    
   
                  //destructor    
   
          }    
   
          MyFunc(int   y)         //值传递    
   
          {    
   
                  y   =   20;    
   
                  x   =   y;         //x   和   y   都等于   20.    
   
          }    
   
  }    
   
       
   
  void   main(void)    
   
  {    
   
          MyClass   m;    
   
          int   z   =   10;    
   
          m.MyFunc(z);    
   
          printf("z=%d,   MyClass.x=%d",z,m.x);         //z   不变,   x   等于20.    
   
  }[/code:1:288c1261d6]    
   
       
   
  通过上面的例子可以看出,z没有发生变化,因为MyFunc()操作的是z的拷贝。为了提高效率,我们可以在传递参数的时候,不采用值传递的方式,而采用引用传递。这样传递给函数的是该参数的引用,而不再是该参数的拷贝。然而问题是如果在函数内部改变了参数,这种改变会扩展到函数的外部,有可能会导致错误。在参数前加const修饰保证该参数在函数内部不会被改变。    
   
  [code:1:288c1261d6]class   MyClass    
   
  {    
   
  public:    
   
          int   x;    
   
          MyClass()    
   
          {    
   
                  //void   constructor    
   
          }    
   
          ~MyClass()    
   
          {    
   
                  //destructor    
   
          }    
   
          int   MyFunc(const   int&   y)         //引用传递,   没有任何拷贝    
   
          {    
   
                  y   =20;         //错误,不能修改常变量    
   
                  x   =   y            
   
          }    
   
  }    
   
       
   
  void   main(void)    
   
  {    
   
          MyClass   m;    
   
          int   z   =   10;    
   
          m.MyFunc(z);    
   
          printf("z=%d,   MyClass.x=%d",z,m.x);         //z不变,   x等于10.    
   
  }[/code:1:288c1261d6]    
   
       
   
  如此,const通过这种简单安全机制使你写不出那种说不定是什么时候就会掉过头来咬你一口的代码。你应该尽可能的使用const引用,通过声明你的函数参数为常变量(任何可能的地方)或者定义那种const   method,你就可以非常有效确立这样一种概念:本成员函数不会改变任何函数参数,或者不会改变任何该对象的数据。别的程序员在使用你提供的成员函数的时候,不会担心他们的数据被改得一塌糊涂。    
     
   
  Top

7 楼yintongshun(左岸思雨)回复于 2003-12-02 12:03:01 得分 0

unicode   如何转换为ASCII?      
   
  程序在FressBSD/Linux上调试通过,Windows   用户只要修改几个变量类型和头文件即可      
   
  说明:函数名称      
  int   strUnicode2GB(char   *strSourcer,   char   *strDest,int   n)      
   
  转换Unicde字符串到GB码,返回汉字数      
  输入(   Unicode   源串,GB2312/ASCII混合编码串,Unicode字节数   必须是偶数!!!)      
   
  [code:1:0a0b72ed53]    
   
  #include   <sys/types.h>      
   
  const   unsigned   short   int   Unicode_GB2312[][2]   =      
  {      
   
  /*Unicode   ,GB||ASCII   ,MEMO*/      
  0x0000,0x0040,/*   0   COMMERCIAL   AT   @   */      
  0x0001,0x00A3,/*   1   POUND   SIGN   £   */      
  0x0002,0x0024,/*   2   DOLLAR   SIGN   $   */      
  0x0003,0x00A5,/*   3   YEN   SIGN   ¥   */      
  0x0004,0x00E8,/*   4   LATIN   SMALL   LETTER   E   WITH   GRAVE   è   */      
   
  .............      
  .............      
  码表太长了   请尽量通过   Google   查询,多的是      
  或者通过   stevenkoh@sohu.com   向我索取      
   
  0xFF5C,0xA3FC,/*   '|'   ->   65372   */      
  0xFF5D,0xA3FD,/*   '}'   ->   65373   */      
  0xFF5E,0xA1AB,/*   '~'   ->   65374   */      
  0xFFE0,0xA1E9,/*   '¢'   ->   65504   */      
  0xFFE1,0xA1EA,/*   '£'   ->   65505   */      
  0xFFE3,0xA3FE,/*   ' ̄'   ->   65507   */      
  0xFFE5,0xA3A4,/*   '¥'   ->   65509   */      
   
   
  };      
   
  u_int16_t   Unicode2GBcode(u_int16_t   iUnicode)      
  {      
  int   i,j,n;      
   
  switch   (iUnicode){      
  case   0x0002:      
  return   0x24;      
  break;      
  case   0x000a:      
  return   0xa;      
  break;      
  case   0x000d:      
  return   0xd;      
  break;      
  case   0x0040:      
  return   0xA1;      
  break;      
  }      
   
  if   ((iUnicode>=0x20&&iUnicode<=0x5a)||(iUnicode>=0x61&&iUnicode<=0x7a))   return   iUnicode;      
   
   
  for   (i=0,j=0,n=sizeof(Unicode_GB2312)/sizeof(Unicode_GB2312[0])-1;n>0;n>>=1,++j){      
   
  if(Unicode_GB2312[i][0]==iUnicode)   return   Unicode_GB2312[i][1];      
   
  if   (j>1){      
  if(Unicode_GB2312[i-1][0]==iUnicode)   return   Unicode_GB2312[i-1][1];      
  if(Unicode_GB2312[i+1][0]==iUnicode)   return   Unicode_GB2312[i+1][1];      
  }      
   
  if   (Unicode_GB2312[i][0]<iUnicode)   i=i+n;      
  else   i=i-n;      
  }      
   
  if(Unicode_GB2312[i][0]==iUnicode)   return   Unicode_GB2312[i][1];      
  if(Unicode_GB2312[i-1][0]==iUnicode)   return   Unicode_GB2312[i-1][1];      
  if(Unicode_GB2312[i+1][0]==iUnicode)   return   Unicode_GB2312[i+1][1];      
   
  return   0;   //转换不成功      
  }      
   
   
  /*转换Unicde字符串到GB码,返回汉字数*/      
  int   strUnicode2GB(const   char   *strSourcer,const   char   *strDest,int   n)      
  {      
  char   cTmp;      
  u_int16_t   hz,tmphz;      
   
  char   *pSrc;      
  char   *pDest;      
   
  int   i;      
   
   
  for   (i=0,pSrc=strSourcer,pDest=strDest;n>0;n-=2,pSrc+=2,++i,++pDest){      
   
  hz=0;      
  hz=*pSrc<<8|(*(pSrc+1)&0x00FF);      
  tmphz=Unicode2GBcode(hz);      
   
  if   (!tmphz||tmphz>0x7F&&tmphz<0xFF){      
  *pDest='.';      
  continue;      
  }      
  else   if   (tmphz>0x00&&tmphz<=0x7F){      
  cTmp=tmphz;      
  *pDest=cTmp;      
  }      
  else{      
  cTmp=tmphz;      
  *pDest=(tmphz>>=8);      
  ++pDest;      
  *pDest=cTmp;      
  }      
  }      
   
  *pDest='\0';      
  return   i;      
   
  }      
  [/code:1:0a0b72ed53]    
  希望您对大家有帮助!至于GB->Unicode,我想就算了,反正网关可以支持的。    
     
   
  Top

8 楼yintongshun(左岸思雨)回复于 2003-12-02 12:03:17 得分 0

VC++多串口DLL解决方案API版  作者   许士敏    
   
  调用Win32的串口通信API函数,以动态连接库(DLL)形式实现对多串口的操作。    
   
  正文  
  [refer]下载演示程序[/refer]  
  [refer]下载技术文档[/refer]  
   
  在现代的各种实时监控系统和通信系统中,在Windows   9X/NT下利用VC++对RS-232串口编程是常用的手段。  
  本程序以动态连接库(DLL)形式实现对多串口的操作,MSCOMM串口通讯控件在基于对话框的程序中很好使用,但在基于文档的程序不能直接使用该控件,且生成的可执行文件不能适用于不同的计算机。  
  Windows通信一般都以WOSA(Windows   Open   Services   Architecture,即Windows开放式服务体系)模型为基础,在此模型中位于上层的应用程序通过调用各种通信API(Application   Programming   Interfaces,即应用程序接口)与位于下层的设备驱动程序进行数据交换。Windows对串行口的读写也不例外,通过调用Win32的串口通信API函数,即可编写串行通信程序。本DLL是基于API函数的。  
   
  一、   多串口DLL功能  
  (一)   串口参数设置  
    对RS-232-C串行端口进行参数配置是使用串口进行通讯的必要条件。而且由于场合不同、用途、功能的不同对串口也采取不同的配置方式,为了使本程序更灵活,适应面更广,采取将所有的可能参数都预先设置在几个组合框中,可以在程序运行后随时更改设置。  
  图1   串口参数设置界面  
   
  串口号:   自动检测计算机存在的串口,但只能控制从COM1到COM5,能满足绝大多数人的需求。  
  是否使用:如果选中,则表示要对该串口进行初始化,以及收发控制。否则表示对计算机存在的串口不操作。  
  波特率:从1200bps到57600bps。  
  校验:有无校验(NOPARITY)。  
  数据位:7位或是8位。  
  停止位:1位、1.5位或是2位。  
  流控制:有无流控制(flow)。  
   
  (二)   串口收发数据调试显示  
  在进行串口操作时,尤其是在调试期间,需要知道串口的收发数据。本DLL提供串口收发数据的显示界面。  
   
   
  图2   串口收发数据ASCII显示界面  
   
   
   
  图3   串口收发数据16进制显示界面  
  (三)   串口参数设置文件  
  串口参数设置文件名为MscommSet.ini,该文件保存在工程工作目录下,如果没有该文件,串口按缺省值设置,在设置串口参数后,自动生成该设置文件。其内容如下:  
  [0串口信息]  
  use=1  
  baud=2400  
  port=1  
  databits=8  
  stopbits=1  
  flow=0  
  parity=N  
  [1串口信息]  
  use=1  
  baud=2400  
  port=2  
  databits=8  
  stopbits=1  
  flow=0  
  parity=N  
  [2串口信息]  
  use=0  
  baud=2400  
  port=1  
  databits=8  
  stopbits=1  
  flow=0  
  parity=N  
  [3串口信息]  
  use=0  
  baud=2400  
  port=4  
  databits=8  
  stopbits=1  
  flow=0  
  parity=N  
  [4串口信息]  
  use=0  
  baud=2400  
  port=1  
  databits=8  
  stopbits=1  
  flow=0  
  parity=N  
   
  二、   多串口DLL的作用  
  本DLL   提供三个文件SerialAPIDLL.dll,SerialAPIDLL.lib和MultiSerialAPI.h。在应用时,要把这三个文件拷贝到相应的目录下,并且要作如图4的设置。详见示例工程。  
   
   
  图4   应用工程连接设置界面    
   
  三、   多串口DLL主要函数使用说明  
  1.BOOL   OnCommsCreate(CWnd*   pParentWnd);  
  创建串口,本DLL同时创建32个串口。pParentWnd为窗口指针,一般为this。  
  2.   BOOL   OnComportOpen();  
  根据串口设置文件中的设置参数,打开多串口。  
  3.void   OnCommSend(unsigned   int   uiCom,   CString   sSendData,   unsigned   int   uiKind);    
  往指定串口发送数据。其中uiCom为指定的串口,注意串口1的uiCom为0;sSendData为要发送的字符串;   uiKind:   发送数据类型:0:原始数据 1:加回车符 2:加校验和。  
  4.void   OnMscommsSet();  
  打开串口设置界面,改变串口设置,并保存到设置文件中。  
  5.BOOL   OnComportTimerProcess(CString   strProcess[MaxPort]);  
  判断串口接收的一组数据是否已经接收完。  
  6.         BOOL   OnPopupDialog();            
  打开串口收发数据显示调试界面。  
   
  Top

9 楼yintongshun(左岸思雨)回复于 2003-12-02 12:03:49 得分 0

COM中的可连接对象与连接点机制及其MFC程序实现    
    本文首先论述可连接对象和连接点机制的原理,然后通过一个示例说明怎样用MFC编程实现可连接对象和内嵌于客户的事件接收器.    
    1、可连接对象和连接点机制的基本原理    
    为了在组件对象和客户之间提供更大的交互能力,组件对象也需要主动与客户进行通信。组件对象通过出接口(Outgoing   Interface)与客户进行通信。如果一个组件对象定义了一个或者多个出接口则此组件对象叫做可连接点对象。    
    所谓出接口也是COM接口。每个出接口包含一组成员函数,每个成员函数代表了一个事件、一个通知或者一个请求。但是这些接口是在客户的事件接收器(sink)中实现的,所以叫出接口。事件接收器也是COM对象。    
    可连接对象必须实现一个IConnectionPointContainer接口用于管理所有的出接口。每个出接口对应一个连接点对象,连接点对象实现了IConnectionPoint接口。客户正是通过IConnectionPoint接口与可连接对象建立连接。每一个连接用CONNECTDATA结构描述。    
    CONNECTDATA包含两个成员:IUnknown*   pUnk和DWORD   dwCookie。pUnk对应于客户中事件接收器的IUnknown接口指针;dwCookie是由连接点对象生成的用于唯一标识此连接的32位整数。    
    通过一个由可连接对象实现的枚举器接口IEnumConnectionPoints,客户可以访问可连接对象的所有连接点。但是要获得IEnumConnectionPoints接口指针,要通过IConnectionPointContainer::EnumConnectionPoints(IEnumConnectionPoints**)函数,此函数返回枚举器接口指针。    
    通过另一个有可连接对象实现的枚举器接口IEnumConnections,无论客户还是可连接对象都可以访问一个连接点上的所有连接。通过IConnectionPoint::EnumConnections(IEnumConnections**)函数可以获得IEnumConnections接口指针。    
    综上所述,一个可连接对象必须实现四个接口:IConnectionPointContainer、IConnectionPoint、IEnumConnectionPoints、IEnumConnections。这四个接口的定义请阅读MSDN文档。    
    现在结合后面的示例简单描述一下可连接对象和客户通信的过程。在后面的示例中,可连接对象ConnObject定义了出接口IEventSink,对应此出接口,实现了一个连接点对象SampleConnPoint(此对象实现了对应于出接口的连接点接口IConnectionPoint,接口ID为IID_IEventSink)。  
    1.客户在获取了可连接对象的IUnknown接口指针m_pIUnknown后,调用m_pIUnknown->QueryInterface(IID_IConnectionPointContainer,(void**)&pConnPtCont);如果调用成功,pConnPtCont中将存放可连接对象的IConnectionPointContainer接口指针。如果调用不成功,则表明对象不是可连接对象。    
    2.调用pConnPtCont->FindConnectionPoint(IID_IEventSink,&pConnPt)。如果调用成功,pConnPt将存放对应于出接口IEventSink的连接点对象SampleConnPoint所实现的连接点接口IConnectionPoint指针;如果调用不成功,说明可连接对象不支持出接口IEventSink。    
    3.调用pConnPt->Advise(pIEventSink,&m_dwCookie)以建立事件接收器(EventSink)与连接点的连接。其中pIEventSink是客户事件接收器IUnknown接口的指针,此指针通过此函数传递给了可连接对象以便可连接对象发起对客户的通信;m_dwCookie是连接标识,此值由可连接对象设置由客户保存,客户还要使用此值以断开连接。    
    4.可连接对象可以通过连接点调用客户事件接收器中的方法。在客户与连接点成功建立连接后,连接点中已经保存了客户事件接收器接口的指针并可以调用pConnPt->GetConnections()来获取。    
    5.客户调用pConnPt->Unadvise(m_dwCookie)来取消连接,同时调用pConnPt->Release()释放连接点对象。    
   
  COM中的可连接对象与连接点机制及其MFC程序实现    
   
  2002-03-25·   ·杨宁··    
    2、编程实例    
    现在用MFC实现一个可连接对象,然后写一个极为简单的客户和时间接收器。    
    需要说明的是,MFC通过CCmdTarget类实现了IConnectionPointContainer和IEnumConnectionPoints接口,此外,通过CConnectionPoint类实现了IConnectionPoint接口    
    1.可连接对象ConnObject    
    在这个对象中,实现一个一般的COM接口IEventServer,客户可以使用此接口的方法DoSomething()作一些事情,但主要的是对象将在此处触发事件。SampleConnPoint实现连接点对象。    
      (1)在GUIDs.h中写入:  
  //   {EE888B01-EA9C-11d3-97B5-5254AB191930}    
  static   const   IID   CLSID_ConnObject   =   //组件ID    
  {   0xee888b01,   0xea9c,   0x11d3,   {   0x97,   0xb5,   0x52,   0x54,   0xab,   0x19,   0x19,   0x30   }   };    
  //   {EE888B02-EA9C-11d3-97B5-5254AB191930}    
  static   const   IID   IID_IEventServer   =   //一般的COM接口,客户使用此接口的方法    
  //DoSomething()    
  {   0xee888b02,   0xea9c,   0x11d3,   {   0x97,   0xb5,   0x52,   0x54,   0xab,   0x19,   0x19,   0x30   }   };    
   
  ////   {EE888B03-EA9C-11d3-97B5-5254AB191930}    
  static   const   IID   IID_IEventSink   =   //连接点对象所实现的连接点接口ID    
  {   0xee888b03,   0xea9c,   0x11d3,   {   0x97,   0xb5,   0x52,   0x54,   0xab,   0x19,   0x19,   0x30   }   };    
   
    2.   在IConnObject.h中写入    
  #include   "GUIDs.h"    
  //声明IEventServer接口    
  DECLARE_INTERFACE_(IEventServer,IUnknown)    
  {    
  STDMETHOD(DoSomething)()PURE;    
  };    
  //声明出接口,此出接口将由客户的事件接收器实现    
  DECLARE_INTERFACE_(IEventSink,IUnknown)    
  {    
  STDMETHOD(EventHandle)()PURE;    
  };    
    3.添加基类为CCmdTarget的类CConnObject.在类声明文件CConnObject1.h中加上#include   “IConnObject.h”,在类声明中写入:    
  protected:    
  ……    
  //声明实现IEventServer接口的嵌套类    
  BEGIN_INTERFACE_PART(EventServer,IEventServer)    
  STDMETHOD(DoSomething)();    
  END_INTERFACE_PART(EventServer)    
  DECLARE_INTERFACE_MAP()    
  //声明实现连接点的嵌套类    
  BEGIN_CONNECTION_PART(CConnObject,SampleConnPoint)    
  CONNECTION_IID(IID_IEventSink)    
  END_CONNECTION_PART(SampleConnPoint)    
  DECLARE_CONNECTION_MAP()    
  DECLARE_OLECREATE(CConnObject)    
    说明:BEGIN_CONNECTION_PART和END_CONNECTION_PART宏声明了实现连接点的嵌套类SampleConnPoint,并且是基于CConnectionPoint类的,如果需要重载CConnectionPoint类的成员函数或者添加自己的成员函数,可以在这两个宏中声明.这里,CONNECTION_IID宏重载了CConnectionPoint::GetIID()函数.使用DECLARE_CONNECTION-MAP()宏声明连接点映射表.    
    4.在类CConnObject的实现文件中写入    
  IMPLEMENT_OLECREATE(CConnObject,"ConnObject",    
  0xee888b01,   0xea9c,   0x11d3,   0x97,   0xb5,   0x52,   0x54,   0xab,   0x19,   0x19,   0x30);    
  BEGIN_INTERFACE_MAP(CConnObject,CCmdTarget)    
  INTERFACE_PART(CConnObject,IID_IEventServer,EventServer)    
  INTERFACE_PART(CConnObject,IID_IConnectionPointContainer,ConnPtContainer)    
  END_INTERFACE_MAP()    
  BEGIN_CONNECTION_MAP(CConnObject,CCmdTarget)    
  CONNECTION_PART(CConnObject,IID_IEventSink,SampleConnPoint)    
  END_CONNECTION_MAP()    
  说明:A.必须在接口映射中写入INTERFACE_PART(CConnObject,IID_IConnectionPointContainer,ConnPtContainer)以实现IConnectionPointContainer接口.注意,CCmdTarget类内嵌有才ConnPtContainer类以实现IConnectionPointContainer接口,并用m_xConnPtContainer加以记录.    
  B.用BEGIN_CONNECTION_MAP和END_CONNECTION_MAP宏实现连接点映射.CONNECTION_PART定义了实现连接点的类.    
    5.在CConnObject::CConnObject()中写入:    
  EnableConnections();    
    6.实现IEventServer接口    
    IEventServer接口是基于IUnknown接口的,实现IUnknown接口的方法这里不在赘述.在实现文件中写入:    
  STDMETHODIMP    
  CConnObject::XEventServer::DoSomething()    
  {    
  //DoSomething    
  METHOD_PROLOGUE(CConnObject,EventServer)    
  pThis->FireEvent();    
  return   S_OK;    
  }    
  DoSomething()方法可以为客户提供需要的服务.这里着重的是可连接对象在此处触发客户事件接收器的事件,FireEvent()函数是ConnObject类实现的专门触发事件的的函数,代码如下:    
  void   CConnObject::FireEvent()    
  {    
  //获取连接点上的连接指针队列    
  const   CPtrArray*   pConnections   =   m_xSampleConnPoint.GetConnections();    
  ASSERT(pConnections!=NULL);    
  int   cConnections   =   pConnections->GetSize();    
  IEventSink*   pIEventSink;    
  //对每一个连接触发事件    
  for(int   i   =   0;   i   <   cConnections;   i++)    
  {    
  //获取客户事件接收器接口指针    
  pIEventSink   =   (IEventSink*)(pConnections->GetAt(i));    
  ASSERT(pIEventSink!=NULL);    
  //调用客户事件接受器事件处理函数    
  //此函数是出接口定义,由客户事件接收器实现的    
  pIEventSink->EventHandle();    
  }    
  }    
   
   
  Top

10 楼yintongshun(左岸思雨)回复于 2003-12-02 12:03:57 得分 0

COM中的可连接对象与连接点机制及其MFC程序实现    
    3、客户事件接收器(Sink)    
    事件接收器也是COM对象,也可以用嵌套类来实现,但是它只是客户的一个内部对象,所以可以没有CLSID和类厂.下面示例是一个对话框程序,对话框有三个按钮:”连接”(IDC_CONNECT),”断开”(IDC_DISCONNECT),”事件”(IDC_EVENT).    
    1.创建一个基于对话框的工程:ConnClient.    
    2.在CConnClientDlg中首先加入#include   “IConnObject.h”,然后在对话框类声明中声明事件接收器嵌套类:    
  BEGIN_INTERFACE_PART(EventSink,IEventSink)    
  STDMETHOD(EventHandle)();    
  END_INTERFACE_PART(EventSink)    
  同时声明几个私有变量:    
  private:    
  LPCONNECTIONPOINTCONTAINER   pConnPtCont;//记录组件对象    
  //IConnectionPointContainer接口指针    
  LPCONNECTIONPOINT   pConnPt;//记录连接点接口指针    
  DWORD   m_dwCookie;//记录连接标识    
  IUnknown*   m_pIUnknown;//用以记录组件对象IUnknown接口指针    
    3.实现事件接收器:    
  STDMETHODIMP_(ULONG)    
  CConnClientDlg::XEventSink::AddRef()    
  {    
  return   1;    
  }    
  STDMETHODIMP_(ULONG)    
  CConnClientDlg::XEventSink::Release()    
  {    
  return   0;    
  }    
  STDMETHODIMP    
  CConnClientDlg::XEventSink::QueryInterface(REFIID   riid,void**   ppvObj)    
  {    
  METHOD_PROLOGUE(CConnClientDlg,EventSink)    
  if(IsEqualIID(riid,IID_IUnknown)||    
  IsEqualIID(riid,IID_IEventSink))    
  {    
  *ppvObj   =   this;    
  AddRef();    
  return   S_OK;    
  }    
  else    
  {    
  return   E_NOINTERFACE;    
  }    
  }    
  STDMETHODIMP    
  CConnClientDlg::XEventSink::EventHandle()   //此函数将被可连接对象调用    
  {    
  ::AfxMessageBox("源对象向事件接收器发出了的通知!");    
  return   S_OK;    
  }    
    4.初始化COM库并创建组件对象实例    
    在CConnClientDlg::OninitDialog()中写入:    
  HRESULT   hResult;    
  hResult   =   ::CoInitialize(NULL);    
  if(FAILED(hResult))    
  {    
  ::AfxMessageBox("不能初始化COM库!");    
  return   FALSE;    
  }    
  m_pIUnknown   =   NULL;    
  hResult   =   ::CoCreateInstance(CLSID_ConnObject,NULL,    
  CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&m_pIUnknown);    
  if(FAILED(hResult))    
  {    
  m_pIUnknown   =   NULL;    
  ::AfxMessageBox("不能创建ConnObject对象!");    
  return   FALSE;    
  }    
  m_dwCookie   =   0;//预置连接标识为0    
   
  COM中的可连接对象与连接点机制及其MFC程序实现    
    5.在按钮”连接”(IDC_CONNECT)的CLICK事件处理函数void   CConnClientDlg::OnConnect()中写入:    
  void   CConnClientDlg::OnConnect()    
  {    
  if(m_dwCookie!=0)    
  {    
  return;    
  }    
  if(m_pIUnknown!=NULL)    
  {    
  HRESULT   hResult;    
  hResult   =   m_pIUnknown->QueryInterface(IID_IConnectionPointContainer,    
  (void**)&pConnPtCont);    
  if(FAILED(hResult))    
  {    
  ::AfxMessageBox("不能获取对象的IConnectionPointContainer接口!");    
  return;    
  }    
  ASSERT(pConnPtCont!=NULL);    
  hResult   =   pConnPtCont->FindConnectionPoint(IID_IEventSink,&pConnPt);    
  if(FAILED(hResult))    
  {    
  pConnPtCont->Release();    
  ::AfxMessageBox("不能获取对象的IEventSink连接点接口!");    
  return;    
  }    
  ASSE