1

I have server application in C++ that making screenshot and it must be transferred to client, that written in C#. I've encountered few problems:

  1. I can't just transfer bmp image because its too heavy, so i need to convert image to another format (jpg, png maybe?)
  2. I need to retreive picture's byte array on client side and use it as picturebox's image.

The problem is that I don't know proper algorithm of achieving this. Saving image to file on server side and transferring a file and then saving file on client and only then reading it to picturebox is not a solution either. So, summarizing my task: How can i convert bitmap to light-weight format and transfer it to client?

Thank you for your time!

  • 1
    There are many libraries that convert from BMP to JPEG. You don't need to know the "algorithm" for it, as it is not trivial. – PaulMcKenzie Nov 16 '15 at 16:13

3 Answers3

2

If you can use .Net managed code from your c++ application then you can use native libs (see https://stackoverflow.com/a/3517974/149818).

If you are restricted by clear c++ only please see LodePNG

Community
  • 1
  • 1
Dewfy
  • 23,277
  • 13
  • 73
  • 121
1

You can use libpng at c++ side for convertion to .png. At C# side Bitmap class .png can be loaded by just:

new Bitmap(png);

Remember: I may be wrong about C# side.

Still Dead
  • 61
  • 6
0

Much thanks for answering that question!After some research on stack I found similar questions, and there was a code that converts jpg image to byte array that is ready to be transferred through socket. Here it is (some variables may be unused)

#include <objidl.h>
#include <Gdiplus.h>
#pragma comment(lib, "gdiplus.lib")

int GetEncoderClsid(WCHAR *format, CLSID *pClsid)
{
    unsigned int num = 0, size = 0;
    GetImageEncodersSize(&num, &size);
    if (size == 0) return -1;
    ImageCodecInfo *pImageCodecInfo = (ImageCodecInfo *)(malloc(size));
    if (pImageCodecInfo == NULL) return -1;
    GetImageEncoders(num, size, pImageCodecInfo);
    for (unsigned int j = 0; j < num; ++j)
    {
        if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0){
            *pClsid = pImageCodecInfo[j].Clsid;
            free(pImageCodecInfo);
            return j;
        }
    }
    free(pImageCodecInfo);
    return -1;
}

BYTE *GetScreeny(LPWSTR lpszFilename, ULONG uQuality, int *buff_size) // by Napalm
{
    ULONG_PTR gdiplusToken;
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    HWND hMyWnd = GetDesktopWindow(); // get my own window
    RECT  r;             // the area we are going to capture 
    int w, h;            // the width and height of the area
    HDC dc;              // the container for the area
    int nBPP;
    HDC hdcCapture;
    LPBYTE lpCapture;
    int nCapture;
    int iRes;
    CLSID imageCLSID;
    Bitmap *pScreenShot;
    HGLOBAL hMem;
    int result;

    // get the area of my application's window      
    //GetClientRect(hMyWnd, &r);
    GetWindowRect(hMyWnd, &r);
    dc = GetWindowDC(hMyWnd);//   GetDC(hMyWnd) ;
    w = r.right - r.left;
    h = r.bottom - r.top;
    nBPP = GetDeviceCaps(dc, BITSPIXEL);
    hdcCapture = CreateCompatibleDC(dc);


    // create the buffer for the screenshot
    BITMAPINFO bmiCapture = {
        sizeof(BITMAPINFOHEADER), w, -h, 1, nBPP, BI_RGB, 0, 0, 0, 0, 0,
    };

    // create a container and take the screenshot
    HBITMAP hbmCapture = CreateDIBSection(dc, &bmiCapture,
        DIB_PAL_COLORS, (LPVOID *)&lpCapture, NULL, 0);

    // failed to take it
    if (!hbmCapture)
    {
        DeleteDC(hdcCapture);
        DeleteDC(dc);
        GdiplusShutdown(gdiplusToken);
        printf("failed to take the screenshot. err: %d\n", GetLastError());
        return 0;
    }

    // copy the screenshot buffer
    nCapture = SaveDC(hdcCapture);
    SelectObject(hdcCapture, hbmCapture);
    BitBlt(hdcCapture, 0, 0, w, h, dc, 0, 0, SRCCOPY);
    RestoreDC(hdcCapture, nCapture);
    DeleteDC(hdcCapture);
    DeleteDC(dc);

    GpImage *bob;
    IStream *ssStr;

    // save the buffer to a file    
    pScreenShot = new Bitmap(hbmCapture, (HPALETTE)NULL);
    EncoderParameters encoderParams;
    encoderParams.Count = 1;
    encoderParams.Parameter[0].NumberOfValues = 1;
    encoderParams.Parameter[0].Guid = EncoderQuality;
    encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;
    encoderParams.Parameter[0].Value = &uQuality;
    GetEncoderClsid(L"image/jpeg", &imageCLSID);

    IStream *pStream = NULL;
    LARGE_INTEGER liZero = {};
    ULARGE_INTEGER pos = {};
    STATSTG stg = {};
    ULONG bytesRead = 0;
    HRESULT hrRet = S_OK;

    //BYTE* buffer = NULL;  // this is your buffer that will hold the jpeg bytes
    DWORD dwBufferSize = 0;  // this is the size of that buffer;


    hrRet = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
    hrRet = pScreenShot->Save(pStream, &imageCLSID, &encoderParams) == 0 ? S_OK : E_FAIL;
    hrRet = pStream->Seek(liZero, STREAM_SEEK_SET, &pos);
    hrRet = pStream->Stat(&stg, STATFLAG_NONAME);

    // allocate a byte buffer big enough to hold the jpeg stream in memory
    BYTE *buffer = new BYTE[stg.cbSize.LowPart];
    //buffer = (BYTE *)malloc(stg.cbSize.LowPart);
    hrRet = (buffer == NULL) ? E_OUTOFMEMORY : S_OK;
    dwBufferSize = stg.cbSize.LowPart;
    //wchar_t message[512];
    //wsprintf(message, L"%d", stg.cbSize.LowPart);
    //MessageBox(NULL, message, NULL, MB_OK);
    // copy the stream into memory
    hrRet = pStream->Read(buffer, stg.cbSize.LowPart, &bytesRead);

    // now go save "buffer" and "dwBufferSize" off somewhere.  This is the jpeg buffer
    // don't forget to free it when you are done

    // After success or if any of the above calls fail, don't forget to release the stream
    if (pStream)
    {
        pStream->Release();
    }

    delete pScreenShot;
    DeleteObject(hbmCapture);
    GdiplusShutdown(gdiplusToken);
    *buff_size = (int)dwBufferSize;
    //return iRes;
    return buffer;

}
  • 1
    That code has potential memory leaks. It would do a lot of good to take that code and convert it to C++ using proper constructs. – PaulMcKenzie Nov 16 '15 at 19:49
  • Actually, it was my mistake, server is writen in C/winapi. Could you point to leaks you found? – Александр Пушкин Nov 16 '15 at 20:32
  • First one I see is this: `BYTE *buffer = new BYTE[stg.cbSize.LowPart];` The call to `new` throws a `std::bad_alloc` exception, it doesn't return NULL. The problem with the code (even though it tries its best) is to check every single return path and attempt to clean up the memory. The problem is that if an exception is thrown in the middle of all of this, that cleanup code won't get called. In C++, you don't have to do this if you use RAII techniques. Usage of `std::unique_ptr` could be used instead of naked calls to `new`. – PaulMcKenzie Nov 16 '15 at 20:57