Unable to read xml string with xmllite using memory buffer - c++

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.

Related

Is there a Win32 function to get the architecture of a DLL?

There is GetBinaryType() for determining if an .exe file is 32-bit or 64-bit, but how can I do this for a .dll file? I want to ensure that a DLL is the right architecture before trying to load it with LoadLibrary().
if you want use dll for call functions or load resource from it - just try load it. if it was wrong architecture - you got error ERROR_BAD_EXE_FORMAT and dll wil be not loaded. check before this nothing more give. the try load is check already.
if you need check for some other reasons, exist several ways. most correct is next - open file, create image section from it and check section (last operation is undocumented)
HRESULT CheckImage( _In_ PCWSTR lpLibFileName, _Out_ PUSHORT Machine)
{
HANDLE hFile = CreateFileW(lpLibFileName, FILE_EXECUTE|FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
HANDLE hSection = CreateFileMappingW(hFile, 0, PAGE_EXECUTE_READ|SEC_IMAGE, 0, 0, 0);
NtClose(hFile);
if (hSection)
{
SECTION_IMAGE_INFORMATION sii;
NTSTATUS status = ZwQuerySection(hSection, SectionImageInformation, &sii, sizeof(sii), 0);
NtClose(hSection);
*Machine = sii.Machine;
return status ? HRESULT_FROM_NT(status) : S_OK;
}
}
return HRESULT_FROM_WIN32(GetLastError());
}
you got or some error from this func (file not found, bad image, etc) or some IMAGE_FILE_MACHINE_*. usually IMAGE_FILE_MACHINE_AMD64 or IMAGE_FILE_MACHINE_I386
another way - use LoadLibraryExW with LOAD_LIBRARY_AS_DATAFILE and check IMAGE_NT_HEADERS of mapped image - really this way do all what first (including ZwQuerySection call internally) + mapped dll to memory - last is not need. so this is less efficient way.
HRESULT CheckImage2( _In_ PCWSTR lpLibFileName, _Out_ PUSHORT Machine)
{
if (HMODULE hmod = LoadLibraryExW(lpLibFileName, 0, LOAD_LIBRARY_AS_DATAFILE))
{
HRESULT hr = S_OK;
if (PIMAGE_NT_HEADERS pinth = RtlImageNtHeader(PAGE_ALIGN(hmod)))
{
*Machine = pinth->FileHeader.Machine;
}
else
{
hr = HRESULT_FROM_NT(STATUS_INVALID_IMAGE_NOT_MZ);
}
FreeLibrary(hmod);
return hr;
}
return HRESULT_FROM_WIN32(GetLastError());
}
else one way - direct read file and check it headers. this from one side is fastest, from another side - even if headers is correct - no guarantee that whole file is ok and not corrupted
HRESULT CheckImage3( _In_ PCWSTR lpLibFileName, _Out_ PUSHORT Machine, _Out_ PBOOL Is64Bit)
{
HANDLE hFile = CreateFileW(lpLibFileName, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
HRESULT hr = HRESULT_FROM_NT(STATUS_INVALID_IMAGE_NOT_MZ);
union {
IMAGE_DOS_HEADER idh;
IMAGE_NT_HEADERS inth;
};
OVERLAPPED ov {};
ULONG dwBytesRead;
if (ReadFile(hFile, &idh, sizeof(idh), &dwBytesRead, &ov))
{
if (dwBytesRead == sizeof(idh) && idh.e_magic == IMAGE_DOS_SIGNATURE)
{
hr = HRESULT_FROM_NT(STATUS_INVALID_IMAGE_FORMAT);
ov.Offset = idh.e_lfanew;
if (ReadFile(hFile, &inth, sizeof(inth), &dwBytesRead, &ov))
{
if (dwBytesRead == sizeof(inth) && inth.Signature == IMAGE_NT_SIGNATURE)
{
switch (inth.OptionalHeader.Magic)
{
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
*Is64Bit = FALSE;
hr = S_OK;
break;
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
*Is64Bit = TRUE;
hr = S_OK;
break;
}
*Machine = inth.FileHeader.Machine;
}
}
}
}
CloseHandle(hFile);
return hr;
}
return HRESULT_FROM_WIN32(GetLastError());
}

Can you register a directshow filter without admin?

