Reputation: 37
I am writing a simple tool, like TeamViewer, to transmit a bitmap screenshot stream, frame by frame, from one computer to another over a network connection, I am not sure what is happening to make the program behave this way. (Video: https://youtu.be/8jENn2jbu3w?si=aE9zhAhsCVDm3jzm)
I am getting the screenshot of one computer, getting the bitmap information in BITMAPINFOHEADER to determine the size of the buffer based on the screen size calculated pixels.
On the server side (Receiving the data) I am receiving the BITMAPINFOHEADER to determine the size of the buffer for the next frame. Then calling another recv() function to receive the actual Bitmap bytes and then creating a window and calling UpdateWindow to use the received Bitmap to paint to the window.
The screen just keeps getting smaller and smaller and does not draw over the entire screen as shown in the video. One aspect to take note of is that the memory is increasing at a high rate in both applications shown at near the end of the video.
Can anyone see what is wrong with the code I have written?
Client (Sending Screenshot):
#include <WS2tcpip.h>
#include <Windows.h>
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <winuser.h>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "ws2_32.lib")
#pragma pack(push, 1)
WSAData wData;
WORD ver = MAKEWORD(2, 2);
char* lpbitmap = NULL;
int totalBytes = 0;
int remainingBytes = 0;
int sendBmBytes = 0;
int totalBytesSent = 0;
int main() {
int wsOk = WSAStartup(ver, &wData);
if (wsOk != 0) {
std::cerr << "Error Initializing WinSock! Exiting" << std::endl;
return -1;
}
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
//Bind IP address to port
std::string ipAddress = "127.0.0.1";
sockaddr_in sockin;
sockin.sin_family = AF_INET;
sockin.sin_port = htons(8081);
inet_pton(AF_INET, ipAddress.c_str(), &sockin.sin_addr);
std::cout << "Attempting to connect to master controller..." << std::endl;
int x1, y1, x2, y2, w, h;
// get screen dimensions
x1 = GetSystemMetrics(SM_XVIRTUALSCREEN);
y1 = GetSystemMetrics(SM_YVIRTUALSCREEN);
x2 = GetSystemMetrics(SM_CXVIRTUALSCREEN);
y2 = GetSystemMetrics(SM_CYVIRTUALSCREEN);
w = x2 - x1;
h = y2 - y1;
//Connect to server
int connected = connect(sock, (sockaddr*)&sockin, sizeof(sockin)) != SOCKET_ERROR;
while (connected) {
// copy screen to bitmap
HDC hScreen = GetDC(NULL);
HDC hdcCompat = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h);
//Use the previously created device context with the bitmap
HGDIOBJ old_obj = SelectObject(hdcCompat, hBitmap);
StretchBlt(hdcCompat, 0, 0, w, h, hScreen, x1, y1, w, h, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
//BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY);
DWORD dwBmpSize = 0;
BITMAP bmpScreen;
GetObject(hBitmap, sizeof(BITMAP), &bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = w;
bi.biHeight = h;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
HGLOBAL hDIB = GlobalAlloc(GHND, dwBmpSize);
lpbitmap = (char*)GlobalLock(hDIB);
// Gets the "bits" from the bitmap, and copies them into a buffer
// that's pointed to by lpbitmap.
GetDIBits(hScreen, hBitmap, 0,
(UINT)h,
lpbitmap,
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
//HBITMAP hbitTest = CreateBitmap(4096, 2160, 1, 32, lpbitmap);
HBITMAP hbitTest = CreateCompatibleBitmap(hScreen, w, h);
int diSet = SetDIBits(hScreen, hbitTest, 0,
(UINT)h,
lpbitmap,
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
//Prepare buffer and send data
std::cout << "diBitSet: " << diSet << std::endl;
char* buffer = new char[sizeof(BITMAPINFOHEADER)];
std::cout << "size of bitmapinfoheader " << sizeof(BITMAPINFOHEADER) << std::endl;
memcpy(buffer, &bi, sizeof(BITMAPINFOHEADER));
std::cout << "sent Device Context \n" << std::endl;
int sent = send(sock, (char*)buffer, sizeof(bi), NULL);
//check to make sure data was sent
if (sent != SOCKET_ERROR && sent == sizeof(BITMAPINFOHEADER)) {
std::cout << "sent bitmapinfo \n" << std::endl;
}
else {
send(sock, (char*) buffer, sizeof(bi), NULL);
}
//Send bitmap bytes
totalBytes = ((((w * 32 + 31) / 32) * 4) * h) + 1;
remainingBytes = 0;
sendBmBytes = 0;
totalBytesSent = 0;
while (totalBytesSent < totalBytes && remainingBytes >= 0) {
char* bufferOffset = (char*)lpbitmap + sendBmBytes;
sendBmBytes = send(sock, bufferOffset, totalBytes - remainingBytes, NULL);
remainingBytes += totalBytes - sendBmBytes;
totalBytesSent += sendBmBytes;
}
Sleep(500);
}
}
Server (Receiving Bitmap and Displaying it in a new window):
#include <WS2tcpip.h>
#include <Windows.h>
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <winuser.h>
#include <wingdi.h>
#pragma comment(lib, "ws2_32.lib")
#pragma pack(push, 1)
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int getTaskBarHeight();
char* getBitmap();
HBITMAP hbitTest = { 0 };
BITMAPINFOHEADER bmInfo;
HWND hwnd;
SOCKET clientSocket;
char* buf = 0;
char* buffed = 0;
int newWidth = 0;
int newHeight = 0;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
WSADATA wData;
WORD ver = MAKEWORD(2, 2);
int wsOk = WSAStartup(ver, &wData);
if (wsOk != 0)
{
std::cerr << "Error Initializing WinSock! Exiting" << std::endl;
return -1;
}
// Create Socket
SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
if (listening == INVALID_SOCKET)
{
std::cerr << "could not open socket" << std::endl;
return -1;
}
//Bind IP address to port
sockaddr_in sockin;
sockin.sin_family = AF_INET;
sockin.sin_port = htons(8081);
sockin.sin_addr.S_un.S_addr = INADDR_ANY;
//bind socket to ip address and port
bind(listening, (sockaddr*)&sockin, sizeof(sockin));
//Tell winsock that socket is for listening
listen(listening, SOMAXCONN);
//wait for connection
sockaddr_in clientSockAddr;
int clientSize = sizeof(clientSockAddr);
clientSocket = accept(listening, (sockaddr*)&clientSockAddr, &clientSize);
char host[NI_MAXHOST];
char service[NI_MAXSERV];
ZeroMemory(host, NI_MAXHOST);
ZeroMemory(service, NI_MAXSERV);
bool isReceiving = true;
//If client connects
if (getnameinfo((sockaddr*)&clientSockAddr, sizeof(clientSockAddr), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
{
std::cout << host << " Connection established on: " << service << std::endl;
}
else
{
std::cout << "Connection could not be established." << std::endl;
}
buffed = getBitmap();
// Register window class
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = L"BitmapWindowClass";
RegisterClass(&wc);
// Create the window
hwnd = CreateWindow(L"BitmapWindowClass", L"Client Screen", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, bmInfo.biWidth, bmInfo.biHeight, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// Main message loop
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
buffed = getBitmap();
UpdateWindow(hwnd);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// Create a compatible DC for the bitmap
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, hbitTest);
// Draw the bitmap to the window
BitBlt(hdc, 0, 0, bmInfo.biWidth, bmInfo.biHeight, hdcMem, 0, 0, SRCCOPY);
//Get Task Bar height
int taskBar = getTaskBarHeight();
//Stretch image
if (newWidth && newHeight == 0) {
StretchDIBits(hdc, 0, 0, bmInfo.biWidth, bmInfo.biHeight, 0, 0, bmInfo.biWidth,
bmInfo.biHeight, buffed, (BITMAPINFO*)&bmInfo, DIB_RGB_COLORS, SRCCOPY);
}
else {
StretchDIBits(hdc, 0, 0, newWidth, newHeight, 0, 0, bmInfo.biWidth,
bmInfo.biHeight, buffed, (BITMAPINFO*)&bmInfo, DIB_RGB_COLORS, SRCCOPY);
}
// Clean up
SelectObject(hdcMem, hOldBitmap);
DeleteDC(hdcMem);
DeleteDC(hdc);
EndPaint(hwnd, &ps);
buffed = getBitmap();
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
//ShowWindow(hwnd, SW_SHOW);
return 0;
}
case WM_SIZE:
{
// Update the bitmap size based on the new window size
newWidth = LOWORD(lParam);
newHeight = HIWORD(lParam);
// Invalidate the window to trigger a repaint
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
return 0;
}
case WM_CLOSE:
DestroyWindow(hwnd);
WSACleanup();
return 0;
case WM_DESTROY:
DeleteObject(hbitTest);
PostQuitMessage(0);
WSACleanup();
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
char* getBitmap() {
//Receive the bitmap info header
char* bitmapInfo = new char[sizeof(BITMAPINFOHEADER)];
ZeroMemory(bitmapInfo, sizeof(BITMAPINFOHEADER));
int rec = recv(clientSocket, bitmapInfo, sizeof(BITMAPINFOHEADER), NULL);
if (rec == sizeof(BITMAPINFOHEADER)) {
//Copy Bitmap Info Header to Bitmap Info Header Structure
std::cout << "bytes received bitmap info " << rec << std::endl;
memcpy(&bmInfo, bitmapInfo, sizeof BITMAPINFOHEADER);
std::cout << "Incoming width/height: " << bmInfo.biWidth << " " << bmInfo.biHeight << std::endl;
//MessageBoxA(hwnd, (LPCSTR)bmInfo.biWidth, "Screen", MB_OK);
}
//Create receiving buffer based on bitmap image screen size
buf = new char[((((bmInfo.biWidth * 32 + 31) / 32) * 4) * bmInfo.biHeight) + 1];
ZeroMemory(buf, ((((bmInfo.biWidth * 32 + 31) / 32) * 4) * bmInfo.biHeight) + 1);
//Receive bitmap bits
int totalBytes = ((((bmInfo.biWidth * 32 + 31) / 32) * 4) * bmInfo.biHeight) + 1;
int remainingBytes = 0;
int totalBytesReceived = 0;
int recvBmBytes = 0;
while (totalBytesReceived < totalBytes && remainingBytes >= 0) {
char* bufferOffset = (char*)buf + recvBmBytes;
recvBmBytes = recv(clientSocket, bufferOffset, totalBytes - totalBytesReceived, NULL);
totalBytesReceived += recvBmBytes;
std::cout << "receive bitmap bytes: " << recvBmBytes << " " << WSAGetLastError() << std::endl;
}
return buf;
}
int getTaskBarHeight()
{
RECT rect;
SystemParametersInfo(SPI_GETWORKAREA, NULL, &rect, NULL);
return bmInfo.biHeight - rect.bottom;
}
Upvotes: 0
Views: 123