切换到宽版
  • 19581阅读
  • 14回复

献上大量C++学习资料 [复制链接]

上一主题 下一主题
离线郁闷的猪
 
只看楼主 正序阅读 0 发表于: 2006-07-26
如何把位图拷贝到剪贴板中

把位图拷贝到剪贴板中是比较简单的,但是有件事情你可别忘记了,如果位图有调色板的话,你也要把调色板一起拷贝过去,否则的话位图就不能正确显示了。

函数1:拷贝一个DDB到剪贴板中

copybitmaptoclipboard()函数能把一个DDB拷贝到剪贴板中去,如果有调色板它也一起拷贝过去。最后,注意调用detach()函数。这是非常重要的,因为GDI(图形设备接口)对象已经转移到了剪贴板中了。

//copybitmaptoclipboard -拷贝DDB到剪贴板中
//pwnd -打开剪贴板窗口的指针
//bitmap -DDB
//ppal -逻辑调色板的指针,它可能是空的
//note -位图被拷贝后,剪贴板拥有的bitmap和ppal,它应该被分离
//
void copybitmaptoclipboard( const cwnd *pwnd, cbitmap& bitmap, cpalette* ppal )
{
::openclipboard(pwnd->getsafehwnd());
::emptyclipboard() ;
if( ppal )
::setclipboarddata (cf_palette, ppal->getsafehandle() ) ;
::setclipboarddata (cf_bitmap, bitmap.getsafehandle() ) ;
::closeclipboard () ;
bitmap.detach();
if( ppal )
ppal->detach();
}

//函数2:拷贝一个DIB到剪贴板中

函数copydibtoclipboard()与函数copybitmaptoclipboard()很相似,内存句柄应该包括bitmapinfo和bitmap bits,可以使用函数globalalloc()来实现。一旦DIB被拷贝到剪贴板中,内存句柄就会被剪贴板所拥有,而且不会被你的应用程序给释放掉。

// copydibtoclipboard -拷贝一个DIB到剪贴板中
// pwnd -剪贴板的指针
// hdib -包含bitmapinfo和bitmap bits内存句柄
// ppal -逻辑调色板指针,可能是空的
// note -位图被拷贝后,剪贴板拥有的bitmap和ppal,它应该被分离
//
void copydibtoclipboard( const cwnd *pwnd, hglobal hdib, cpalette* ppal )
{
::openclipboard(pwnd->getsafehwnd());
::emptyclipboard();
if( ppal )
::setclipboarddata (cf_palette, ppal->getsafehandle() ) ;
::setclipboarddata (cf_dib, hdib ) ;
::closeclipboard () ;
bitmap.detach();
if( ppal )
ppal->detach();
}

//函数3:拷贝一个图象到剪贴板中去

函数copywndtoclipboard()功能是拷贝一个图象到剪贴板中去

void copywndtoclipboard( cwnd *pwnd )
{
cbitmap bitmap;
cclientdc dc(pwnd);
cdc memdc;
crect rect;

memdc.createcompatibledc(&dc);

pwnd->getwindowrect(rect);

bitmap.createcompatiblebitmap(&dc, rect.width(),rect.height() );

cbitmap* poldbitmap = memdc.selectobject(&bitmap);
memdc.bitblt(0, 0, rect.width(),rect.height(), &dc, 0, 0, srccopy);

pwnd->openclipboard() ;
emptyclipboard() ;
setclipboarddata (cf_bitmap, bitmap.getsafehandle() ) ;
closeclipboard () ;

memdc.selectobject(poldbitmap);
bitmap.detach();
}
离线cefly
只看该作者 14 发表于: 2006-08-29
引用第12楼郁闷的猪2006-08-10 23:24发表的“”:
.....................这是新手社区耶!发点这个也合情合理呀!

有没有搞错。。。这个应该是超越了NOI的范畴了。。。你还嫌它浅。。。。难道是我退步了。。。?
离线johnson
只看该作者 13 发表于: 2006-08-17
多谢。(楼主也应该辛苦了)
离线郁闷的猪
只看该作者 12 发表于: 2006-08-10
.....................这是新手社区耶!发点这个也合情合理呀!
离线r134a
只看该作者 11 发表于: 2006-07-30
引用第10楼eagleoi2006-07-30 14:28发表的“”:
...我是pascal的,但也看的懂c程序,这些...好像OI不需要把


