Bitmap transfer using Winsock, GetDIBits and SetDi

2019-02-18 16:53发布

问题:

I started working on something similar to a remote control application in c++. I wish to transfer a particular window's screenshot to another PC and display it in a window. Both GetDIBits and SetDIBits functions succeed, the connection is established, the data is sent, yet the image does not appear on the other side, just blackness.

Here's my sending code:

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);
    }
  }
}

And here is the receiving side:

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;
      }
    }
  }
}

The code I presented here is a bit long, because I wasn't sure what might be of use. Thank you in advance.

回答1:

I assigned an insufficient amount of bytes to store the packetsize, it works now that I increased that number. ;-)



回答2:

The problem with posted code is that that there were two BYTEs only ([data+1] and [data+2]) allocated for the total transfer data length. Two bytes handle up to 64K of data and images easily go beyond that, i value is not checked for overflows.

To bring the code snippet back to life, one needs to add bits there so that they could hold real length. That is, there should be an additional byte or two to make packet length 24 or 32 bit value.