VC使用GDI+技术实现的屏幕监控

本文档介绍了如何使用GDI+进行屏幕捕获并将图像通过TCP/IP协议发送到服务端,包括客户端的线程处理、IP地址输入、图片编码和传输,以及服务端的接收、解码和显示。作者强调了运行Release版的重要性,并提供了下载链接以供进一步学习。
摘要由CSDN通过智能技术生成

前言:本例写于2012年百度空间,现在百度空间没了,ishare也找不到了。而且我记得当时这个例子是有问题的,有bug没找出来,监控几十秒后画面就不动了。不算一个好例子,问题多多。

只能说再发一下,具有一些参考意义吧。只做参考使用。

本例不假设有任何意外情况,这个小程序当然不能算完善的。。比如程序关闭了,就关闭了,没有去响应WM_CLOSE消息,然后在里面结束线程。还有服务端窗口等待时透明。。这些琐碎的事,自己去完成吧。。。。
我只是完成关键部分,还是那句话,有兴趣的朋友,可以自己去完善。。比如,地址不对,弹出一个消息提示框。或者服务端关闭了。。。

下工程的话,记住安装GDI+包。不然,生成Release版,倒不需要什么附带的文件,大部分系统版本应该都有gdiplus.dll这个动态链接库吧。

问题:
在自己电脑上,倒没什么问题。(本机测试,IP地址填127.0.0.1就行了)
可一到别人电脑上,只可以坚持几十秒钟,就出问题了,难道是数据丢失了,对不上了。如果是这个的话,那就算了,我以后做一个TCP的,看是什么样的。。如果不是的话,那也算了,因为我估计,可能花很久时间我也找不出来,只能是以后对某些知识点有更深入的了解再来找了。现在,我也不急于解决这个问题。
注意:
如果是用VC编译器打开工程,然后再运行,那就不用注意这个事了。如果是直接点击应用程序运行的话,那么请运行Release版的程序,因为我生成的是Release版的,Debug版是以前的,会有错,客户端和服务端都得Release版。切记。因为之前我就犯了这个错误。我还找了半天。

这个例子所涉及的知识点,我以前都讲过,现在只是把它们组合在一起,如果看不懂的话,按照下面顺序学习。。。这里,我就简短地提一下这个过程。不做详细说明了。

最好先看一下:

屏幕截图,第八十七和第八十八个函数。

如流,新一代智能工作平台

GDI+入门,这里可以找到相关的知识点,如在内存中转换图片格式。

如流,新一代智能工作平台

TCP&UDP协议,只有代码,没多少说明,不懂的可到网上另找相关资料。UDP的例子,在复制代码的时候,不知怎么,有些空格被清除了。再直接复制的话,会出错。需手动添加空格。

如流,新一代智能工作平台

客户端编写的大概过程:

1.包含相关头文件,连接动态链接库,初始化GDI+

3.给对话框类添加一个成员变量 CLSID m_encoderClsid;

3.给对话框添加一个按钮控件,关联变量m_Connect,再添加一个IP地址输入框控件CIPAddressCtrl,关联变量m_IPAddress

4.添加按钮单击消息处理函数,该函数代码如下:

void CMonClientDlg::OnButton1()
{
 // TODO: Add your control notification handler code here
 m_Connect.EnableWindow(FALSE);
 //连接,一切交给线程函数去执行
   CreateThread(NULL,0,ThreadProc,this,0,NULL);
  
}

5.编写线程函数,函数代码如下:

