使用 Winsock、GetDIBits 和 SetDiBits 进行位图传输 [关闭]

Posted

技术标签:

【中文标题】使用 Winsock、GetDIBits 和 SetDiBits 进行位图传输 [关闭]【英文标题】:Bitmap transfer using Winsock, GetDIBits and SetDiBits [closed] 【发布时间】:2010-08-22 02:36:25 【问题描述】:

我开始研究类似于 C++ 中的远程控制应用程序的东西。我希望将特定窗口的屏幕截图传输到另一台 PC 并在窗口中显示。 GetDIBits 和 SetDIBits 函数都成功了,建立连接,发送数据,但是另一边没有出现图像,只是一片黑。

这是我的发送代码:

void GetScreenData(BITMAPINFO* bi, BYTE* buf) //gets the bitmap data

  HBITMAP hBitmap;
  BITMAP Bitmap;
  RECT r;

  HDC ActiveDC = GetDC(hActive);
  HDC CopyDC = CreateCompatibleDC(ActiveDC);

  GetWindowRect(hActive, &r);

  int scrWidth = r.right-r.left;
  int scrHeight = r.bottom-r.top;

  hBitmap = CreateCompatibleBitmap(ActiveDC, scrWidth, scrHeight);
  SelectObject(CopyDC, hBitmap);

  BitBlt(CopyDC, 0, 0, scrWidth, scrHeight, ActiveDC, 0, 0, SRCCOPY);

  GetObject(hBitmap, sizeof(BITMAP), &Bitmap);

  int cClrBits = Bitmap.bmPlanes*Bitmap.bmBitsPixel;

  memset(bi, 0, sizeof(BITMAPINFO));

  bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  bi->bmiHeader.biWidth = Bitmap.bmWidth;
  bi->bmiHeader.biHeight = Bitmap.bmHeight;
  bi->bmiHeader.biPlanes = Bitmap.bmPlanes;
  bi->bmiHeader.biBitCount = Bitmap.bmBitsPixel;

  if(cClrBits<24)
  
    bi->bmiHeader.biClrUsed = (1<<cClrBits);
  

  bi->bmiHeader.biCompression = BI_RGB;
  bi->bmiHeader.biSizeImage = ((bi->bmiHeader.biWidth * cClrBits +31) & ~31)/8*bi->bmiHeader.biHeight;

  int i = GetDIBits(CopyDC, hBitmap, 0, scrHeight, buf, bi, DIB_RGB_COLORS);

  printf("GetDIBits returned %i\n", i);

  ReleaseDC(hActive, ActiveDC);
  DeleteDC(CopyDC);


DWORD WINAPI SendImage(LPVOID param) //sends the bitmap data

  BITMAPINFO bi;
  BYTE* data = new BYTE[256*256*256];
  BYTE* buf = new BYTE[256*256*256];
  BYTE *packetsize1, *packetsize2;
  int biSize = sizeof(BITMAPINFO);
  int i, clocks, oldclocks=0;

  while(true)
  
    clocks=clock();

    if((clocks-oldclocks)*CLOCKS_PER_SEC<0.1)
    
      continue;
    

    oldclocks=clocks;

    if(bConnected)
    
      GetScreenData(&bi, buf);

      i=0;

      data[i++]=3;
      packetsize1=&data[i++];
      packetsize2=&data[i++];

      memcpy(data+i, &bi, biSize);

      i+=biSize;

      memcpy(data+i, buf, bi.bmiHeader.biSizeImage);

      printf("Sending image...\n");

      i+=bi.bmiHeader.biSizeImage;

      *packetsize1=int(i/256);
      *packetsize2=int(i%256);

      send(s, (char*)data, i, 0);
    
  

这里是接收方:

void DrawScreen(HDC hdc) //called from windows message WM_PAINT

  HGDIOBJ hobj;

  hobj = SelectObject(RemoteDC, hRemoteBitmap);

  BitBlt(hdc, 0, 0, scrWidth, scrHeight, RemoteDC, 0, 0, SRCCOPY);

  SelectObject(hdc, hobj);


