I have IStream and I want to convert it to base 64 string in c++ so that I can use it in C++/cli to convert it to .net stream.
Similar way, I want to know how to convert base 64 string back to IStream.
My code uses ATL CString...
template<class T>
HRESULT StreamToBase64(IStream* pStream, T &strOut)
{
// Clear buffer
strOut.ReleaseBuffer(0);
if (!pStream)
return S_OK;
// Get size
STATSTG stat;
pStream->Stat(&stat, STATFLAG_NONAME);
unsigned iLen = stat.cbSize.LowPart;
// Seek to start
LARGE_INTEGER lPos;
lPos.QuadPart = 0;
HRESULT hr = pStream->Seek(lPos, SEEK_SET, nullptr);
if (FAILED(hr))
return hr;
// Reserve the memory
strOut.GetBuffer(Mfx::BinToBase64GetRequiredLength(iLen));
strOut.ReleaseBuffer(0);
// Use one line to decode a time. On line of Base64 code has usually 39 chars. We
// use a nearly 1kb buffer
const int iBase64LIneLen = 76/4*3;
BYTE bLine[(1024/iBase64LIneLen)*iBase64LIneLen];
ULONG bRead;
while (SUCCEEDED(hr = pStream->Read(bLine, sizeof(bLine), &bRead)) && bRead!=0)
strOut += T(Mfx::BinToBase64A(bLine, bRead));
// And seek back to start
pStream->Seek(lPos, SEEK_SET, nullptr);
return SUCCEEDED(hr) ? S_OK : hr;
}
HRESULT StreamToBase64(IStream* pStream, CStringA &strOut)
{
return StreamToBase64<CStringA>(pStream, strOut);
}
HRESULT StreamToBase64(IStream* pStream, CStringW &strOut)
{
return StreamToBase64<CStringW>(pStream, strOut);
}
Related
I came across this example and couldn't figure out how to change stream->InitializeFromFilename to something that outputs to STDOUT, inside the HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride, LPBYTE pixels, LPWSTR filePath, const GUID &format) function. Tried using stream->InitializeFromMemory but hit a wall due to minimal C++ experience.
Reasons behind this is that i'm building something and would like to pipe the stdout of the example using something else.
Regards in advance
Thanks to #SimonMourier, I was able to update his code to achieve what was needed.
SavePixelsToFile32bppPBGRA now writes the data to an IStream pointer, streamOut. It also invokes a new function at the end, to output data.
HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride, LPBYTE pixels, const GUID& format)
{
if (!pixels)
return E_INVALIDARG;
HRESULT hr = S_OK;
IWICImagingFactory* factory = nullptr;
IWICBitmapEncoder* encoder = nullptr;
IWICBitmapFrameEncode* frame = nullptr;
IWICStream* streamOut = nullptr;
IStream * streamIn = nullptr;
GUID pf = GUID_WICPixelFormat32bppPBGRA;
BOOL coInit = CoInitialize(nullptr);
HRCHECK(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory)));
HRCHECK(factory->CreateStream(&streamOut));
HRCHECK(CreateStreamOnHGlobal(NULL, true, &streamIn));
HRCHECK(streamOut->InitializeFromIStream(streamIn));
HRCHECK(factory->CreateEncoder(format, nullptr, &encoder));
HRCHECK(encoder->Initialize(streamOut, WICBitmapEncoderNoCache));
HRCHECK(encoder->CreateNewFrame(&frame, nullptr)); // we don't use options here
HRCHECK(frame->Initialize(nullptr)); // we dont' use any options here
HRCHECK(frame->SetSize(width, height));
HRCHECK(frame->SetPixelFormat(&pf));
HRCHECK(frame->WritePixels(height, stride, stride * height, pixels));
HRCHECK(frame->Commit());
HRCHECK(encoder->Commit());
sendIStreamToOutput(streamOut);
cleanup:
RELEASE(streamIn);
RELEASE(streamOut);
RELEASE(frame);
RELEASE(encoder);
RELEASE(factory);
if (coInit) CoUninitialize();
return hr;
}
sendIStreamToOutput reads the data from the IStream and writes it to std::cout (with binary mode active).
void sendIStreamToOutput (IStream * pIStream) {
STATSTG sts;
pIStream->Stat(&sts, STATFLAG_DEFAULT);
ULARGE_INTEGER uli = sts.cbSize;
LARGE_INTEGER zero;
zero.QuadPart = 0;
ULONG size = (ULONG)uli.QuadPart;
char* bits = new char[size];
ULONG written;
pIStream->Seek(zero, STREAM_SEEK_SET, NULL);
pIStream->Read(bits, size, &written);
std::ostream& lhs = std::cout;
_setmode(_fileno(stdout), _O_BINARY);
lhs.write(bits, size);
fflush(stdout);
delete[] bits;
}
I'm attempting to read text into a CString using an LPSTREAM, but it doesn't seem to be working correctly, here is the code that I'm calling:
static HRESULT UTL_ReadStreamTxt(MyStorage* pSrcStg, const char* pszStream, CString* myCStr, int size)
{
HRESULT hrRet = STG_E_INVALIDPARAMETER;
LPSTREAM lpSrc = NULL;
ULONG ul;
TRY
{
USES_CONVERSION;
HRESULT hrSrc = pSrcStg->GetStg()->OpenStream(CT2COLE(strSrc),
NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE,
0,
&lpSrc);
if (hrSrc != NOERROR)
{
hrRet = hrSrc;
}
else
{
hrRet = lpSrc->Read(&myCStr, size, NULL); // Read into CString
}
}
CATCH_ALL(e)
{
hrRet = STG_E_UNKNOWN;
}
END_CATCH_ALL
_AfxRelease((LPUNKNOWN*)&lpSrc);
return hrRet;
}
When it reads into the string, Visual Studio says that the data in the CString is corrupted.
The compound storage's stream contents are the following:
abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
I'm not entirely sure I'm using Read() correctly, How do I fix this issue?
The main problem is that you are passing a bad pointer to Read(). You are passing the memory address of the myCStr parameter itself, not the address of the CString being pointed at, or more accurately, the memory address of a character buffer that the CString owns. The code compiles only because Read() takes a simple void* pointer to a buffer, and any pointer is implicitly convertible to void*.
Also note that CString is based on TCHAR, which maps to either char or wchar_t depending on whether you are compiling your project for ANSI/MBCS or Unicode. So, reading from the stream directly into a CString will only work correctly if:
the stream contains ANSI characters and TCHAR maps to char.
the stream contains UTF-16 characters and TCHAR maps to wchar_t.
If the stream's character type does not match the character type used by CString, you would have to first read the stream into an intermediate buffer and then convert that to TCHAR before it can be stored in the CString.
Try something more like this:
static HRESULT UTL_ReadStreamTxt(MyStorage* pSrcStg, const char* pszStream, CString* myCStr, int size)
{
HRESULT hrRet = STG_E_INVALIDPARAMETER;
LPSTREAM lpSrc = NULL;
ULONG ul;
LPVOID buffer;
TRY
{
USES_CONVERSION;
HRESULT hrSrc = pSrcStg->GetStg()->OpenStream(CT2COLE(strSrc),
NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE,
0,
&lpSrc);
if (hrSrc != S_OK)
{
hrRet = hrSrc;
}
else
{
// if the stream's character type matches TCHAR...
buffer = myCStr->GetBuffer(size / sizeof(TCHAR));
hrRet = lpSrc->Read(buffer, size, &ul);
myCStr->ReleaseBuffer(ul / sizeof(TCHAR));
// else, if the stream's character type is 'char' and TCHAR is 'wchar_t'...
CStringA tmp;
buffer = tmp.GetBuffer(size);
hrRet = lpSrc->Read(buffer, size, &ul);
tmp.ReleaseBuffer(ul);
*myCStr = CString((LPSTR)tmp, tmp.GetLength());
// else, if the stream's character type is 'wchar_t' and TCHAR is 'char'...
CStringW tmp;
buffer = tmp.GetBuffer(size / sizeof(wchar_t));
hrRet = lpSrc->Read(buffer, size, &ul);
tmp.ReleaseBuffer(ul / sizeof(wchar_t));
*myCStr = CString((LPWSTR)tmp, tmp.GetLength());
// alternatively, you can do the above 2 cases more generically...
typedef CStringT<char or wchar_t> CStreamString;
CStreamString tmp;
buffer = tmp.GetBuffer(size / sizeof(CStreamString::XCHAR));
hrRet = lpSrc->Read(buffer, size, &ul);
tmp.ReleaseBuffer(ul / sizeof(CStreamString::XCHAR));
*myCStr = CString((CStreamString::PXSTR)tmp, tmp.GetLength());
}
}
CATCH_ALL(e)
{
hrRet = STG_E_UNKNOWN;
}
END_CATCH_ALL
_AfxRelease((LPUNKNOWN*)&lpSrc);
return hrRet;
}
I am currently trying to figure out how to properly store a CImage file within a CArchive (JPEG). My current approach to this is the following (pseudo) code:
BOOL CPicture::Serialize(CArchive &ar)
{
IStream *pStream = NULL;
HRESULT hr;
CImage *img = GetImage();
if (ar.IsLoading())
{
HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, 54262);
hr = CreateStreamOnHGlobal(hMem, FALSE, &pStream);
if(SUCCEEDED(hr))
{
ar.Read(pStream, 54262);
img->Load(pStream);
pStream->Release();
GlobalUnlock(hMem);
GlobalFree(hMem);
}
}
else
{
hr = CreateStreamOnHGlobal(0, TRUE, &pStream);
if (SUCCEEDED(hr))
{
hr = img->Save(pStream, Gdiplus::ImageFormatJPEG);
if (SUCCEEDED(hr))
ar.Write(pStream, 54262);
}
}
...
I am just now getting back into C++ and have only done a little with it in the past. Any help would be very much appreciated.
Thank you very much in advance.
I'm not an expert on IStream, but I think you may not be using it correctly. The following code seems to work. It currently archives in png format, but it can easily be changed through Gdiplus::ImageFormatPNG.
It owes a lot to the tutorial "Embracing IStream as just a stream of bytes" by S. Arman on CodeProject.
void ImageArchive(CImage *pImage, CArchive &ar)
{
HRESULT hr;
if (ar.IsStoring())
{
// create a stream
IStream *pStream = SHCreateMemStream(NULL, 0);
ASSERT(pStream != NULL);
if (pStream == NULL)
return;
// write the image to a stream rather than file (the stream in this case is just a chunk of memory automatically allocated by the stream itself)
pImage->Save(pStream, Gdiplus::ImageFormatPNG); // Note: IStream will automatically grow up as necessary.
// find the size of memory written (i.e. the image file size)
STATSTG statsg;
hr = pStream->Stat(&statsg, STATFLAG_NONAME);
ASSERT(hr == S_OK);
ASSERT(statsg.cbSize.QuadPart < ULONG_MAX);
ULONG nImgBytes = ULONG(statsg.cbSize.QuadPart); // any image that can be displayed had better not have more than MAX_ULONG bytes
// go to the start of the stream
LARGE_INTEGER seekPos;
seekPos.QuadPart = 0;
hr = pStream->Seek(seekPos, STREAM_SEEK_SET, NULL);
ASSERT(hr == S_OK);
// get data in stream into a standard byte array
char *bytes = new char[nImgBytes];
ULONG nRead;
hr = pStream->Read(bytes, nImgBytes, &nRead); // read the data from the stream into normal memory. nRead should be equal to statsg.cbSize.QuadPart.
ASSERT(hr == S_OK);
ASSERT(nImgBytes == nRead);
// write data to archive and finish
ar << nRead; // need to save the amount of memory needed for the file, since we will need to read this amount later
ar.Write(bytes, nRead); // write the data to the archive file from the stream memory
pStream->Release();
delete[] bytes;
}
else
{
// get the data from the archive
ULONG nBytes;
ar >> nBytes;
char *bytes = new char[nBytes]; // ordinary memory to hold data from archive file
UINT nBytesRead = ar.Read(bytes, nBytes); // read the data from the archive file
ASSERT(nBytesRead == UINT(nBytes));
// make the stream
IStream *pStream = SHCreateMemStream(NULL, 0);
ASSERT(pStream != NULL);
if (pStream == NULL)
return;
// put the archive data into the stream
ULONG nBytesWritten;
pStream->Write(bytes, nBytes, &nBytesWritten);
ASSERT(nBytes == nBytesWritten);
if (nBytes != nBytesWritten)
return;
// go to the start of the stream
LARGE_INTEGER seekPos;
seekPos.QuadPart = 0;
hr = pStream->Seek(seekPos, STREAM_SEEK_SET, NULL);
ASSERT(hr == S_OK);
// load the stream into CImage and finish
pImage->Load(pStream); // pass the archive data to CImage
pStream->Release();
delete[] bytes;
}
}
I am trying to open an in memory stream for use with the xmllite library. Writing to one works ok, but reading from one is giving me a hard time. Below is the code that I am using. Basically I create a default xml string (LPWSTR) and write it to a memory stream using CreateStreamOnHGlobal. I then seek to the beginning and read it back to make sure its in there (it is). Then I seek back again and assign it to the input of the reader. It never gets past the line:
while (S_OK == (hr = pReader->Read(&nodeType)))
I get an XmlNodeType_None and an HRESULT value of -1072894427. I believe it is having trouble reading the stream, but I dont know for sure. The same code works fine if I use a filestream instead and writing to the xml from the memory stream works as well
HRESULT hr = S_OK; CComPtr<IStream> pStream = NULL;
IXmlReader *pReader = NULL;
XmlNodeType nodeType;
LPWSTR pwszXMLString =
L"<?xml version\"1.0\" encoding=\"UTF-8\" ?>\r\n"
L"<paramlist name=\"LP\">\r\n"
L"<value></value>\r\n"
L"<value></value>\r\n"
L"</paramlist>\r\n"
L"<param name=\"AutoConnect\">false</param>\r\n"
L"<param name=\"ConnectWhenLit\">false</param>\r\n"
L"<param name=\"SessionMaxBytes\">200000</param>\r\n"
L"<param name=\"SessionTimeoutSecs\">300</param>\r\n"
L"<param name=\"PacketDelayMs\">0</param>\r\n"
L"<param name=\"PacketSizeBytes\">4096</param>\r\n"
L"<param name=\"LowSSLSecurity\">true</param>\r\n";
DWORD dwWritten = 0;
hr = CreateStreamOnHGlobal(NULL, FALSE, &pStream);
hr = pStream->Write(pwszXMLString, wcslen(pwszXMLString) * sizeof(WCHAR), &dwWritten);
// print out the contents of the memory stream just to make sure we have it
LARGE_INTEGER pos;
pos.QuadPart = 0;
pStream->Seek(pos, STREAM_SEEK_SET, NULL);
STATSTG ssStreamData = {0};
pStream->Stat(&ssStreamData, STATFLAG_NONAME);
SIZE_T cbSize = ssStreamData.cbSize.LowPart;
LPWSTR pwszContent = (WCHAR*) new BYTE[cbSize + sizeof(WCHAR)];
if (pwszContent == NULL)
return E_OUTOFMEMORY;
pStream->Seek(pos, STREAM_SEEK_SET, NULL);
SIZE_T cbRead;
pStream->Read(pwszContent, cbSize, &cbRead);
pwszContent[cbSize/sizeof(WCHAR)] = L'\0';
CZString czContent;
czContent.LoadWideString(pwszContent, cbSize);
wprintf(L"%S", czContent.GetString().c_str());
pStream->Seek(pos, STREAM_SEEK_SET, NULL);
if (hr == S_OK)
{
typedef HRESULT (WINAPI *CreateXmlReaderFunc)(const IID & riid, void** ppvObject, IMalloc * pMalloc);
CreateXmlReaderFunc _CreateXmlReaderFunc = (CreateXmlReaderFunc)GetProcAddress(m_hXMLLite, "CreateXmlReader");
if (FAILED(hr = _CreateXmlReaderFunc(__uuidof(IXmlReader), (void**) &pReader, NULL)))
{
MessageBox(NULL, CStringHelper::Format(L"Error: GetProcAddress() failed to find 'CreateXmlReader' %d\n", GetLastError()).c_str(), L"Error", MB_OK);
return -1;
}
pReader->SetInput(pStream);
}
while (S_OK == (hr = pReader->Read(&nodeType)))
{
switch (nodeType)
{
// parse xml here
}
}
return 0;
Your xml string is not correct. There must be "=" between "version" and "1.0". Secondly, the string uses UTF-16, while the header states it is UTF-8. Change UTF-8 to UTF-16 in header or remove encoding attribute.
Either
LPWSTR pwszXMLString =
L"<?xml version=\"1.0\" encoding=\"UTF-16\" ?>\r\n"
or
LPWSTR pwszXMLString =
L"<?xml version=\"1.0\"?>\r\n"
works.
I've got an IE BHO plugin that sends out via a COM call the HTML of a page that was loaded in the window.
// Note all error handling removed for readability :)
STDMETHODIMP CPlugin::get_HTML(long lMaxSize, BSTR *pbstrHTML)
{
CComPtr<IDispatch> pDispatch;
MSHTML::IHTMLDocument2Ptr pDocument2 = NULL;
MSHTML::IHTMLDocument3Ptr pDocument3 = NULL;
hr = m_spWebBrowser->get_Document(&pDispatch);
hr = pDispatch->QueryInterface(IID_IHTMLDocument3, (void**)&pDocument3);
MSHTML::IHTMLElementPtr pRoot = pDocument3->documentElement;
wstring strHTML = pRoot->outerHTML;
CComBSTR bstrHTML = strOutput.c_str();
bstrHTML.CopyTo(pbstrHTML);
}
However when it encounters a very large page (e.g. "http://sitemap.zillow.com/uncompressed/ForSale_Hood_MedPri_1.xml"), it takes 3 minutes to create the HTML from the DOM.
Is there a way to access the raw HTML/XML?
When you do a 'view page source' in IE, it pops up almost immediately, so internally IE must be using some API that can do what I want.
Thanks,
Shane.
It seems that in old versions of MSHTML, outerHTML had a O(n^2) performance. However, in newer versions (IE8) this problem is gone. If you have a choice, use IE8 or later.
Otherwise, using IPersistStream::Save is an option. But CreateStreamOnHGlobal won't help you since its implementation is also O(n^2). You'll have to use a custom IStream for that.
Included is an IStream implementation which was made for this purpose and supports quick writes:
#include <atlbase.h>
#include <atlcom.h>
#include <vector>
// an implementation of a write-only IStream.
// needed because the CreateStreamOnHGlobal implementation doesn't handle
// resizes well (N writes seem to take O(N^2) time)
class MyStream :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<MyStream>,
public IStreamImpl
{
public:
std::vector<char> buf;
BEGIN_COM_MAP(MyStream)
COM_INTERFACE_ENTRY(IStream)
END_COM_MAP()
STDMETHOD(Write) (const void * pv, ULONG cb, ULONG *pcbWritten);
};
/*
Usage:
CComPtr<IStream> stream;
hr = MyStream::CreateInstance(&stream);
// streamObj will be valid as long as IStream smart pointer lives
MyStream *streamObj = (MyStream*)stream.p;
*/
STDMETHODIMP MyStream::Write(const void * pv, ULONG cb, ULONG *pcbWritten)
{
buf.insert(buf.end(), (char*)pv, (char*)pv+cb);
return S_OK;
}
Yes, you can QI for IPersistStream and save to a memory stream created by CreateStreamOnHGlobal
Note the document must finished downloading (ready state needs to be complete).
Thanks Amnon, the following code is mostly working for me.
// an implementation of a write-only IStream.
// needed because the CreateStreamOnHGlobal implementation doesn't handle
// resizes well (N writes seem to take O(N^2) time)
class MyStream :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<MyStream>,
public IStream
{
public:
std::vector<char> buf;
BEGIN_COM_MAP(MyStream)
COM_INTERFACE_ENTRY(IStream)
END_COM_MAP()
STDMETHOD(Write) (const void * pv, ULONG cb, ULONG *pcbWritten);
// Implement IStream abstract functions
STDMETHOD(Read) (void *pv, ULONG cb, ULONG *pcbRead) { return S_OK; };
STDMETHOD(Seek) (LARGE_INTEGER dlibMove,DWORD dwOrigin,ULARGE_INTEGER *plibNewPosition) { return S_OK; };
STDMETHOD(SetSize) (ULARGE_INTEGER libNewSize) { return S_OK; };
STDMETHOD(CopyTo) (IStream *pstm,ULARGE_INTEGER cb,ULARGE_INTEGER *pcbRead,ULARGE_INTEGER *pcbWritten) { return S_OK; };
STDMETHOD(Commit) (DWORD grfCommitFlags) { return S_OK; };
STDMETHOD(Revert) () { return S_OK; };
STDMETHOD(LockRegion) (ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType) { return S_OK; };
STDMETHOD(UnlockRegion) (ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType) { return S_OK; };
STDMETHOD(Stat) (__RPC__out STATSTG *pstatstg,DWORD grfStatFlag) { return S_OK; };
STDMETHOD(Clone) (__RPC__deref_out_opt IStream **ppstm) { return S_OK; };
};
STDMETHODIMP MyStream::Write(const void * pv, ULONG cb, ULONG *pcbWritten)
{
buf.insert(buf.end(), (char*)pv, (char*)pv+cb);
return S_OK;
}
// Retrieves the HTML of the current page
STDMETHODIMP CPlugin::get_HTML(long lMaxSize, BSTR *pbstrHTML)
{
HRESULT hr = S_OK;
try
{
CComPtr<IDispatch> pDispatch;
MSHTML::IHTMLDocumentPtr pDocument = NULL;
CComPtr<IStream> mystream;
hr = MyStream::CreateInstance(&mystream);
// streamObj will be valid as long as IStream smart pointer lives
MyStream *streamObj = (MyStream*)mystream.p;
hr = m_spWebBrowser->get_Document(&pDispatch);
hr = pDispatch->QueryInterface(IID_IHTMLDocument, (void**)&pDocument);
IPersistStreamInitPtr persistStream = pDocument;
hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
hr = persistStream->Save(mystream, FALSE);
}
catch(...)
{
TRACE_FN("Got exception somewhere");
}
return hr;
}
Now the only problem left is how to figure why some it returns me single-byte chars most times, and double-byte chars at other times. Any ideas?
Thanks for the help.