Search code examples
c++winapibitmapwinsockhbitmap

How can you send a HBITMAP over a socket then back to a HBITMAP?


So I have a working HBITMAP which can be set to the clipboard using SetClipboardData. What I'm trying to do is send the HBITMAP to another application (without writing anything to disk) which will receive it and be able set it to the clipboard just as the original application could do. I know that you can't just send the handle so I used GetObject and GetDIBits and sent the collected data across. I've looked at all the other people's stackoverflow questions about sending hbitmaps over a network but none of the answers given have worked for me.

UPDATE: Code still not working after updated.

This is now what it shows as when I paste the clipboard into mspaint

Here's what my code looks like now:

Client:

#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")
#define Port 6000

SOCKET Socket, Sub;
WSADATA Winsock;
sockaddr_in Addr;
sockaddr_in IncomingAddress;
int AddressLen = sizeof(IncomingAddress);

BOOL send_function(BYTE* dib, int dib_size) {

    WSAStartup(MAKEWORD(2, 2), &Winsock);    // Start Winsock

    if (LOBYTE(Winsock.wVersion) != 2 || HIBYTE(Winsock.wVersion) != 2)    // Check version
    {
        WSACleanup();
        return 0;
    }

    Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    ZeroMemory(&Addr, sizeof(Addr));
    Addr.sin_family = AF_INET;
    Addr.sin_port = htons(Port);
    bind(Socket, (sockaddr*)&Addr, sizeof(Addr));

    if (listen(Socket, 1) == SOCKET_ERROR)
    {
        printf("listening error\n");
    }
    else
    {
        printf("listening ok\n");
    }

    if (Sub = accept(Socket, (sockaddr*)&IncomingAddress, &AddressLen))
    {
        char* ClientIP = inet_ntoa(IncomingAddress.sin_addr);
        int ClientPort = ntohs(IncomingAddress.sin_port);
        printf("Client conncted!\n");
        printf("IP: %s:%d\n", ClientIP, ClientPort);

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

        char bufsize[10];
        sprintf(bufsize, "%d", dib_size);

        send(Sub, (char*)bufsize, 10, 0);
        send(Sub, (char*)dib, dib_size, 0);


        closesocket(Sub);
        closesocket(Socket);
        WSACleanup();
    }
}


int main()
{
    HWND hwnd = GetWindow(GetForegroundWindow(), GW_HWNDLAST);
    RECT rc;
    GetWindowRect(hwnd, &rc);
    HDC hdcScreen = GetDC(NULL);
    HDC hdc = CreateCompatibleDC(hdcScreen);
    HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen,
        rc.right - rc.left, rc.bottom - rc.top);
    SelectObject(hdc, hbmp);
    //Print to memory hdc
    PrintWindow(hwnd, hdc, NULL);



    auto hcopy = (HBITMAP)CopyImage(hbmp, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);

    BITMAP bm;
    GetObject(hcopy, sizeof(bm), &bm);

    BITMAPINFOHEADER bi = { sizeof(bi) };
    bi.biWidth = bm.bmWidth;
    bi.biHeight = bm.bmHeight;
    bi.biBitCount = bm.bmBitsPixel;
    bi.biPlanes = bm.bmPlanes;
    bi.biSizeImage = bm.bmWidthBytes * bm.bmHeight;

    int dib_size = sizeof(bi) + bi.biSizeImage;
    BYTE* dib = new BYTE[dib_size];
    memcpy(dib, &bi, sizeof(bi));
    memcpy(dib + sizeof(bi), bm.bmBits, bi.biSizeImage);
    send_function(dib, dib_size);

    //cleanup
    DeleteObject(hcopy);
    delete[]dib;
    getchar();
    return 0;
}

Server:

#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")

SOCKET Socket;
WSADATA Winsock;
sockaddr_in Addr;
int Addrlen = sizeof(Addr);


void receive_function(BYTE* dib, int dib_size)
{
    BITMAPINFOHEADER* bi = (BITMAPINFOHEADER*)dib;
    BYTE* bits = dib + sizeof(bi);
    HBITMAP hbitmap = CreateBitmap(bi->biWidth, bi->biHeight, bi->biPlanes, bi->biBitCount, bits);
    OpenClipboard(NULL);
    EmptyClipboard();
    SetClipboardData(CF_BITMAP, hbitmap);
    CloseClipboard();
    printf("Clipboard set!");
    getchar();
}


