使用 Winsock、GetDIBits 和 SetDiBits 进行位图传输
我开始使用 C++ 开发类似于远程控制应用程序的东西。我希望将特定窗口的屏幕截图传输到另一台电脑并将其显示在窗口中。 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;
}
}
}
}
我在这里提供的代码有点长,因为我不确定什么可能有用。先感谢您。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
发布代码的问题在于,为总传输数据长度分配了两个 BYTE([data+1] 和 [data+2])。两个字节可处理最多 64K 的数据,而图像很容易超出这个范围,不会检查
i
值是否溢出。为了使代码片段恢复活力,需要在其中添加位,以便它们可以保持真实的长度。也就是说,应该有一个或两个附加字节来使数据包长度为 24 或 32 位值。
The problem with posted code is that that there were two
BYTE
s 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.
我分配的字节数不足以存储数据包大小,现在我增加了该数字,它就可以工作了。 ;-)
I assigned an insufficient amount of bytes to store the packetsize, it works now that I increased that number. ;-)