Dynamically allocate buffer[] for URLOpenBlockingStreamW() output - c++

I'm super new to C++ and I'm trying to download an executable file from a URL and write it to disk.
I have the below code which successfully downloads the file and stores it in memory. The issue I am having is then writing that to disk.
I am pretty sure this is down to where I am creating the buffer where the downloaded data will be written to before being going to the new file.
char buffer[4096];
The 4096 is an arbitrary number from the template code I got elsewhere. What I can't figure out or don't know is how to dynamically allocate that buffer size based on the size of the data at &pStream.
I have tried using functions such as sizeof() but these just get me the memory address size rather than the value itself.
Alternatively, is there better way to try and accomplish this download and write?
#include <Windows.h>
#include <Urlmon.h> // URLOpenBlockingStreamW()
#include <atlbase.h> // CComPtr
#include <iostream>
#include "download.h"
#include <fstream>
#include <assert.h>
#include <chrono>
#include <thread>
#pragma comment( lib, "Urlmon.lib" )
struct ComInit
{
HRESULT hr;
ComInit() : hr(::CoInitialize(nullptr)) {}
~ComInit() { if (SUCCEEDED(hr)) ::CoUninitialize(); }
};
int download_file()
{
ComInit init;
HRESULT hr;
// use CComPtr so you don't have to manually call Release()
CComPtr<IStream> pStream;
bool success = false;
while (success == false)
{
try {
// Open the HTTP request.
hr = URLOpenBlockingStreamW(nullptr, L"https://www.foo.bar/download/somefile.exe", &pStream, 0, nullptr);
if (FAILED(hr))
{
std::cout << "ERROR: Could not connect. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
}
else
{
success = true;
}
}
catch (const std::exception& ex) {
std::cout << ex.what();
}
}
// Download the response and write it to stdout.
char buffer[4096]; // Issue is here I think
do
{
DWORD bytesRead = 0;
hr = pStream->Read(buffer, sizeof(buffer), &bytesRead);
if (bytesRead > 0)
{
//std::cout.write(buffer, bytesRead);
std::ofstream file;
file.open("some_path_dot_exe", std::ios_base::binary);
assert(file.is_open());
for (int i = 0; i < sizeof(buffer) / sizeof(buffer[0]); ++i)
file.write((char*)(buffer + i * sizeof(buffer[0])), sizeof(buffer[0]));
file.close();
}
} while (SUCCEEDED(hr) && hr != S_FALSE);
if (FAILED(hr))
{
std::cout << "ERROR: Download failed. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
return 2;
}
std::cout << "\n";
return 0;
}

The IStream that URLOpenBlockingStreamW() gives you isn't guaranteed to be able to give you the full file size up front, so if you want to hold the entire file in memory, you will have to use std::vector or other dynamically-growing buffer.
Though, you don't actually need to hold the entire file in memory just to save it to disk, you can use a fixed array and write it to disk as it is being downloaded, as you already are doing.
The real problem is, you are opening and closing the file on every Read(), wiping out all previous data written. And you are ignoring the bytesRead value that Read() gives you.
You need to open the file one time, leave it open until you are done with the download, and don't write more than is actually in the buffer on each write().
Try this:
#include <Windows.h>
#include <Urlmon.h> // URLOpenBlockingStreamW()
#include <atlbase.h> // CComPtr
#include <iostream>
#include "download.h"
#include <fstream>
#include <assert.h>
#include <chrono>
#include <thread>
#pragma comment( lib, "Urlmon.lib" )
struct ComInit
{
HRESULT hr;
ComInit() : hr(::CoInitialize(nullptr)) {}
~ComInit() { if (SUCCEEDED(hr)) ::CoUninitialize(); }
};
int download_file()
{
ComInit init;
HRESULT hr;
// use CComPtr so you don't have to manually call Release()
CComPtr<IStream> pStream;
do
{
try {
// Open the HTTP request.
hr = URLOpenBlockingStreamW(nullptr, L"https://www.foo.bar/download/somefile.exe", &pStream, 0, nullptr);
if (SUCCEEDED(hr)) break;
std::cout << "ERROR: Could not connect. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
}
catch (const std::exception& ex) {
std::cout << ex.what();
}
}
while (true);
std::ofstream file("some_path_dot_exe", std::ios_base::binary);
if (!file.is_open()) {
std::cout << "ERROR: Download failed. Unable to create output file.\n";
return 1;
}
// Download the response and write it to file.
char buffer[4096];
DWORD bytesRead;
do
{
hr = pStream->Read(buffer, sizeof(buffer), &bytesRead);
if (bytesRead > 0)
file.write(buffer, bytesRead);
} while (SUCCEEDED(hr) && hr != S_FALSE);
file.close();
if (FAILED(hr))
{
std::cout << "ERROR: Download failed. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
return 2;
}
std::cout << "\n";
return 0;
}