DWORD _stdcall ThreadProc(LPVOID lpParameter)//线程执行函数
{
 //获取IP地址
 CMonClientDlg *pClientDlg=(CMonClientDlg *)lpParameter;
 CString strIPAddress;
 pClientDlg->m_IPAddress.GetWindowText(strIPAddress);


 struct sockaddr_in client,server;
 SOCKET s;
 //初始化winsock
 WSADATA wsaData;
 WSAStartup(0x0202,&wsaData);
 //建立套接字
 s=socket(AF_INET,SOCK_DGRAM,0);
 //初始化客户端地址结构
 memset((char *)&client,0,sizeof(client));
 client.sin_family=AF_INET;
 client.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
 client.sin_port=htons(0);
 //绑定socket对象到客户地址
 bind(s,(struct sockaddr *)&client,sizeof(client));
 //初始化服务器地址结构,确定要连接的服务器
 memset((char *)&server,0,sizeof(server));
 server.sin_family=AF_INET;
 server.sin_addr.S_un.S_addr=inet_addr(strIPAddress);

 server.sin_port=htons(39872);
  
 HWND DeskWnd=::GetDesktopWindow();//获取桌面窗口句柄
    RECT DeskRC;
    ::GetClientRect(DeskWnd,&DeskRC);//获取桌面窗口大小
    HDC DeskDC=GetDC(DeskWnd);
 //兼容位图
    HBITMAP hDeskBmp=::CreateCompatibleBitmap(DeskDC,DeskRC.right,DeskRC.bottom);
    HDC memDC;
 //兼容DC
    memDC=::CreateCompatibleDC(DeskDC);
    SelectObject(memDC,hDeskBmp);
    //无限循环,不停地的捕获桌面图像,发送数据
 while(TRUE)
 {
  ::BitBlt(memDC,0,0,DeskRC.right,DeskRC.bottom,DeskDC,0,0,SRCCOPY);
  Bitmap bmp(hDeskBmp,NULL);

  //创建流

  IStream *pStream;
  ::CreateStreamOnHGlobal(NULL,TRUE,&pStream);
  //以JPEG图片格式储存数据到流中
  bmp.Save(pStream,&pClientDlg->m_encoderClsid);
  //获得与流对应的内存句柄
  HGLOBAL hMem;
  ::GetHGlobalFromStream(pStream,&hMem);
  DWORD dwSize=::GlobalSize(hMem);//获得内存块大小

  BYTE *pImgData=(BYTE *)GlobalLock(hMem);//获得内存块首地址*/
  //发送数据大小,告诉服务端有多少数据要发送
     sendto(s,(char *)&dwSize,sizeof(DWORD),0,(struct sockaddr *)&server,sizeof(server));
  DWORD sendSize=60000;//每次发送的数据大小
        while(TRUE)
  {
         if(dwSize>sendSize)
         sendto(s,(char *)pImgData,sendSize,0,(struct sockaddr *)&server, sizeof(server));
   else
   {
   sendto(s,(char *)pImgData,dwSize,0,(struct sockaddr *)&server,sizeof(server));
   break;
   }
         pImgData+=sendSize;
   dwSize-=sendSize;
  }
     pStream->Release();//释放资源
  //等候通知再一次发送数据
  char buf[4];
  int serLen=sizeof(server);
  recvfrom(s,buf,4,0,(struct sockaddr *)&server,&serLen);
 }

}

6.编写GetEncoderClsid函数

//获取图片格式的CLSID自定义函数
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num= 0;
UINT size= 0;

ImageCodecInfo* pImageCodecInfo= NULL;

GetImageEncodersSize(&num, &size);
if(size== 0)
{
return -1;
}
pImageCodecInfo= (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo== NULL)
{
return -1;
}

GetImageEncoders(num, size, pImageCodecInfo);

for(UINT j=0; j< num; ++j)
{
if(wcscmp(pImageCodecInfo[j].MimeType, format)== 0)
{
*pClsid= pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}

free(pImageCodecInfo);
return -1;
}

7.在对话框类构造函数获取CLSID,构造函数代码如下:

