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.
Related
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;
}
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.
I tried to create a little project in C++ . My program should be able to read the currently running processes in Windows and send information about the processes to my private MySQL database every 5 minutes. In this amount of time I can read the processes.
See code below:
#include <stdio.h>
#include <iostream>
#include <windows.h>
#include <tlhelp32.h>
bool getAllProcesses(void);
int main(void){
getAllProcesses();
}
bool getAllProcesses(){
HANDLE WINAPI snapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
LPPROCESSENTRY32 pe32;
Process32First(snapshot, pe32);
while(Process32Next(snapshot, pe32)){
std::cout << pe32->szExeFile << "\n";
}
std::cout << "End of list";
CloseHandle( snapshot );
return true;
}
The code above is working fine.
But if I add the code "int i;i=1;" like this:
#include <stdio.h>
#include <iostream>
#include <windows.h>
#include <tlhelp32.h>
bool getAllProcesses(void);
int main(void){
getAllProcesses();
}
bool getAllProcesses(){
int i;
i=0;
HANDLE WINAPI snapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
LPPROCESSENTRY32 pe32;
Process32First(snapshot, pe32);
while(Process32Next(snapshot, pe32)){
std::cout << pe32->szExeFile << "\n";
}
std::cout << "End of list";
CloseHandle( snapshot );
return true;
}
With this change the program will crash, showing the alert "Program stop the work".
I tried to find the problem and I've determined the following :
If I use the function Process32First() OR Process32Next() I can't declare the same int in all of the program.
What is the problem?
You have a lot of issues going on here. I tried this on VS 2008 Win7.
The "i" declaration has nothing to do with the problem, but may just be moving the stack a bit to hide the real problems. See my comments in the corrected code below.
bool getAllProcesses(){
int i;
i=0;
HANDLE WINAPI snapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
/* replace LPPROCESSENTRY32 with PROCESSENTRY32. LP is just a define as
a pointer to a structure, so you did not actually allocate any memory for the
return of Process32First() to return the result into.
So you were probably overwriting stuff in the stack. */
PROCESSENTRY32 pe32;
/* need to initialize the structure properly. Memset may be overkill,
but better to see all zeros than garbage. dwSize must be
initialized as per the SDK documentation. */
memset(&pe32,0,sizeof(pe32) );
pe32.dwSize = sizeof(pe32);
BOOL result;
/* did not check the result from the call if TRUE/FALSE */
result = Process32First(snapshot, &pe32);
std::cout << "result =" << result << "\n";
while(Process32Next(snapshot, &pe32)){
std::cout << pe32.szExeFile << "\n";
}
std::cout << "End of list";
CloseHandle( snapshot );
return true;
}
The addition of those 2 lines making the program not able to run is just a coincidence. The program should not be running at all, since there are 2 major mistakes:
pe32 is an uninitialized pointer
dwSize member of PROCESSENTRY32 structure is not initialized as described in the [MS.Docs]: Process32First function:
The calling application must set the dwSize member of PROCESSENTRY32 to the size, in bytes, of the structure.
And a third, smaller mistake is that you are ignoring the process data returned by Process32First.
In order to make things work correctly, replace the following lines of your code:
LPPROCESSENTRY32 pe32;
Process32First(snapshot, pe32);
while(Process32Next(snapshot, pe32)){
std::cout << pe32->szExeFile << "\n";
, with:
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(snapshot, &pe32))
{
do {
std::cout << pe32.szExeFile << "\n";
}
while (Process32Next(snapshot, &pe32));
}
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;
}
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.