Related

Why is my sound recording with WinAPI C++ not played back properly in audacity?

I'm trying to record sound from the microphone but it is getting difficult. I have tried several ways and it doesn't work. I created a project only for testing which is going to be implemented in a bigger project later. Here is the code of the project in question:
#include <iostream>
#include <fstream>
#include <Windows.h>
#include <dshow.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <ks.h>
#include <ksmedia.h>
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mf")
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "strmbase")
int main() {
HRESULT hr = MFStartup(MF_VERSION);
IMFMediaSource* pSoundSource = NULL;
IMFAttributes* pSoundConfig = NULL;
IMFActivate** ppSoundDevices = NULL;
hr = MFCreateAttributes(&pSoundConfig, 1);
if (FAILED(hr)) {
std::cout << "Failed to create attribute store";
}
hr = pSoundConfig->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID);
UINT32 count;
hr = MFEnumDeviceSources(pSoundConfig, &ppSoundDevices, &count);
if (FAILED(hr)) {
std::cout << "Failed to enumerate capture devices";
}
hr = ppSoundDevices[0]->ActivateObject(IID_PPV_ARGS(&pSoundSource));
if (FAILED(hr)) {
std::cout << "Failed to connect microphone to source";
}
IMFSourceReader* pSoundReader;
hr = MFCreateSourceReaderFromMediaSource(pSoundSource, pSoundConfig, &pSoundReader);
if (FAILED(hr)) {
std::cout << "Failed to create source reader";
}
/*This part is for getting the audio format that the microphone outputs*/
/*______________________*/
IMFMediaType* pSoundType = NULL;
DWORD dwMediaTypeIndex = 0;
DWORD dwStreamIndex = 0;
hr = pSoundReader->GetNativeMediaType(dwStreamIndex, dwMediaTypeIndex, &pSoundType);
LPVOID soundRepresentation;
pSoundType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, &soundRepresentation);
GUID subSoundType = ((AM_MEDIA_TYPE*)soundRepresentation)->subtype;
BYTE* pbSoundFormat = ((AM_MEDIA_TYPE*)soundRepresentation)->pbFormat;
GUID soundFormatType = ((AM_MEDIA_TYPE*)soundRepresentation)->formattype;
if (soundFormatType == FORMAT_WaveFormatEx) { std::cout << 8; }
WAVEFORMATEXTENSIBLE* soundFormat = (WAVEFORMATEXTENSIBLE*)pbSoundFormat;
std::cout << std::endl;
std::cout << soundFormat->Format.wFormatTag << std::endl;
std::cout << soundFormat->Format.nChannels << std::endl;
std::cout << soundFormat->Format.nBlockAlign << std::endl;
std::cout << soundFormat->Format.nSamplesPerSec << std::endl;
std::cout << soundFormat->Format.wBitsPerSample << std::endl;
std::cout << soundFormat->Format.cbSize << std::endl;
if (soundFormat->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
std::cout << "IEEE-FLOAT!" << std::endl;
/*_____________________*/
DWORD streamIndex, flags;
LONGLONG llTimeStamp;
IMFSample* pSoundSample;
while (true) {
hr = pSoundReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &streamIndex, &flags, &llTimeStamp, &pSoundSample);
if (FAILED(hr)) {
std::cout << "Failed to get sound from microphone";
}
if (pSoundSample != NULL) {
IMFMediaBuffer* pSoundBuffer;
pSoundSample->ConvertToContiguousBuffer(&pSoundBuffer);
DWORD soundlength;
pSoundBuffer->GetCurrentLength(&soundlength);
unsigned char* sounddata;
hr = pSoundBuffer->Lock(&sounddata, NULL, &soundlength);
if (FAILED(hr)) {
std::cout << "Failed to get sounddata from buffer";
}
std::ofstream file;
file.open("C:\\Users\\user\\Documents\\test.raw", std::ios::app);
for (unsigned int i = 0; i < soundlength; i++)
file << sounddata[i];
file.close();
}
}
}
The part of the code which is supposed to determine the format of the data prints on the console:
8
65534
1
4
48000
32
22
IEEE-FLOAT!
From this, I determined that the sound is recorded in 1 channel 32bits 48000Hz IEEE-FLOAT format. Now I need to playback this sound. The problem is most APIs will take 16bit PCM for sound playback.
I tried converting the sound to 16bits PCM but it doesn't work well. If you are aware of how to do that, can you show some code? Also, in the code presented here, I'm appending the sound to a raw audio file without a header. I heard that the float representation is between 1 and -1 so I tried the following code to do the conversion:
void iefloat_to_pcm16(unsigned char* sounddata, std::vector<unsigned char>& newdata, int soundlength) {
for (int i = 0; i < soundlength && i + 3 < soundlength; i += 4) {
float f;
unsigned char b[] = { sounddata[i], sounddata[i + 1], sounddata[i + 2], sounddata[i + 3] };
memcpy(&f, &b, sizeof(f));
short pcm16 = f * 32767 + 0.5;
newdata.push_back((unsigned char)(pcm16 >> 8));
newdata.push_back((unsigned char)pcm16);
}
}
This code doesn't seem to work.
After this, I've been using Audacity with File > Import > Raw Data which allows to import raw data and specify the format the data is in. So I selected 1 channel 32 bits float, 48kHZ and I tried all endianness to no avail. I did the same with the data "converted" to 16 bits PCM. The result is just random noise in audacity. I can see that there are spikes where I make noise and the rest is silent. But the spikes are just noise. Is there something I'm doing wrong here?
Audio files are binary format, but you're placing text in the file.
file << sounddata[i];
That's a formatted insertion operator, which converts the data to a textual representation. Instead use file.write().
You may also need to mess with the flags you use to open the stream. C++ Standard I/O streams are not made for binary data. Since you are extensively using Windows API objects already, you might just switch to CreateFile / WriteFile where no conversion facets are active beneath the surface.

