AndroidAcolyteFX
AndroidAcolyteFX

Reputation: 37

Cannot Draw Bitmap to Window

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

Answers (0)

Related Questions