用同感~~~
.


祝大家明年NOIP大获全盛!


.
离线eagleoi
只看该作者 10 发表于: 2006-07-30
...我是pascal的,但也看的懂c程序,这些...好像OI不需要把
离线郁闷的猪
只看该作者 9 发表于: 2006-07-26
离线郁闷的猪
只看该作者 8 发表于: 2006-07-26
控制远程计算机拨号上网并获取其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:
// 通 知 远 程 计 算 机 登 录Internet
ShowMsg(hWin,“Call remote... ");
if ( StartCon( hWin,“connect",“",
“d123",“9W7607714" ) == TRUE )
{
ShowMsg(hWin,“Remote Connected");
// 取remote 分 配 的IP 地 址
rip.dwSize=sizeof(RASPPPIP);
  if ( (ret=RasGetProjectionInfo(hCon,RASP_PppIp,
(LPVOID) &rip,(LPDWORD) &ll ) ) != 0 )
ShowMsg(hWin,“ 取IP 地 址 失 败");
  else
  {
ShowMsg(hWin,rip.szIpAddress);
strcpy(szIp,rip.szIpAddress);
pdest=strrchr(szIp,‘.');
*pdest=‘\0';strcat(szIp,“.1");
ShowMsg(hWin,szIp);
SendCmd(szIp,“REQUEST");
  }
  RasHangUp(hCon);
}
else
  ShowMsg(hWin,“Call remote fail");
hCon=NULL;
break;
case IDC_INTERNET:
// 本 地 计 算 机 登 录Internet
ShowMsg(hWin,“ 正 在 连 接 Internet... ");
StartCon( hWin,“internet",“local@pub2.fz.fj.cn",
“aabb99",“9W163" );
break;
case IDC_ADDRESS:
// 读 取 远 程 计 算 机Internet IP 地 址
  ShowMsg(hWin,“ 正 在 取 IP 地 址...");
  if ( (dwLen=FtpWriteRead(“ftp.maoming.gd.cn",
  “abc.txt",40,cRemoteIP,1))>0 )
  {
  cRemoteIP[dwLen]=‘\0';ShowMsg(hWin,cRemoteIP);
  ShowMsg(hWin,“ 删 除IP 地 址 文 件..");
  MyFtpDeleteFile(“ftp.maoming.gd.cn",“abc.txt");
  }
  else
ShowMsg(hWin,“ 取IP 失 败");
  break;
case IDC_RDISC:
// 通 知 远 程 计 算 机 断 开Internet 连 接
  ShowMsg(hWin,“ 正 在 向 远 端 发 Disconnect...");
  SendCmd(cRemoteIP,“DISCONNECT");
  break;
}
ThreadId=0;hThread=NULL;
return;
}
// 远 程 计 算 机 运 行 线 程 函 数
void ServerThread(void * pp )
{
RASPPPIP rip;
DWORD ret,ll;
SOCKADDR_IN acc_sin, dest_sin; // 地 址
int acc_sin_len,status;
char szMsg[ MY_MSG_LENGTH ];
serv_sock = socket(AF_INET,SOCK_STREAM,0);
dest_sin.sin_family=AF_INET;
dest_sin.sin_addr.s_addr=INADDR_ANY;
dest_sin.sin_port=htons(1023);
bind(serv_sock,(struct sockaddr FAR *)
&dest_sin,sizeof(dest_sin));
listen(serv_sock,MAX_PENDING_CONNECTS);
while( TRUE )
{
acc_sin_len = sizeof(acc_sin);ShowMsg
(hWin,“ 等 待 呼 叫");
rsock = accept( serv_sock,(struct sockaddr FAR *)
&acc_sin,(int FAR *) &acc_sin_len );
if (rsock < 0)
{ShowMsg(hWin,“Accept Error!");break;}
  ShowMsg(hWin,“ 收 到 连 接 请 求!");
status = recv( rsock, szMsg, MY_MSG_LENGTH,
NO_FLAGS_SET );
if (status == SOCKET_ERROR)
{ShowMsg(hWin,“Recv Error!");break;}
if (status)
{
szMsg[status] = ‘\0'; ShowMsg(hWin,szMsg);
if ( strstr(szMsg,“REQUEST") != NULL )
{
isend(rsock,“OK",2,0);
closesocket(rsock);Sleep(3000);
// 等 待 拨 号 连 接 退 出
if (StartCon( hWin,“163",“remote@pub2.fz.fj.cn",
“abcd",“163" ) == TRUE )
{
  // 取 动 态 分 配 的IP 地 址
  rip.dwSize=sizeof(RASPPPIP);
  if ( (ret=RasGetProjectionInfo(hCon,RASP_PppIp,
(LPVOID) &rip,(LPDWORD) &ll ) ) != 0 )
  {ShowMsg(hWin,“ 取IP 地 址 失 败");break;}
  ShowMsg(hWin,rip.szIpAddress);ShowMsg(hWin,
“ Send IP Adrress.... !");
  FtpWriteRead( “ftp.maoming.gd.cn",“abc.txt",
strlen(rip.szIpAddress),rip.szIpAddress,0);
  }
  else
  {ShowMsg(hWin,“Connect internet fail");}
  }//if ( strstr(szMsg,“REQUEST") != NULL )
  if ( strstr(szMsg,“DISCONNECT") != NULL )
  {
send(rsock,“OK",2,0);
closesocket(rsock);RasHangUp(hCon);
  } //end if ( strstr(szMsg,“DISCONNECT") != NULL )
} //end if (status)
else
{ShowMsg(hWin,“ 连 接 断 开! 等 待 下 一 个");
closesocket(rsock);}
} //end while( TRUE )
if(hCon != NULL )
{RasHangUp(hCon);Sleep(3000);}
hCon =NULL;closesocket(serv_sock);hThread=NULL;
return;
}
// 实 现 本 地 计 算 机 向 远 程 计 算 机
发 送 命 令 的 函 数 cIp 远 程 计 算 机
IP 地 址 SzCmd 命 令 字 符 串
BOOL SendCmd(char * cIp,char * szCmd)
{
SOCKADDR_IN dest_sin; // 地 址
int status;
char szMsg[ MY_MSG_LENGTH ];
rsock = socket(AF_INET,SOCK_STREAM,0);
dest_sin.sin_family=AF_INET;dest_sin.sin_
addr.s_addr = inet_addr(cIp);
dest_sin.sin_port=htons(1023);
if (connect( rsock,(PSOCKADDR)
&dest_sin,sizeof(dest_sin) )<0 )
{ShowMsg(hWin,“ 连 接 失 败");
closesocket( rsock );return FALSE;}
if ( send(rsock,szCmd,strlen(szCmd),0) != strlen(szCmd))
{ShowMsg(hWin,“ 送 命 令 失 败");
closesocket( rsock );return FALSE;}
status = recv( rsock, szMsg, MY_MSG
_LENGTH, NO_FLAGS_SET );
if (status == SOCKET_ERROR)
{ShowMsg(hWin,“Recv Error!");
closesocket( rsock );return FALSE;}
szMsg[status] = ‘\0';
if ( status != 0 & & strstr(szMsg,“OK") != NULL )
{closesocket( rsock );return TRUE;}
closesocket( rsock );return FALSE;
}
离线郁闷的猪
只看该作者 7 发表于: 2006-07-26
详述C++语言的VxD与外界通讯的所有接口
一.什么是VxD?

   从多任务操作系统Windows 3.1起,计算机中的任一物理设备x可同时被基于Dos或Windows的多个进程使用,这种一对多的关系称为"设备虚拟化",各进程通过运行在核心层的VxD(虚拟x设备驱动程序)存取物理设备x.操作系统提供给用户的软件服务也可以用VxD实现.计算机中的其它资源,如CPU,内存等也可同时被多个进程使用,各进程在系统提供的虚拟机(VM)环境下存取这类资源.

   VxD可由虚拟机管理器(VMM)在开机时装入核心层(称静态装入,即置VxD于c:\windows\system目录下,在c:\windows\system.ini文件中,对节[386Enh]加一行"device=此VxD文件名"),或由应用程序实时装入(称动态装入),而后,各进程便可存取锁定在内存中的VxD数据区,以实时控制VxD的行为,VxD的内部结构可防止两个进程同时存取其数据区.VxD通过响应VMM发给它的事件与外界交互.

   Windows 95中,基于Dos的每个进程在单独的VM中运行(称在V86模式下运行),既可按Dos单进程方式,在640k低内存中运行(称在实模式下运行),又可利用多进程环境的优点,在整个内存中运行(称在保护模式下运行),以通过95的DPMI接口存取内存高端的Windows图形环境.其它16位或32位应用程序均在同一系统VM中运行.

   下面只讨论95环境下的VxD.

   二. VxD的创建:

   1. 由汇编语言创建VxD:需安装微软公司的Win32 SDK及DDK.

   2. 由C或C++语言创建VxD:需安装VC2.0或BC4.0,及Vireo Software公司的VToolsD软件包.VToolsD含3个实用工具:可创建VxD框架的QuickVxD;可动态装卸VxD的VxD Loader;可显示内存VxD特性的VxD Viewer;

   QuickVxD含7个对话页:

   (1) Device Parameters页包括:最多8个字符的VxD名,唯一标识号(ID),相对其它VxD的装入顺序(VxD Viewer可显出某VxD的装入顺序值Init Order,若指定新VxD的装入顺序小于此Init Order,则新VxD将在此VxD前被装入),实现语言(C或C++),静、动态装入方式等.

   (2) VxD Services页包括:可被其它VxD访问的接口(称为VxD服务),要求本VxD的ID>0,且未与内存各VxD的ID值冲突. 此ID可向微软公司申请,也可使用Vireo公司的VIREO_TEST_ID(3180h).下称此类ID为接口ID.

   (3) API页包括:可被应用程序在实模式/V86模式下、保护模式下、DPMI的实模式/V86模式下、DPMI的保护模式下访问的接口(统称应用接口),前两者要求本VxD提供接口ID,后两者只要求本VxD提供以0结尾的唯一标识串;访问前,先要静态或动态装入本VxD(第4者要求静态装入). 第1,3者可被普通汇编程序访问,第2,4者可被在BC的windows 3.x(16)平台上生成的Windows程序访问.

   (4) Control Messages页包括: 对出现在Windows 3.1及Windows 95中各消息的响应,如静态装入时的DYNAMIC_INIT消息.

   (5) Windows95 Control Messages页包括: 对只出现在Windows 95中各消息的响应,如动态装入时的SYS_DYNAMIC_INIT消息.

   (6) 用C++实现VxD时,Classes页包括: 从虚拟设备驱动程序类VDevice派生的类名(如MyDevice),此类的成员函数将接收(4)及(5)页中出现的大多数消息.

   从VM实例类VVirtualMachine派生的类名(如MyVM),此类的成员函数将接收贯穿在VM生命期中的各消息,如系统VM初启消息Sys_VM_Init;

   从线程实例类VThread派生的类名(如MyThread).此类的成员函数将接收贯穿在线程生命期中的各消息,如新线程初启消息THREAD_INIT;

   (7) Output Files页包括: 体现以上内容的3个VxD文件(.h,.c或.cpp,.mak)将被存放的目录位置.

   3. C++语言的VxD与外界通讯的所有接口: 我们将简要实现my.VxD的应用接口及服务,它们均作为类的函数成员,存于my.h,my.cpp中.

   (1) 被32位C应用程序访问的接口:

   应用程序先用CreateFile打开VxD,后用DeviceIoControl使VMM发送W32_DEVICEIOCONTROL消息给VxD:

   HANDLE h;char ibuf[2],obuf[2];BOOL r;DWORD oc;OVERLAPPED o;

   h=CreateFile("\\\\.\\my.vxd",0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,0); //打开静态my.VxD,或动态装入my.VxD

   r=DeviceIoControl(h,命令码C,ibuf,sizeof(ibuf),obuf,sizeof(obuf),&oc,NULL或&o); /*与my.VxD的事件过程OnW32DeviceIoControl交换数据,用ibuf向VxD传数据,用obuf从VxD取 数据,VxD传回的数据总量放在oc中*/

   CloseHandle(h);//关闭或动态卸下VxD

   my.VxD应在windows 95 control messages页上选W32_DEVICEIOCONTROL事件,在 DWORD MyDevice::OnW32DeviceIoControl(PIOCTLPARAMS p)事件过程中写:

switch(p->dioc_IOCtlCode){
case 命令码C:
用p指向的IOCTLPAR
AMS结构,与应用程序交换数据;
if (成功) return(0); /*
使DeviceIoControl的返回值r为TRUE*/
else return(1);
default:
return(0);
}

   以上做法要求VxD立即交换数据(同步通讯),值FILE_FLAG_DELETE_ON_CLOSE指明 CloseHandle将不在内存中保留引用记数为0的VxD.
   VxD也可延迟交换数据,此时,应用程序先传值FILE_FLAG_DELETE_ON_CLOSE|FILE_FLAG_OVERLAPPED

   到CreateFile,用o.hEvent=CreateEvent(0,TRUE,0,NULL)创建事件,再传o的地址到DeviceIoControl,然后用GetOverlappedResult(h,&o,&oc,TRUE)在o上睡眠.

   此时,p->lpoOverlapped一定大于0,VxD可用VMM服务_LinPageLock,按页上锁p->dioc_InBuf指向的应用程序ibuf区,p->dioc_OutBuf指向的obuf区,p->lpoOverlapped指向的o结构.要交换数据时,可置数据及数据总量到p->dioc_OutBuf及p->lpoOverlapped->O_InternalHigh,然后调用VMM服务

   VWIN32_DIOCCompletionRoutine(p->lpoOverlapped->O_Internal)唤醒应用程序.

   VMM动态装卸VxD时,以命令码0及-1发送W32_DEVICEIOCONTROL消息给VxD,故Vireo公司建议命令码C取[2048,4095].

   (2) 被Real/V86模式下16位应用程序访问的接口:

   my.VxD先要指定接口ID(如3180h),再在API页上选Standard Application Entry Points框中的Real/V86 Mode标签,即可生成MyDevice::V86_API_Entry入口,访问它的汇编程序是:

entry dd ?
mov ax,1684h ;功能号
mov bx,3180h ;接口ID
int 2fh ;取入口的段/
偏移到es/di,成功时,
di及es返回非零值

mov ax,es
or ax,di
jz L0

mov word ptr [entry],di
mov word ptr [entry+2],es

mov ah,码C
call [entry]


   L0: 错误处理


MyDevice::V86_API_Entry(VMHANDLE hVM
,CLIENT_STRUCT* p)入口可以是:
if (p->CBRS.Client_AH==码C) p->CBRS.Client_AL=0;

   (3) 被保护模式下16位应用程序访问的接口:

   与(2)类似,但选Protected Mode标签,即可生成MyDevice::PM_API_Entry入口,访问它的程序是:

int PASCAL WinMain(HANDLE h1,HANDLE h0,
LPSTR lpCmdLine,int nCmdShow){
FARPROC entry; //32位
_asm{
mov ax,1684h
mov bx,3180h
int 2fh;取入口的选择符/偏移到es/di,成功时,di及es返回非零值

mov ax,es
or ax,di
jz L0

mov word ptr [entry],di
mov word ptr [entry+2],es

mov ah,码C
call [entry]
}


   对PM_API_Entry的处理如(3.2).

   (4) 被DPMI的实模式/V86模式下16位应用程序访问的接口:

   与(2)类似,但在API页上选Vendor Specific Application Entry Points中的Real/V86 Mode标签,然后在Vendor ID String中输入唯一标识串my,即可生成My_V86VendorEntry::handler入口,访问它的程序是:

str db 'my',0 ;VxD的唯一标识串
entry dd ?
mov ax,168Ah ;功能号
lea si,str ;要求ds/si值是str的段值/偏移值
int 2Fh ;取入口的段/偏移到es/di,成功时,al返回0
cmp al,0
jne L0
mov word ptr [entry],di
mov word ptr [entry+2],es
...
call [entry]

   对handler的处理如(3.2).

   (5) 被DPMI的保护模式下16位应用程序访问的接口: 与(4)类似,但选Protected Mode标签,即可生成My_ProtVendorEntry::handler入口,访问它的程序是:

int PASCAL WinMain(HANDLE h1,HANDLE h0,
LPSTR lpCmdLine,int nCmdShow){
char *id="my";
FARPROC entry;
_asm{
mov ax,168Ah
mov si,id
int 2Fh ;取入口的选择符/偏移到es/di
cmp al,0
...
}
}

   对handler的处理如(3.2).

   (6) 可被其它VxD访问的接口:

   若your.VxD欲调my.VxD的做两数相减的minus接口,需在my.VxD的VxD service页上输入原型DWORD _cdecl minus(DWORD i,DWORD j),再在MyDevice::minus中,写return(i-j);

   your.mak中,需处理中间文件wrap.cpp:

OBJECTS=your.OBJ wrap.obj
...
wrap.OBJ:wrap.cpp my.h

   wrap.cpp中,对带参数的VxD服务,需用VMM宏指令VxDJmp转入,各参数进入wrap时,已按C的调用约定入栈;对不带参数VxD服务,可调用VMM宏指令VxDCall(接口名):


#include "my.h"
DWORD _cdecl MyDevice::
minus(DWORD i,DWORD j){
VxDJmp(minus);
}

   your.cpp的某一函数f,可用VMM服务Get_DDB,查my.VxD是否已装入,若未装入,则用VxDLDR服务

   VxDLDR_LoadDevice将其装入:


#define DEVICE_MAIN
#include "your.h"
Declare_Virtual_Device(YOUR)
#undef DEVICE_MAIN

   #include "my.h" //此行需在DEVICE_MAIN外


VOID f(){
PDEVICEINFO pinfo;
PDDB pddb;
DWORD r;
pddb=Get_DDB(0,"MY ");
//用空格补全长度小于8的VxD名
if (pddb==0) {//未装入
r=VxDLDR_LoadDevice("my.VxD",
VxDLDR_INIT_DEVICE,&pinfo,&pddb);
if (r!=0) //VxDLDR_LoadDevice未能成功装入my.VxD
return;
}
MyDevice::minus(值1,值2);
}
离线郁闷的猪
只看该作者 6 发表于: 2006-07-26
用VC++ 6.0制作网络自动测试程序
由MICROSOFT公司开发的WINDOWS SOCKETS提供了WINDOWS环境下网络通讯的编程接口。在VC++6.0中,可以通过调用WINDOWS SOCKETS函数,采用原始套接字(RAW SOCKETS)类型和互连网控制消息协议(ICMP),来编制一个能实现PING功能的函数。通过定时调用该函数,就可实现网络的自动测试。若再加上语音报警功能,就是一个很实用的网络测猿绦颉1疚哪饨樯苁迪执斯δ艿某绦虻闹谱鞣椒ā?
   为便于说明起见,我们还是按撚肰C++6.0制作网络测试程序斠晃慕樯艿姆椒ǎ?茸鲆桓黾虻サ耐?绮馐猿绦颍∟etest)。注意,在制作Netest工程的STEP 4 OF 6时 ,要钩选WINDOWS SOCKETS选项。否则,在下面要编译AUTOP.CPP文件时,将会出错。在Netest工程编译成功后,再做以下几项工作:

一、增加AUTOP. CPP到工程文件中

   在工程à添加工程à文件à将AUTOP.CPP添加到当前工程文件中(AUTOP.CPP的源码见下面第三节的内容所示,应事先将其COPY到当前工程的目录中)。

二、在工程中增加自动测试的有关菜单和函数

(1)在撚肰C++6.0制作网络测试程序斠晃慕樯艿腘etest工程中,其Readinfo()函数的最后一条语句GlobalFree(hHost)释放了装载有初始化信息的内存,当随后调用PING.EXE时,问题不是很大。但若调用WINSOCK函数,内存中的初始化信息会被冲掉。所以须将该语句移到程序结束处再执行。可如下增加OnDestroy()函数:

在ClassWizard中,对应Class name=CNetestView, Object IDs= CNetestView,Message =WM_DESTROY,点击Add FunctionàOnDestroyàEdit Code,增加相应代码如下:

void CNtestView::OnDestroy()

   {

     CFormView::OnDestroy();

     GlobalFree(hHost); //从Readinfo()移到此

     for (int i=0; i<nodeNum; i++)

     DeleteObject(lpHost.hrgn);

   }

(2)增加自动测试菜单Autotest:在资源工作区,选中MENUàIDR_MAINFRAMEà点中空的菜单条à属性à令ID=ID_AUTOTEST,标题=Autotest。

   在ClassWizard中,对应Class name=CNetestView,Object IDs=ID_AUTOTEST,Message =COMMAND,点击Add FunctionàOnAutotest àEdit Code,增加相应代码如下:

void CNtestView::OnAutotest()

   {

     SetTimer(1, 30000,NULL); //each 30s interupt 1 time.

     Tc=20;

     CWnd* pParent=GetParent();

     CMenu * pMenu=pParent->GetMenu();

     pMenu->EnableMenuItem(ID_AUTOPING,MF_BYCOMMAND| MF_DISABLED | MF_GRAYED);

     pMenu->EnableMenuItem(ID_STOPAUTO,MF_ENABLED);

     AfxGetMainWnd()->SendMessage (WM_TIMER,0, 0L);

   }

   其中SetTimer()将定时器设为每30秒中断一次。由Tc计算中断次数。余下几句条语句令Autotest菜单变灰,以免多次重入。最后一条语句使得鼠标点击Autotest菜单后,即转到OnTimer()函数开始自动测试。

(3)增加定时测试代码。在ClassWizard中,对应Class name=CNetestView的Message, 选中WM_TIMERàAdd FunctionàEdit Code,在OnTimer() 函数中增加如下代码:



void CNetestView::OnTimer(UINT nIDEvent)

   {

     if(Tc++<20) return;

     KillTimer(1);

     Tc=0;

     BOOL bOK=TRUE;

     InvalidateRect(NULL);

     for(int ipT=0;ipT

   {

if(Autotest(lpHost[ipT].nodeIP,3)==FALSE) {

CString strerr;

strerr.Format("err%d.wav",ipT+1);

sndPlaySound(strerr, SND_LOOP |SND_ASYNC );

HDC hdc= CreateDC("DISPLAY",0,0,0);

SelectObject(hdc,lpHost[ipT].hrgn);

InvertRgn(hdc,lpHost[ipT].hrgn);

DeleteDC(hdc);

bOK=FALSE;

   }

else if(bOK) sndPlaySound("Bird0.wav", SND_ASYNC);

   }

SetTimer(1, 30000,NULL);

CFormView::OnTimer(nIDEvent);

   }

   本程序设计为每10分钟对网络作一次自动测试。所以第一条语句须检查定时器中断是否已够20次(30秒*20=10分钟)。若够的话,就关断定时器,循环调用Autotest()对所有网络节点进行测试。若Autotest()的返回值为FALSE,说明该节点有问题,随即调用对应声波文件发出不停的报警声。在报警的同时,程序继续往下运行,作余下网络节点的测试。若所有节点均正常,则调用BIRD0.WAV声波文件发出动听的鸟鸣声。全部节点测试完后,才用SetTimer()再次启动定时器。

   报警声波文件的制作,可在WINDOWS的附件à娱乐à录音机,通过麦克风录下报警语音。按照在INFO.INI文件中各节点的顺序,将语音文件分别存为ERR1.WAV,ERR2.WAV……。这样,网络节点的报警声就能和出错节点正确对应。

   测试时,若有2个以上网络节点有问题,前一个出错节点只会报警一次,最后一个出错节点则会发出循环报警声。为了便于用户观察出错情况,用InvertRgn()来反相显示出错节点的区域。InvalidateRect(NULL)函数用来使屏幕刷新,以便下一次测试的观察。

三、AUTOP.CPP的源码及说明

AUTO.CPP的源码如下:

//#include 头文件略

. . . . . . . . . . .

typedef struct _ihdr {

BYTE i_type, i_code;

u_short i_cksum,i_id, i_seq;

}IcmpHeader;

struct sockaddr_in saDestAddr;

CString Messtr,Tmpstr;



u_short checksum(u_short *buffer, int size) {

. . . . . . .(略)

}



BOOL Autotest(char far * szDestHost,int Ktest)

{

   WSADATA wsaData;

   SOCKET sockRaw;

   struct sockaddr_in dest,from;

   char icmp_data[10], recvbuf[100];

   unsigned int addr=0;

   int fromlen = sizeof(from);

int timeout = 1000; //ms



WSAStartup(MAKEWORD(2,1),&wsaData) ;

sockRaw = socket (AF_INET,SOCK_RAW,IPPROTO_ICMP);

setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,

(char*)&timeout,sizeof(timeout) );

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

dest.sin_family = AF_INET;

dest.sin_addr.s_addr= inet_addr(szDestHost);

memset(icmp_data,0,sizeof(icmp_data)); //clear icmp_data.

((IcmpHeader*)icmp_data)->i_type = 8; //ICMP_ECHO

((IcmpHeader*)icmp_data)->i_code = 0;

((IcmpHeader*)icmp_data)->i_id = (u_short)GetCurrentProcessId();

((IcmpHeader*)icmp_data)->i_seq = 0;



for(int k=0; k

{

((IcmpHeader*)icmp_data)->i_cksum = 0;

((IcmpHeader*)icmp_data)->i_seq ++;

((IcmpHeader*)icmp_data)->i_cksum=checksum((u_short*)icmp_data,8);

sendto(sockRaw,icmp_data,8,0,(struct sockaddr*)&dest,sizeof(dest));

int bread=recvfrom(sockRaw,recvbuf,1024,0,(struct sockaddr*)&from,

&fromlen);

if (bread == SOCKET_ERROR){

if(k==Ktest-1) goto ERR1 ;

else continue; //try again(3 times)

}

}

return TRUE; //no erros.

ERR1:

closesocket (sockRaw);

sockRaw= INVALID_SOCKET;

WSACleanup();

return FALSE;

}

   为简洁起见,源码中大部分的出错处理语句都删去了。整个函数执行的过程如下:

(1)用WSAStartup()函数初始化WINSOCK DLL。

(2)用socket()函数建立一个原始套接字sockRaw。

(3)用setsockopt()函数来设置套接字的选择项。这里用SO_RCVTIMEO参数来设置接收超时。超时值由timeout=1000设定超时最长时间为1秒钟。

(4)给被测节点的dest和Icmpheader结构变量赋值。其中szDestHost放被测节点的IP地址(点间隔格式),inet_addr()函数将一个点间隔的地址转换成4字节的地址;Icmpheader结构的i_type=8表示发送一个请求响应数据包(ICMP_ECHO),i_id为数据包的标识,i_cksum为数据包的校验和,i_seq用来为发送包计数。

(5)用sendto()函数向被测节点发送信息,用recvfrom()函数接收被测节点的应答信息。若在规定时间内(1秒)收不到应答信息,再连测2次。若3次的发送均收不到应答信息,可认为网络故障。Ktest的值决定对被测节点反复测试的次数。(当收到应答信息时,还认应检查应答信息内容正确后,才认为网络正常。本例略去这些检查似乎也无妨)。

(6)测试完一个节点后,用closesocket()函数关闭套接字,用WSAcCleanup()函数释放为应用程序分配的资源。

至此,就可以编译和执行程序,体会个中乐趣。
快速回复
限100 字节
 
上一个 下一个