【问题标题】:Image transfer through socket between C++ and C# application在 C++ 和 C# 应用程序之间通过套接字传输图像
【发布时间】:2015-11-16 16:02:08
【问题描述】:

我有用 C++ 制作屏幕截图的服务器应用程序,它必须传输到用 C# 编写的客户端。我遇到了一些问题:

  1. 我不能只传输bmp图像,因为它太重了,所以我需要将图像转换为另一种格式(jpg,png也许?)
  2. 我需要在客户端检索图片的字节数组并将其用作图片框的图像。

问题是我不知道实现这一点的正确算法。将图像保存到服务器端的文件并传输文件,然后将文件保存在客户端,然后才将其读取到图片框也不是解决方案。所以,总结一下我的任务:如何将位图转换为轻量级格式并将其传输到客户端?

感谢您的宝贵时间!

【问题讨论】:

  • 有许多库可以从 BMP 转换为 JPEG。你不需要知道它的“算法”,因为它不是微不足道的。

标签: c# c++ sockets bitmap


【解决方案1】:

如果您可以在您的 c++ 应用程序中使用 .Net 托管代码,那么您可以使用本机库(请参阅 https://stackoverflow.com/a/3517974/149818)。

如果您仅受 clear c++ 限制,请参阅LodePNG

【讨论】:

    【解决方案2】:

    您可以在 c++ 端使用 libpng 转换为 .png。 在 C# 端 Bitmap 类 .png 可以通过以下方式加载:

    new Bitmap(png);
    

    记住:我可能对 C# 方面有误。

    【讨论】:

      【解决方案3】:

      非常感谢您回答这个问题!在对堆栈进行一些研究后,我发现了类似的问题,并且有一个将 jpg 图像转换为准备通过套接字传输的字节数组的代码。在这里(有些变量可能没有使用)

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

      【讨论】:

      • 该代码有潜在的内存泄漏。获取该代码并使用适当的构造将其转换为 C++ 会有很多好处。
      • 其实是我的错,server是用C/winapi写的。你能指出你发现的漏洞吗?
      • 我看到的第一个是:BYTE *buffer = new BYTE[stg.cbSize.LowPart];new 的调用会引发std::bad_alloc 异常,它不会返回NULL。代码的问题(即使它尽了最大努力)是检查每个返回路径并尝试清理内存。问题是,如果在所有这些过程中抛出异常,则不会调用清理代码。在 C++ 中,如果您使用 RAII 技术,则不必这样做。可以使用std::unique_ptr 来代替对new 的裸调用。
      猜你喜欢
      • 2015-05-29
      • 1970-01-01
      • 1970-01-01
      • 2012-09-07
      • 2012-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多