UrlDownloadToFile to Memory

I'm using URLDownloadToFile() to download images from a web server to a directory on my desktop. If, instead of saving the images to disk, I wanted to read them into memory (like a byte array or base64 string or something), is there a function similar to URLDownloadToFile() that can achieve this?
There is URLOpenStream(), URLOpenBlockingStream() and URLOpenPullStream() which allow you to download into memory.
From these three, URLOpenBlockingStream() seems to be the most straightforward to use as it returns an IStream pointer from which you can synchronously read in a loop. Although it's not a single do-all function like URLDownloadToFile() it's not much difficult to use.
Here is a complete example console application for URLOpenBlockingStream(). It downloads from an URL and writes the response to stdout. Instead of this, you could store the response in a std::vector or do whatever you like with it.
#include <Windows.h>
#include <Urlmon.h> // URLOpenBlockingStreamW()
#include <atlbase.h> // CComPtr
#include <iostream>
#pragma comment( lib, "Urlmon.lib" )
struct ComInit
{
HRESULT hr;
ComInit() : hr( ::CoInitialize( nullptr ) ) {}
~ComInit() { if( SUCCEEDED( hr ) ) ::CoUninitialize(); }
};
int main(int argc, char* argv[])
{
ComInit init;
// use CComPtr so you don't have to manually call Release()
CComPtr<IStream> pStream;
// Open the HTTP request.
HRESULT hr = URLOpenBlockingStreamW( nullptr, L"http://httpbin.org/headers", &pStream, 0, nullptr );
if( FAILED( hr ) )
{
std::cout << "ERROR: Could not connect. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
return 1;
}
// Download the response and write it to stdout.
char buffer[ 4096 ];
do
{
DWORD bytesRead = 0;
hr = pStream->Read( buffer, sizeof(buffer), &bytesRead );
if( bytesRead > 0 )
{
std::cout.write( buffer, bytesRead );
}
}
while( SUCCEEDED( hr ) && hr != S_FALSE );
if( FAILED( hr ) )
{
std::cout << "ERROR: Download failed. HRESULT: 0x" << std::hex << hr << std::dec << "\n";
return 2;
}
std::cout << "\n";
return 0;
}
There is no single do-all function in the Win32 API to download a URL resource into memory, like URLDownloadToFile() does for a file. You will have to write your own function that requests the URL and then reads the data into memory.
For example, using InternetOpenUrl() or HttpOpenRequest()/HttpSendRequest(), and then InternetReadFile(). See Downloading Resources from the WWW on MSDN for more details.
Or using any other HTTP/S library/API of your choosing.