int main()
{

    WSAStartup(MAKEWORD(2, 2), &Winsock);    // Start Winsock

    if (LOBYTE(Winsock.wVersion) != 2 || HIBYTE(Winsock.wVersion) != 2)    // Check version
    {
        WSACleanup();
        return 0;
    }

    Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    ZeroMemory(&Addr, sizeof(Addr));    // clear the struct
    Addr.sin_family = AF_INET;    // set the address family
    Addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    Addr.sin_port = htons(6000);    // set the port

    if (connect(Socket, (sockaddr*)&Addr, sizeof(Addr)) < 0)
    {
        printf("Connection failed !\n");
        getchar();
        return 0;
    }

    printf("Connection successful !\n");

    printf("Receiving data .. \n");

    int dib_size = 0;
    char Filesize[10];

    if (recv(Socket, Filesize, 10, 0)) // File size
    {
        dib_size = atoi(Filesize);
    }
    printf("File size: %d\n", dib_size);

    BYTE* dib = new BYTE[dib_size];
    if (recv(Socket, (char*)dib, dib_size, 0))
    {
       receive_function(dib, dib_size);
    }

    getchar();
    return 0;
}

Any and all help appreciated!


Solution

  • Please use LoadImage to load BMP image.

    Because the code you provided in your question is to get the background of the desktop, and the GIF picture you provided is a local image file loaded with api(GIF is too vague and I can't identify which API you are using).

    After my test, there should be an error occurred while yoou loading the bmp image.

    Modified code:

    Server:

    #include <WinSock2.h>
    #include <Windows.h>
    #include <stdio.h>
    #include <gdiplus.h>
    
    using namespace Gdiplus;
    #pragma comment (lib,"Gdiplus.lib")
    
    #pragma comment(lib, "Ws2_32.lib")
    #pragma warning(disable: 4996)
    
    
    SOCKET Socket;
    WSADATA Winsock;
    sockaddr_in Addr;
    int Addrlen = sizeof(Addr);
    HBITMAP hbitmap;
    
    
    
    int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
    {
        UINT  num = 0;          // number of image encoders
        UINT  size = 0;         // size of the image encoder array in bytes
    
        ImageCodecInfo* pImageCodecInfo = NULL;
    
        GetImageEncodersSize(&num, &size);
        if (size == 0)
            return -1;  // Failure
    
        pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
        if (pImageCodecInfo == NULL)
            return -1;  // Failure
    
        GetImageEncoders(num, size, pImageCodecInfo);
    
        for (UINT j = 0; j < num; ++j)
        {
            if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
            {
                *pClsid = pImageCodecInfo[j].Clsid;
                free(pImageCodecInfo);
                return 0;  // Success
            }
        }
        free(pImageCodecInfo);
        return -1;  // Failure
    }
    
    void receive_function(BYTE* dib, int dib_size)
    {
        BITMAPINFOHEADER* bi = (BITMAPINFOHEADER*)dib;
        BYTE* bits = dib + sizeof(bi);
        hbitmap = CreateBitmap(bi->biWidth, bi->biHeight, bi->biPlanes, bi->biBitCount, bits);
    
    
        /*OpenClipboard(NULL);
        EmptyClipboard();
        SetClipboardData(CF_BITMAP, hbitmap);
        CloseClipboard();
        printf("Clipboard set!");*/
    //  getchar();
    }
    
    
    int main()
    {
        GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR gdiplusToken;
        GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
        WSAStartup(MAKEWORD(2, 2), &Winsock);    // Start Winsock
    
        if (LOBYTE(Winsock.wVersion) != 2 || HIBYTE(Winsock.wVersion) != 2)    // Check version
        {
            WSACleanup();
            return 0;
        }
    
        Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
        ZeroMemory(&Addr, sizeof(Addr));    // clear the struct
        Addr.sin_family = AF_INET;    // set the address family
        Addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        Addr.sin_port = htons(6000);    // set the port
    
        if (connect(Socket, (sockaddr*)&Addr, sizeof(Addr)) < 0)
        {
            printf("Connection failed !\n");
            getchar();
            return 0;
        }
    
        printf("Connection successful !\n");
    
        printf("Receiving data .. \n");
    
        int dib_size = 0;
        char Filesize[10];
    
        if (recv(Socket, Filesize, 10, 0)) // File size
        {
            dib_size = atoi(Filesize);
        }
        printf("File size: %d\n", dib_size);
    
        BYTE* dib = new BYTE[dib_size];
        if (recv(Socket, (char*)dib, dib_size, 0))
        {
            receive_function(dib, dib_size);
        }
        CLSID myClsId;
        int retVal = GetEncoderClsid(L"image/bmp", &myClsId);
        Bitmap *image = new Bitmap(hbitmap, NULL);
        image->Save(L"output.bmp", &myClsId, NULL);
        delete image;
    
        GdiplusShutdown(gdiplusToken);
    
        getchar();
        return 0;
    }
    

    Updated:

    With the bitmap pixels, note that the captured data has reversed order with the normal bitmap data, so we have to convert it to the correct.

    Modeified client code:

    #include <WinSock2.h>
    #include <Windows.h>
    #include <stdio.h>
    
    #pragma comment(lib, "Ws2_32.lib")
    #pragma warning(disable: 4996)
    #define Port 6000
    
    SOCKET Socket, Sub;
    WSADATA Winsock;
    sockaddr_in Addr;
    sockaddr_in IncomingAddress;
    int AddressLen = sizeof(IncomingAddress);
    
    BOOL send_function(BYTE* dib, int dib_size) {
    
        WSAStartup(MAKEWORD(2, 2), &Winsock);    // Start Winsock
    
        if (LOBYTE(Winsock.wVersion) != 2 || HIBYTE(Winsock.wVersion) != 2)    // Check version
        {
            WSACleanup();
            return 0;
        }
    
        Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
        ZeroMemory(&Addr, sizeof(Addr));
        Addr.sin_family = AF_INET;
        Addr.sin_port = htons(Port);
        bind(Socket, (sockaddr*)&Addr, sizeof(Addr));
    
        if (listen(Socket, 1) == SOCKET_ERROR)
        {
            printf("listening error\n");
        }
        else
        {
            printf("listening ok\n");
        }
    
        if (Sub = accept(Socket, (sockaddr*)&IncomingAddress, &AddressLen))
        {
            char* ClientIP = inet_ntoa(IncomingAddress.sin_addr);
            int ClientPort = ntohs(IncomingAddress.sin_port);
            printf("Client conncted!\n");
            printf("IP: %s:%d\n", ClientIP, ClientPort);
    
            printf("Sending data .. \n");
    
            char bufsize[10];
            sprintf(bufsize, "%d", dib_size);
    
            send(Sub, (char*)bufsize, 10, 0);
            send(Sub, (char*)dib, dib_size, 0);
    
    
            closesocket(Sub);
            closesocket(Socket);
            WSACleanup();
        }
    }
    
    
    int main()
    {       
        HBITMAP Bitmap = (HBITMAP)LoadImage(NULL,
            "C:\\Users\\strives\\Desktop\\timg.bmp",  // file containing bitmap
            IMAGE_BITMAP,  // type = bitmap
            0, 0,      // original size
            LR_LOADFROMFILE);
        auto hcopy = (HBITMAP)CopyImage(Bitmap, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
    
        BITMAP bm;
        GetObject(hcopy, sizeof(bm), &bm);
    
        BITMAPINFOHEADER bi = { sizeof(bi) };
        bi.biWidth = bm.bmWidth;
        bi.biHeight = bm.bmHeight;
        bi.biBitCount = bm.bmBitsPixel;
        bi.biPlanes = bm.bmPlanes;
        bi.biSizeImage = bm.bmWidthBytes * bm.bmHeight;
    
        //convert   
        LONG lineSize = bi.biWidth * bi.biBitCount / 8;
        BYTE* pLineData = new BYTE[lineSize];
        BYTE* pStart;
        BYTE* pEnd;
        BYTE* pData = (BYTE*)bm.bmBits;
        LONG lineStart = 0;
        LONG lineEnd = bi.biHeight - 1;
        while (lineStart < lineEnd)
        {
            pStart = pData + (lineStart * lineSize);
            pEnd = pData + (lineEnd * lineSize);
            // Swap the top with the bottom
            memcpy(pLineData, pStart, lineSize);
            memcpy(pStart, pEnd, lineSize);
            memcpy(pEnd, pLineData, lineSize);
            // Adjust the line index
            lineStart++;
            lineEnd--;
        }
    
        int dib_size = sizeof(bi) + bi.biSizeImage;
        memcpy(pData, &bi, sizeof(bi));
        send_function(pData, dib_size);
    
        //cleanup
        DeleteObject(hcopy);
        delete pLineData;
        getchar();
        return 0;
    }