I would like to register a virtual directshow source device without needing admin. On another post I saw someone reference that you can register COM classes per user account via HKEY_CURRENT_USER instead of HKEY_CLASSES_ROOT or HKEY_LOCAL_MACHINE. This worked, and I could register the class through HKEY_CURRENT_USER\Software\Classes without UAC.
To get the source/filter to appear I need to make a call to IFilterMapper2::RegisterFilter. This fails without UAC privledge. (E_ACCESSDENIED General access denied error.).
Microsoft isn't exactly clear on what the call to RegisterFilter actually does. I know it creates a registry entry under HKEY_CLASSES_ROOT\CLSID\category clsid\Instance\filter clsid but one of the key values is FilterData which is a binary value that should theoretically match https://learn.microsoft.com/en-us/windows/win32/api/strmif/ns-strmif-regfilter2 this struct; but the data doesn't totally line up so they must write other data in there too.
Is there anyway to register the filter on the user account level?
Registration information is under HKEY_LOCAL_MACHINE so you have to have local admin account and elevated privileges for the registration to happen.
First you have to register the server:
STDAPI AMovieSetupRegisterServerOverride(CLSID clsServer, LPCWSTR szDescription, LPCWSTR szFileName, LPCWSTR szThreadingModel = L"Both", LPCWSTR szServerType = L"InprocServer32")
{
TCHAR achTemp[MAX_PATH];
OLECHAR szCLSID[CHARS_IN_GUID];
HRESULT hr = StringFromGUID2(clsServer, szCLSID, CHARS_IN_GUID);
HKEY hkey;
DWORD d;
wsprintf(achTemp, TEXT("Software\\Classes\\CLSID\\%ls"), szCLSID);
LONG lreturn = RegCreateKeyEx(HKEY_CURRENT_USER, (LPCTSTR)achTemp, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, &d);
if (ERROR_SUCCESS != lreturn)
{
return AmHresultFromWin32(lreturn);
}
wsprintf(achTemp, TEXT("%ls"), szDescription);
lreturn = RegSetValue(hkey, (LPCTSTR)NULL, REG_SZ, achTemp, sizeof(achTemp));
if (ERROR_SUCCESS != lreturn)
{
RegCloseKey(hkey);
return AmHresultFromWin32(lreturn);
}
HKEY hsubkey;
wsprintf(achTemp, TEXT("%ls"), szServerType);
lreturn = RegCreateKeyEx(hkey, achTemp, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hsubkey, &d);
if (ERROR_SUCCESS != lreturn)
{
RegCloseKey(hkey);
return AmHresultFromWin32(lreturn);
}
wsprintf(achTemp, TEXT("%ls"), szFileName);
lreturn = RegSetValue(hsubkey, (LPCTSTR)NULL, REG_SZ, (LPCTSTR)achTemp, sizeof(TCHAR) * (lstrlen(achTemp) + 1));
if (ERROR_SUCCESS != lreturn)
{
RegCloseKey(hkey);
RegCloseKey(hsubkey);
return AmHresultFromWin32(lreturn);
}
wsprintf(achTemp, TEXT("%ls"), szThreadingModel);
lreturn = RegSetValueEx(hsubkey, TEXT("ThreadingModel"), 0L, REG_SZ, (CONST BYTE*)achTemp, sizeof(TCHAR) * (lstrlen(achTemp) + 1));
RegCloseKey(hkey);
RegCloseKey(hsubkey);
ASSERT(SUCCEEDED(hr));
return HRESULT_FROM_WIN32(lreturn);
}
STDAPI
AMovieSetupUnregisterServerOverride(CLSID clsServer)
{
OLECHAR szCLSID[CHARS_IN_GUID];
HRESULT hr = StringFromGUID2(clsServer, szCLSID, CHARS_IN_GUID);
TCHAR achBuffer[260];
wsprintf(achBuffer, TEXT("CLSID\\%ls"), szCLSID);
hr = EliminateSubKey(HKEY_CLASSES_ROOT, achBuffer);
return NOERROR;
}
This is copied from the winsdk method, but modified to use the Software\Classes registry instead to avoid UAC:
AMovieSetupRegisterServer
Found Here
Then you can register the filter using the following code (note, I didn't finish figuring out exactly how to serialize the filter data so I registered it with UAC, checked what the binary data was for that, and manually fill it out that way. If you want to serialize the filter data yourself you can reverse how they do it in GraphStudioNext (method FilterTemplate::LoadFilterData)
// {ae7416c6-637d-4b8e-970d-24af9a1dd02a}
DEFINE_GUID(CLSID_EXAMPLE_GUID,
0xae7416c6, 0x637d, 0x4b8e, 0x97, 0x0d, 0x24, 0xaf, 0x9a, 0x1d, 0xd0, 0x2a);
HRESULT RegisterCurrentUser(REGFILTER2 rf)
{
TCHAR achTemp[MAX_PATH];
OLECHAR szCLSID[CHARS_IN_GUID];
HRESULT hr = StringFromGUID2(CLSID_EXAMPLE_GUID, szCLSID, CHARS_IN_GUID);
HKEY hkey;
wsprintf(achTemp, TEXT("Software\\Classes\\CLSID\\{860BB310-5D01-11d0-BD3B-00A0C911CE86}\\Instance\\%ls"), szCLSID);
hr = RegCreateKey(HKEY_CURRENT_USER, (LPCTSTR)achTemp, &hkey);
wsprintf(achTemp, TEXT("%ls"), szCLSID);
hr = RegSetValueEx(hkey, TEXT("CLSID"), 0L, REG_SZ, (CONST BYTE*) achTemp, sizeof(TCHAR) * (lstrlen(achTemp) + 1));
DWORD data[20];
data[0] = 0x00000002; //Version
data[1] = 0x00200000; //Merit
data[2] = 0x00000001; //if v===1, pin#
data[3] = 0x00000000; //if v==2, pin #
data[4] = 0x33697030;
data[5] = 0x08000000;
data[6] = 0x00000000;
data[7] = 0x00000001;
data[8] = 0x00000000;
data[9] = 0x00000000;
data[10] = 0x33797430;
data[11] = 0x00000000;
data[12] = 0x00000038;
data[13] = 0x00000048;
data[14] = 0x73646976;
data[15] = 0x00100000;
data[16] = 0xAA000080;
data[17] = 0x719B3800;
data[18] = 0x00000000;
data[19] = 0x00000000;
hr = RegSetValueEx(hkey, TEXT("FilterData"), 0L, REG_BINARY, (BYTE*)data, sizeof(data));
wsprintf(achTemp, TEXT("%ls"), L"Example Filter");
hr = RegSetValueEx(hkey, TEXT("FriendlyName"), 0L, REG_SZ, (CONST BYTE*) achTemp, sizeof(TCHAR) * (lstrlen(achTemp) + 1));
return hr;
}

How to convert IStream to Base64 string in c++

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

How to use IAudioClient3 (WASAPI) with Real-Time Work Queue API

I'm working on a lowest-possible latency MIDI synthetizer software. I'm aware of ASIO and other alternatives, but as they have apparently made significant improvements to the WASAPI stack (in shared mode, at least), I'm curious to try it out. I first wrote a simple event-driven version of program, but as that's not the recommended way to do low-latency audio on Windows 10 (according to the docs), I'm trying to migrate to the Real-Time Work Queue API.
The documentation on Low Latency Audio states that it is recommended to use the Real-Time Work Queue API or MFCreateMFByteStreamOnStreamEx with WASAPI in order for the OS to manage work items in a way that will avoid interference from non-audio subsystems. This seems like a good idea, but the latter option seems to require some managed code (demonstrated in this WindowsAudioSession example), which I know nothing about and would preferably avoid (also the header Robytestream.h which has defs for the IRandomAccessStream isn't found on my system either).
The RTWQ example included in the docs is incomplete (doesn't compile as such), and I have made the necessary additions to make it compilable:
class my_rtqueue : IRtwqAsyncCallback {
public:
IRtwqAsyncResult* pAsyncResult;
RTWQWORKITEM_KEY workItemKey;
DWORD WorkQueueId;
STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue)
{
HRESULT hr = S_OK;
*pdwFlags = 0;
*pdwQueue = WorkQueueId;
return hr;
}
//-------------------------------------------------------
STDMETHODIMP Invoke(IRtwqAsyncResult* pAsyncResult)
{
HRESULT hr = S_OK;
IUnknown* pState = NULL;
WCHAR className[20];
DWORD bufferLength = 20;
DWORD taskID = 0;
LONG priority = 0;
BYTE* pData;
hr = render_info.renderclient->GetBuffer(render_info.buffer_framecount, &pData);
ERROR_EXIT(hr);
update_buffer((unsigned short*)pData, render_info.framesize_bytes / (2*sizeof(unsigned short))); // 2 channels, sizeof(unsigned short) == 2
hr = render_info.renderclient->ReleaseBuffer(render_info.buffer_framecount, 0);
ERROR_EXIT(hr);
return S_OK;
}
STDMETHODIMP QueryInterface(const IID &riid, void **ppvObject) {
return 0;
}
ULONG AddRef() {
return 0;
}
ULONG Release() {
return 0;
}
HRESULT queue(HANDLE event) {
HRESULT hr;
hr = RtwqPutWaitingWorkItem(event, 1, this->pAsyncResult, &this->workItemKey);
return hr;
}
my_rtqueue() : workItemKey(0) {
HRESULT hr = S_OK;
IRtwqAsyncCallback* callback = NULL;
DWORD taskId = 0;
WorkQueueId = RTWQ_MULTITHREADED_WORKQUEUE;
//WorkQueueId = RTWQ_STANDARD_WORKQUEUE;
hr = RtwqLockSharedWorkQueue(L"Pro Audio", 0, &taskId, &WorkQueueId);
ERROR_THROW(hr);
hr = RtwqCreateAsyncResult(NULL, reinterpret_cast<IRtwqAsyncCallback*>(this), NULL, &pAsyncResult);
ERROR_THROW(hr);
}
int stop() {
HRESULT hr;
if (pAsyncResult)
pAsyncResult->Release();
if (0xFFFFFFFF != this->WorkQueueId) {
hr = RtwqUnlockWorkQueue(this->WorkQueueId);
if (FAILED(hr)) {
printf("Failed with RtwqUnlockWorkQueue 0x%x\n", hr);
return 0;
}
}
return 1;
}
};
And so, the actual WASAPI code (HRESULT error checking is omitted for clarity):
void thread_main(LPVOID param) {
HRESULT hr;
REFERENCE_TIME hnsRequestedDuration = 0;
IMMDeviceEnumerator* pEnumerator = NULL;
IMMDevice* pDevice = NULL;
IAudioClient3* pAudioClient = NULL;
IAudioRenderClient* pRenderClient = NULL;
WAVEFORMATEX* pwfx = NULL;
HANDLE hEvent = NULL;
HANDLE hTask = NULL;
UINT32 bufferFrameCount;
BYTE* pData;
DWORD flags = 0;
hr = RtwqStartup();
// also, hr is checked for errors every step of the way
hr = CoInitialize(NULL);
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
hr = pEnumerator->GetDefaultAudioEndpoint(
eRender, eConsole, &pDevice);
hr = pDevice->Activate(
IID_IAudioClient, CLSCTX_ALL,
NULL, (void**)&pAudioClient);
WAVEFORMATEX wave_format = {};
wave_format.wFormatTag = WAVE_FORMAT_PCM;
wave_format.nChannels = 2;
wave_format.nSamplesPerSec = 48000;
wave_format.nAvgBytesPerSec = 48000 * 2 * 16 / 8;
wave_format.nBlockAlign = 2 * 16 / 8;
wave_format.wBitsPerSample = 16;
UINT32 DP, FP, MINP, MAXP;
hr = pAudioClient->GetSharedModeEnginePeriod(&wave_format, &DP, &FP, &MINP, &MAXP);
printf("DefaultPeriod: %u, Fundamental period: %u, min_period: %u, max_period: %u\n", DP, FP, MINP, MAXP);
hr = pAudioClient->InitializeSharedAudioStream(AUDCLNT_STREAMFLAGS_EVENTCALLBACK, MINP, &wave_format, 0);
my_rtqueue* workqueue = NULL;
try {
workqueue = new my_rtqueue();
}
catch (...) {
hr = E_ABORT;
ERROR_EXIT(hr);
}
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
PWAVEFORMATEX wf = &wave_format;
UINT32 current_period;
pAudioClient->GetCurrentSharedModeEnginePeriod(&wf, &current_period);
INT32 FrameSize_bytes = bufferFrameCount * wave_format.nChannels * wave_format.wBitsPerSample / 8;
printf("bufferFrameCount: %u, FrameSize_bytes: %d, current_period: %u\n", bufferFrameCount, FrameSize_bytes, current_period);
hr = pAudioClient->GetService(
IID_IAudioRenderClient,
(void**)&pRenderClient);
render_info.framesize_bytes = FrameSize_bytes;
render_info.buffer_framecount = bufferFrameCount;
render_info.renderclient = pRenderClient;
hEvent = CreateEvent(nullptr, false, false, nullptr);
if (hEvent == INVALID_HANDLE_VALUE) { ERROR_EXIT(0); }
hr = pAudioClient->SetEventHandle(hEvent);
const size_t num_samples = FrameSize_bytes / sizeof(unsigned short);
DWORD taskIndex = 0;
hTask = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex);
if (hTask == NULL) {
hr = E_FAIL;
}
hr = pAudioClient->Start(); // Start playing.
running = 1;
while (running) {
workqueue->queue(hEvent);
}
workqueue->stop();
hr = RtwqShutdown();
delete workqueue;
running = 0;
return 1;
}
This seems to kind of work (ie. audio is being output), but on every other invocation of my_rtqueue::Invoke(), IAudioRenderClient::GetBuffer() returns a HRESULT of 0x88890006 (-> AUDCLNT_E_BUFFER_TOO_LARGE), and the actual audio output is certainly not what I intend it to be.
What issues are there with my code? Is this the right way to use RTWQ with WASAPI?
Turns out there were a number of issues with my code, none of which had really anything to do with Rtwq. The biggest issue was me assuming that the shared mode audio stream was using 16-bit integer samples, when in reality my audio was setup for 32-bit float format (WAVE_FORMAT_IEEE_FLOAT). The currently active shared mode format, period etc. should be fetched like this:
WAVEFORMATEX *wavefmt = NULL;
UINT32 current_period = 0;
hr = pAudioClient->GetCurrentSharedModeEnginePeriod((WAVEFORMATEX**)&wavefmt, &current_period);
wavefmt now contains the output format info of the current shared mode. If the wFormatTag field is equal to WAVE_FORMAT_EXTENSIBLE, one needs to cast WAVEFORMATEX to WAVEFORMATEXTENSIBLE to see what the actual format is. After this, one needs to fetch the minimum period supported by the current setup, like so:
UINT32 DP, FP, MINP, MAXP;
hr = pAudioClient->GetSharedModeEnginePeriod(wavefmt, &DP, &FP, &MINP, &MAXP);
and then initialize the audio stream with the new InitializeSharedAudioStream function:
hr = pAudioClient->InitializeSharedAudioStream(AUDCLNT_STREAMFLAGS_EVENTCALLBACK, MINP, wavefmt, NULL);
... get the buffer's actual size:
hr = pAudioClient->GetBufferSize(&render_info.buffer_framecount);
and use GetCurrentPadding in the Get/ReleaseBuffer logic:
UINT32 pad = 0;
hr = render_info.audioclient->GetCurrentPadding(&pad);
int actual_size = (render_info.buffer_framecount - pad);
hr = render_info.renderclient->GetBuffer(actual_size, &pData);
if (SUCCEEDED(hr)) {
update_buffer((float*)pData, actual_size);
hr = render_info.renderclient->ReleaseBuffer(actual_size, 0);
ERROR_EXIT(hr);
}
The documentation for IAudioClient::Initialize states the following about shared mode streams (I assume it also applies to the new IAudioClient3):
Each time the thread awakens, it should call IAudioClient::GetCurrentPadding to determine how much data to write to a rendering buffer or read from a capture buffer. In contrast to the two buffers that the Initialize method allocates for an exclusive-mode stream that uses event-driven buffering, a shared-mode stream requires a single buffer.
Using GetCurrentPadding solves the problem with AUDCLNT_E_BUFFER_TOO_LARGE, and feeding the buffer with 32-bit float samples instead of 16-bit integers makes the output sound fine on my system (although the effect was quite funky!).
If someone comes up with better/more correct ways to use the Rtwq API, I would love to hear them.

Serialize: CArchive a CImage

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