How to use IFileOperation CopyItems for a destination ZIP file

I am using IFileOperation interface's CopyItems method to copy one more files from source folder to a destination folder. This works without any error as long as the destination folder is a standard file system directory. However, my main motivation of using IFileOperation instead of SHFileOperation() method is to allow copying items from / into non-file-system objects as the documentation says (https://msdn.microsoft.com/en-us/library/windows/desktop/bb775771%28v=vs.85%29.aspx).
The following code works perfectly for file-system destination e.g. "g:\my folder" but does not work if the destination is a ZIP file e.g. "g:\Super Cars.zip". For this quick sample please overlook the resource leaks :).
Please note the ZIP file is an existing ZIP file created using standard built-in ZIP capability of Windows and it does not contain the file is am trying to copy.
#include "stdafx.h"
#include <Windows.h>
#include <shellapi.h>
#include <Shlobj.h>
#include <iostream>
#include <atlbase.h>
void CopyFile(LPCTSTR strSource, LPCTSTR strDestination)
{
PIDLIST_ABSOLUTE abSourcePidl = nullptr;
SFGAOF attrs;
HRESULT hr = SHParseDisplayName(strSource, nullptr, &abSourcePidl, 0, &attrs);
if (FAILED(hr))
{
std::wcout << _T("SHParseDisplayName failed for ") << strSource << std::endl;
return;
}
PIDLIST_ABSOLUTE abDestPidl = nullptr;
hr = SHParseDisplayName(strDestination, nullptr, &abDestPidl, 0, &attrs);
if (FAILED(hr))
{
std::wcout << _T("SHParseDisplayName failed for ") << strDestination << std::endl;
return;
}
IShellItem2* pDestShellItem;
hr = SHCreateItemFromIDList(abDestPidl, IID_IShellItem2, reinterpret_cast<LPVOID*>(&pDestShellItem));
if (FAILED(hr))
{
std::wcout << _T("SHCreateItemFromIDList failed") << std::endl;
return;
}
PIDLIST_ABSOLUTE* pPidlRawArray = new PIDLIST_ABSOLUTE[1];
pPidlRawArray[0] = abSourcePidl;
IShellItemArray* pShellItemArr = nullptr;
hr = SHCreateShellItemArrayFromIDLists(1, (LPCITEMIDLIST*)pPidlRawArray, &pShellItemArr);
if (FAILED(hr))
{
std::wcout << _T("SHCreateShellItemArrayFromIDLists failed") << std::endl;
return;
}
CComPtr<IFileOperation> fileOp;
hr = fileOp.CoCreateInstance(CLSID_FileOperation);
if (FAILED(hr))
{
std::wcout << _T("Creation of IFileOperation failed") << std::endl;
return;
}
hr = fileOp->CopyItems(pShellItemArr, pDestShellItem);
if (FAILED(hr))
{
std::wcout << _T("CopyItems fall failed") << std::endl;
return;
}
hr = fileOp->PerformOperations();
if (FAILED(hr))
{
std::wcout << _T("PerformOperations call failed") << std::endl;
return;
}
std::wcout << _T("File copied successfully") << std::endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
CopyFile(_T("D:\\TEMP\\COMTIME.TXT"), _T("G:\\My Folder")); // WORKS!!
CopyFile(_T("D:\\TEMP\\COMTIME.TXT"), _T("G:\\Super Cars.zip")); // DOES NOT WORK!!
CoUninitialize();
return 0;
}

getting external media type