DWORD WINAPI RecvData(LPVOID param)

  BYTE* data = new BYTE[256*256*256];
  int packetsize, num;
  int newWidth, newHeight;
  int recvimgsize=0;

  bool bAwaitingImage = false;

  while(true)
  
    if(bConnected)
    
      num=recv(s, (char*)data, 3, 0);

      if(num>0)
      
        packetsize = data[1]*256+data[2];

        num=recv(s, (char*)(data+3), packetsize-3, 0);
      

      if(num>0)
      
        switch(data[0])
        
          case 2: //received information about window size (image size)
            newWidth = data[3]*256+data[4];
            newHeight = data[5]*256+data[6];

            if(newHeight!=scrHeight || newWidth!=scrWidth)
            
              scrWidth = newWidth;
              scrHeight = newHeight;

              RECT r;

              GetWindowRect(hwnd, &r);
              SetWindowPos(hwnd, NULL, r.left, r.top, scrWidth, scrHeight, 0);

              HDC ThisDC = GetDC(hwnd);

              DeleteDC(RemoteDC);
              RemoteDC = CreateCompatibleDC(ThisDC);

              DeleteObject(hRemoteBitmap);
              hRemoteBitmap = CreateCompatibleBitmap(ThisDC, scrWidth, scrHeight);

              SelectObject(RemoteDC, hRemoteBitmap);

              ReleaseDC(hwnd, ThisDC);
            
            break;
          case 3:
          
            BITMAPINFO bi;
            HBITMAP hBitmap;

            int biSize = sizeof(BITMAPINFO);
            memcpy(&bi, data+3, biSize);
            SetDIBits(RemoteDC, hRemoteBitmap, 0, scrHeight, data+biSize+3, &bi, DIB_RGB_COLORS);

            InvalidateRect(hwnd, NULL, false);

            break;
          
        

        continue;
      

      if(num==0)
      
        //connection closed
        bConnected=false;
      else
        //error
        bConnected=false;
      
    
  

我在这里提供的代码有点长,因为我不确定什么可能有用。提前谢谢你。

【问题讨论】:

您是否验证过您的解码和显示代码,而无需先通过网络发送? 刚刚做了(不知何故我以前没有想到)。它按预期工作。一定是转账本身有问题…… 很好,这消除了很大一部分问题。不能保证任何事情,但我会看看网络方面是否有什么让我兴奋的事情:) 非常感谢。 ;-) 我意识到这可能是因为通过一次 recv 调用,我没有获得所有数据,但是在进行了一个循环收集数据直到它获得数据包大小字节之后,它仍然是相同的。 (而且我还注意到,在发送了一定数量的图像后,它就停止发送它们了——而且我用 printscreen 制作的每张截图都是黑白的负片,直到我关闭应用程序......奇怪的东西) 这与您的问题没有直接关系,但您可能应该在那里为您的缓冲区调用 delete[]。 【参考方案1】:

我分配的字节数不足来存储数据包大小,现在我增加了这个数字。 ;-)

【讨论】:

【参考方案2】:

发布代码的问题在于,只有两个BYTEs([data+1] 和 [data+2])分配给总传输数据长度。两个字节处理高达 64K 的数据和图像很容易超出此范围,i 值不检查溢出。

要使代码 sn-p 恢复活力,需要在其中添加位,以便它们可以保持真实长度。也就是说,应该有一个或两个额外的字节来使数据包长度为 24 或 32 位值。

【讨论】:

以上是关于使用 Winsock、GetDIBits 和 SetDiBits 进行位图传输 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

处理 GetDIBits() 返回的像素缓冲区的正确方法是啥?

winsock编译错误,找不到addrinfo结构和一些相关函数

GetDIBits 返回兼容位图的无效颜色数组

GetDIBits 遍历位图 获取像素的颜色值(RGB)

GetDIBits 遍历位图 获取像素的颜色值(RGB)

GetDIBits 返回所有值为 0 的数组