CMonClientDlg::CMonClientDlg(CWnd* pParent /*=NULL*/)
 : CDialog(CMonClientDlg::IDD, pParent)
{
 //{{AFX_DATA_INIT(CMonClientDlg)
  // NOTE: the ClassWizard will add member initialization here
 //}}AFX_DATA_INIT
 // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 //初始化GDI+
 GdiplusStartup(&m_pGdiToken,&m_gdiplusStartupInput,NULL);
 //获取JPEG的CLSID
 GetEncoderClsid(L"image/jpeg",&m_encoderClsid);
}

服务端编写的大概过程

1.初始化GDI+

2.对话框类添加如下变量

 Image *m_pImg;//保存接收过来的图片
 int cliWidth,cliHeight;//窗口的大小

3.在对应框初始化函数里创建一个线程,代码如下:

 this->MoveWindow(0,0,800,600);
 //等待连接,一切交给线程函数去执行
 CreateThread(NULL,0,ThreadProc,this,0,NULL);

4.编写线程函数,函数代码如下:

DWORD _stdcall ThreadProc(LPVOID lpParameter)//线程执行函数
{

 CMonServerDlg *pSerDlg=(CMonServerDlg *)lpParameter;
 struct sockaddr_in client,server;
 SOCKET s;
 //初始化winsock
 WSADATA wsaData;
 WSAStartup(0x0202,&wsaData);
 //建立套接字
 s=socket(AF_INET,SOCK_DGRAM,0);
 //初始化服务器地址结构
 memset((char *)&server,0,sizeof(server));
 server.sin_family=AF_INET;
 server.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
 server.sin_port=htons(39872);
 //绑定socket对象到服务器地址
 bind(s,(struct sockaddr *)&server,sizeof(server));
 int cliLen=sizeof(client);
 
 DWORD dwSize;
 //无限循环,接收数据,绘制图片
 while(TRUE)
 {
 //接收数据的大小
 recvfrom(s,(char *)&dwSize,sizeof(DWORD),0,(struct sockaddr *)&client,&cliLen);
    //分配内存
 HGLOBAL hMem=::GlobalAlloc(GMEM_MOVEABLE,dwSize);
 //创建流
 IStream *pStream;
 CreateStreamOnHGlobal(hMem,TRUE,&pStream);
 //获得首地址
 BYTE *pImgData=(BYTE *)GlobalLock(hMem);
 DWORD recSize=60000;//每次接收的数据大小
 while(TRUE)
 {
   if(dwSize>recSize)
   recvfrom(s,(char *)pImgData,recSize,0,(struct sockaddr *)&client,&cliLen);
      else
   {
    recvfrom(s,(char *)pImgData,dwSize,0,(struct sockaddr *)&client,&cliLen);
    break;
   }
  pImgData+=recSize;
  dwSize-=recSize;
    }
    if(pSerDlg->m_pImg!=NULL)
 {
  
  delete pSerDlg->m_pImg;
  pSerDlg->m_pImg=NULL;
 }
 
 pSerDlg->m_pImg=Image::FromStream(pStream);
    pStream->Release();

 pSerDlg->Invalidate();
 Sleep(300);//延迟三百毫秒传输,暂时这样了。。。
 //发送消息给客户端,发送图片过来。
 sendto(s,"yes",4,0,(struct sockaddr *)&client,cliLen);
 }

}

5.对话框OnPaint函数,绘制图片,else里代码如下:

 else
 {
     CDialog::OnPaint();
  CDC *pDC=GetDC();
 
  if(m_pImg!=NULL)
  {
      Graphics graphics(pDC->m_hDC);
   graphics.DrawImage(m_pImg,0,0,cliWidth,cliHeight); 
  }
  
  // pDC->Rectangle(0,0,300,300);
 }

似乎就这些了,反正有完整的工程,可以直接到工程里看,需要手打的代码估计不会超过一百行,还觉得难么。

关键在线程函数里的代码,把那些看懂就差不多了。

工程下载地址:

客户端:_下载 - 爱问文库

服务端:_下载 - 爱问文库

再说一句,运行Release版下的程序。没办法了,已经上传上去了。

  • 16
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bczheng1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值