i would like to find a way detecting the type of the media in my optical drive (e.g. DVD+R, DVD-R, DVD-RW, CD+R, etc.) using a simple function in C++ on windows.
The function should not require Admin privilege.
EDIT
I implemented the following code:
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <imapi2.h>
#include <imapi2fs.h>
#include <imapi2error.h>
#include <imapi2fserror.h>
int main(int argc, char *argv[])
{
IDiscFormat2Data* discFormatData = NULL;
HRESULT hr;
CoInitialize ( NULL );
hr = CoCreateInstance( __uuidof(MsftDiscFormat2Data),
NULL,
CLSCTX_ALL,
__uuidof(IDiscFormat2Data),
(void**)&discFormatData);
if ( SUCCEEDED(hr) )
{
IMAPI_MEDIA_PHYSICAL_TYPE mediaType = IMAPI_MEDIA_TYPE_UNKNOWN;
hr = discFormatData->get_CurrentPhysicalMediaType(&mediaType);
if ( SUCCEEDED(hr) )
{
std::cout << "MediaPhysicalType: " << mediaType << std::endl;
}
else
{
std::stringstream str;
str << "get_CurrentPhysicalMediaType() failed with the error: 0x";
str << std::hex << hr << ".";
std::cout << str.str() << std::endl;
}
// Release the interface.
// Tell the COM object that we're done with it.
discFormatData->Release();
}
else
{
std::stringstream str;
str << "CoCreateInstance() failed with the error: 0x" << std::hex << hr;
std::cout << str.str() << std::endl;
}
cin.get();
return 0;
}
at the moment my problem is that i get the following error: E_IMAPI_RECORDER_REQUIRED which means
"The request requires a current disc recorder to be selected."
Assuming i have at least two optical drivers, how can i differ between them?
Any ideas?
On Windows 2000 and later, you can use IOCTL_CDROM_GET_CONFIGURATION with the SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT flag to query an optical device for its current profile, which will tell you which type of disc (CD, DVD+-R/W, HDDVD, BluRay, etc) has been inserted, if any. On earlier versions, you will have to manually send SCSI MMC commands directly to the device to query the same info.

How to Enumerate Names of All Named Pipes in a Process?

I need to open a certain named pipe so I can fuzz test it, however my test code does not have access to the same data used to generate the name of the named pipe. However I can recognize the name of the pipe and then use that name to open up the pipe for fuzzing.
I used this forum post to start enumerating names of the handles on the system:
http://forum.sysinternals.com/howto-enumerate-handles_topic18892.html
However it seems that won't work with named pipes for some reason.
TL;DR: What API(s) do I need to use to list the names of all named pipes in the current process on Windows?
This will enumerate all named pipes in the system, or at the very least put you a step in the right direction.
This works in MinGW when built with -fpermissive. It should work with similar settings in MSVC.
#ifndef _WIN32_WINNT
// Windows XP
#define _WIN32_WINNT 0x0501
#endif
#include <Windows.h>
#include <Psapi.h>
// mycreatepipeex.c is at http://www.davehart.net/remote/PipeEx.c
// I created a simple header based on that.
#include "mycreatepipeex.h"
#include <iostream>
#include <cstdio>
#include <errno.h>
void EnumeratePipes()
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
#define TARGET_PREFIX "//./pipe/"
const char *target = TARGET_PREFIX "*";
memset(&FindFileData, 0, sizeof(FindFileData));
hFind = FindFirstFileA(target, &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
std::cerr << "FindFirstFileA() failed: " << GetLastError() << std::endl;
return;
}
else
{
do
{
std::cout << "Pipe: " << TARGET_PREFIX << FindFileData.cFileName << std::endl;
}
while (FindNextFile(hFind, &FindFileData));
FindClose(hFind);
}
#undef TARGET_PREFIX
return;
}
int main(int argc, char**argv)
{
HANDLE read = INVALID_HANDLE_VALUE;
HANDLE write = INVALID_HANDLE_VALUE;
unsigned char pipe_name[MAX_PATH+1];
BOOL success = MyCreatePipeEx(&read, &write, NULL, 0, 0, 0, pipe_name);
EnumeratePipes();
if ( success == FALSE )
{
std::cerr << "MyCreatePipeEx() failed: " << GetLastError() << std::endl;
return 1;
}
FILE *f = fopen((const char*)pipe_name, "rwb");
if ( f == NULL )
{
std::cerr << "fopen(\"" << pipe_name << "\") failed: " << (int)errno << std::endl;
}
CloseHandle(read);
CloseHandle(write);
return 0;
}