如何通过套接字发送 HBITMAP 然后返回 HBITMAP?

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

所以我有一个可用的 HBITMAP,可以使用 SetClipboardData 将其设置到剪贴板。我想要做的是将 HBITMAP 发送到另一个应用程序(不向磁盘写入任何内容),该应用程序将接收它并能够将其设置到剪贴板,就像原始应用程序可以做的那样。我知道您不能只发送句柄,所以我使用了 GetObjectGetDIBits 并发送了收集到的数据。我查看了所有其他人关于通过网络发送 hbitmap 的 Whosebug 问题,但 none 给出的答案对我有用。

更新:更新后代码仍然无法运行。

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

这是我的代码现在的样子:

客户:

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

服务器:

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

感谢任何帮助!

您可以将 HBITMAP 保存到内存中,就像存储在文件中一样..

使用Bitmap::FromHBITMAP()制作GDI+位图。

使用CreateStreamOnHGlobal,您可以在内存中创建流.. 并使用 GDI+ Bitmap::Save(),您可以将位图文件写入内存..

发送到套接字..

在socket的另一端,内存到Stream和Bitmap。

搜索 MSDN。你可以找到例子...

请使用LoadImage加载BMP图片。

因为你问题中提供的代码是获取桌面背景,而你提供的GIF图片是加载了api的本地图片文件(GIF太模糊,看不懂确定您使用的 API)。

经过我的测试,应该是你加载bmp图片的时候出错了。

修改后的代码:

服务器:

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

更新:

对于位图像素,请注意捕获的数据与普通位图数据的顺序相反,因此我们必须将其转换为正确的。

修改后的客户端代码:

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