stdio redirect "fails" when calling waveInOpen, why? - winapi

Here's my basic program, it should compile fairly easily with VisualStudio (even express).
// ConsoleApplication1.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <mmsystem.h>
#include <stdint.h>
#pragma comment(lib, "winmm.lib")
HWAVEIN hWaveIn;
WAVEFORMATEX WaveFormat;
WAVEHDR WaveHeader;
typedef union
{
uint32_t u32;
struct
{
int16_t iLeft;
int16_t iRight;
};
} audiosample16_t;
#define AUDIORATE (44100*4)
#define SECONDS (13)
audiosample16_t MyBuffer[AUDIORATE*SECONDS];
int _tmain(int argc, _TCHAR* argv[])
{
std::cout << "Hello World!\n";
UINT WaveId = 0;
WaveFormat.wFormatTag = WAVE_FORMAT_PCM; // simple, uncompressed format
WaveFormat.nChannels = 2; // 1=mono, 2=stereo
WaveFormat.nSamplesPerSec = 44100;
WaveFormat.wBitsPerSample = 16; // 16 for high quality, 8 for telephone-grade
WaveFormat.nBlockAlign = WaveFormat.nChannels*WaveFormat.wBitsPerSample/8;
WaveFormat.nAvgBytesPerSec = (WaveFormat.nSamplesPerSec)*(WaveFormat.nChannels)*(WaveFormat.wBitsPerSample)/8;
WaveFormat.cbSize=0;
WaveHeader.lpData = (LPSTR)MyBuffer;
WaveHeader.dwBufferLength = sizeof(MyBuffer);
WaveHeader.dwFlags = 0;
std::cout << "Hello World!\n";
//std::cout << std::flush;
HRESULT hr;
if(argc>1)
hr= waveInOpen(&hWaveIn,WaveId,&WaveFormat,0,0,CALLBACK_NULL);
std::cout << "Hello World!\n";
std::cout << "Hello World!\n";
//std::cout << std::flush;
return 0;
}
If you call it from the command line with no arguments, everything prints out fine(several 'Hello World!'s). If you redirect this to a file (myprog.exe > blah.txt) , again, everything works fine and several lines of 'Hello World!' end up in the file as expected.
HOWEVER, if you have an argument (so that waveInOpen is called), it will not redirect anything to the file. The file is empty. If you don't redirect the output, it'll print out to the command prompt just fine.
UNLESS you uncomment the std::flush lines, then the file isn't empty and everything works fine.
What the heck is going on under the hood that's causing that? Shouldn't stdout be flushed on exit and piped to the file no matter what? What is the waveInOpen() call doing that screws up the stdio buffering like that?
FWIW, this came to light because we're calling this program from TCL and Python to do audio quality measurements on an attached product and nothing was being read back, even though it would print out fine when run from the command line (and not redirected).

Related

How to test an instance counter by asynchronous run of a boost childprocess?

I have tried to use boost::childprocess with an async_pipe as shown in the code example below, while expecting since there is a wait method, that the call to run would not wait for the called executable to finish before continuing to the line where I call wait(). My aim is namely to start the same executable multiple times in order to test in GTest an instance counting method (implemented based on boost managed shared memory segment).
But here fore I need the call to io_service::run(), to not wait for the called executable to finish as it does right now. Can someone tell me where I am using it wrong please? Or if this is the wrong way to unit test my function? I have been trying to find the solution for quite some time!
Here is a sample of how I call one instance of the executable:
int CallChildProcess_Style9() {
std::string strCmdLine = "E:\\file.exe --Debug MainStartUps_Off --Lock 3";
boost::asio::io_service m_oIOS;
std::vector<char> m_oAsyncBuffer_Out;
bp::async_pipe m_oAsyncPipe_Out(m_oIOS);
std::error_code build_ec;
size_t nReadSize(0);
boost::scoped_ptr<boost::process::child> m_pChildProcess(nullptr);
m_pChildProcess.reset(new bp::child(strCmdLine.data(), bp::std_out > m_oAsyncPipe_Out, build_ec));
m_oAsyncBuffer_Out.resize(1024*8);
boost::asio::async_read(m_oAsyncPipe_Out, boost::asio::buffer(m_oAsyncBuffer_Out),
[&](const boost::system::error_code &ec, std::size_t size) { nReadSize = size; });
size_t iii = m_oIOS.run();
m_pChildProcess->wait();
m_oAsyncBuffer_Out.resize(nReadSize);
std::string strBuf(m_oAsyncBuffer_Out.begin(), m_oAsyncBuffer_Out.begin() + nReadSize);
int result = m_pChildProcess->exit_code();
m_oAsyncPipe_Out.close();
m_oIOS.reset();
return result;
}
Using io_service
To be using async_pipe, you need to supply the io_service instance to the parameter keywords of bp::child:
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/scoped_ptr.hpp>
#include <iostream>
namespace bp = boost::process;
int CallChildProcess_Style9() {
std::string strCmdLine = "/bin/cat";
boost::asio::io_service m_oIOS;
std::vector<char> m_oAsyncBuffer_Out;
bp::async_pipe m_oAsyncPipe_Out(m_oIOS);
std::error_code build_ec;
size_t nReadSize(0);
boost::scoped_ptr<boost::process::child> m_pChildProcess(nullptr);
std::vector<std::string> const args = { "/home/sehe/Projects/stackoverflow/test.cpp" };
m_pChildProcess.reset(new bp::child(strCmdLine, args, bp::std_out > m_oAsyncPipe_Out, build_ec, m_oIOS));
std::cout << "Launched: " << build_ec.message() << std::endl;
m_oAsyncBuffer_Out.resize(1024 * 8);
boost::asio::async_read(m_oAsyncPipe_Out, boost::asio::buffer(m_oAsyncBuffer_Out),
[&](const boost::system::error_code &ec, std::size_t size) {
std::cout << "read completion handler: size = " << size << " (" << ec.message() << ")" << std::endl;
nReadSize = size;
});
std::cout << "read started" << std::endl;
size_t iii = m_oIOS.run();
std::cout << "io_service stopped" << std::endl;
std::cout << "initiate child::wait" << std::endl;
m_pChildProcess->wait();
std::cout << "wait completed" << std::endl;
std::string const strBuf(m_oAsyncBuffer_Out.data(), nReadSize);
int result = m_pChildProcess->exit_code();
m_oAsyncPipe_Out.close();
m_oIOS.reset();
return result;
}
int main() {
CallChildProcess_Style9();
}
Prints
http://coliru.stacked-crooked.com/a/8a9bc6bed3dd5e0a
Launched: Success
read started
read completion handler: size = 1589 (End of file)
io_service stopped
initiate child::wait
wait completed
Hanging Up The Child
Even with that fixed, async_pipe::async_read only reads until the buffer is full or EOF is reached. If the child process outputs more than the buffer size (8k in your sample) then it will get stuck and never finish.
E.g.: replacing the command like this:
std::string strCmdLine = "/usr/bin/yes";
Results in
Live On Coliru
Launched: Success
read started
read completion handler: size = 8192 (Success)
io_service stopped
initiate child::wait
At which it will hang till infinity. This is not because yes has infinite output. Any command having large output will hang (e.g. /bin/cat /etc/dictionaries-common/words hangs in the same way). You can prove this by looking at the strace output:
$ sudo strace -p $(pgrep yes)
strace: Process 21056 attached
write(1, "/home/sehe/Projects/stackoverflo"..., 8170
The easiest way to "fix" this would be to close the output sink after you filled up your output buffer:
boost::asio::async_read(m_oAsyncPipe_Out, boost::asio::buffer(m_oAsyncBuffer_Out),
[&](const boost::system::error_code &ec, std::size_t size) {
std::cout << "read completion handler: size = " << size << " (" << ec.message() << ")" << std::endl;
nReadSize = size;
m_oAsyncPipe_Out.close();
});
This requires you to anticipate that the child exited before you call wait() so wait() might fail:
Live On Coliru
Launched: Success
read started
read completion handler: size = 8192 (Success)
io_service stopped
initiate child::wait
wait completed (Success)
Taking A Step Back: What Do You Need?
It looks, though, that you might be complicating. If you're happy limiting the output to 8k, and all you need is to have multiple copies, why bother with async io?
Any child is already asynchronous, and you can just pass the buffer:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <iostream>
namespace bp = boost::process;
using Args = std::vector<std::string>;
using Buffer8k = std::array<char, 8192>;
int main() {
auto first_out = std::make_unique<Buffer8k>(),
second_out = std::make_unique<Buffer8k>();
*first_out = {};
*second_out = {};
boost::asio::io_service svc;
bp::child first("/bin/echo", Args{"-n", "first"}, bp::std_out > boost::asio::buffer(*first_out), svc);
bp::child second("/bin/echo", Args{"-n", "second"}, bp::std_out >boost::asio::buffer(*second_out), svc);
std::cout << "Launched" << std::endl;
svc.run();
first.wait();
second.wait();
std::string const strFirst(first_out->data()); // uses NUL-termination (assumes text output)
std::string const strSecond(second_out->data()); // uses NUL-termination (assumes text output)
std::cout << strFirst << "\n";
std::cout << strSecond << "\n";
return first.exit_code();
}
Prints
Launched
first
second
More Examples
Because I can't really be sure about what you need, look at other examples that I wrote to actually show live async IO, where you might need to respond to particular output of one process.
Boost::process output blank lines
Read child process stdout in a separate thread with BOOST process
How to retrieve program output as soon as it printed?

serialize temporary into boost archive

The following is not possible for any boost output archive:
int foo(){
return 4;
}
ar << static_cast<unsigned int>(foo());
Is there an alternative without out creating a local temporary x=foo().
and why is the underlying archive operator <<(T & t) not const reference , for an output archive such that the above would work?
This seems to work, and I think this is why:
... To help detect such cases, output archive operators expect to be
passed const reference arguments.
It seems worth noting that in your example ar << foo(); does not work either (i.e. it doesn't have to do with your cast).
#include <fstream>
#include <iostream>
#include <boost/serialization/serialization.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
unsigned int foo(){
return 4;
}
int main()
{
{
std::ofstream outputStream("someFile.txt");
boost::archive::text_oarchive outputArchive(outputStream);
outputArchive << static_cast<const int&>(foo());
}
std::ifstream inputStream("someFile.txt");
boost::archive::text_iarchive inputArchive(inputStream);
int readBack;
inputArchive >> readBack;
std::cout << "Read back: " << readBack << std::endl;
return 0;
}

Move or swap a stringstream

I want to move a stringstream, in the real world application I have some stringstream class data member, which I want to reuse for different string's during operation.
stringstream does not have a copy-assignment or copy constructor, which makes sense. However, according to cppreference.com and cplusplus.com std::stringstream should have a move assignment and swap operation defined. I tried both, and both fail.
Move assignment
#include <string> // std::string
#include <iostream> // std::cout
#include <sstream> // std::stringstream
int main () {
std::stringstream stream("1234");
//stream = std::move(std::stringstream("5678"));
stream.operator=(std::move(std::stringstream("5678")));
//stream.operator=(std::stringstream("5678"));
return 0;
}
source: http://ideone.com/Izyanb
prog.cpp:11:56: error: use of deleted function ‘std::basic_stringstream<char>& std::basic_stringstream<char>::operator=(const std::basic_stringstream<char>&)’
stream.operator=(std::move(std::stringstream("5678")));
The compiler states that there is no copy assignment for all three statements, which is true. However, I fail to see why it is not using the move-assignment, especially since std::move is supposed to return a rvalue reference. Stringstream should have a move assignment, as shown here: http://en.cppreference.com/w/cpp/io/basic_stringstream/operator%3D
PS: I'm working with c++11, hence rvalue-references are part of the 'world'.
Swap
This I found really strange, I copied example code from cplusplus.com and it failed:
// swapping stringstream objects
#include <string> // std::string
#include <iostream> // std::cout
#include <sstream> // std::stringstream
int main () {
std::stringstream foo;
std::stringstream bar;
foo << 100;
bar << 200;
foo.swap(bar);
int val;
foo >> val; std::cout << "foo: " << val << '\n';
bar >> val; std::cout << "bar: " << val << '\n';
return 0;
}
source: http://ideone.com/NI0xMS
cplusplus.com source: http://www.cplusplus.com/reference/sstream/stringstream/swap/
prog.cpp: In function ‘int main()’:
prog.cpp:14:7: error: ‘std::stringstream’ has no member named ‘swap’
foo.swap(bar);
What am I missing? Why can't I move or swap a stringstream? How should I swap or move a stringstream?
This is a missing feature on GCC : see bug 54316 , it has been fixed (you can thank Jonathan Wakely) for the next versions (gcc 5)
Clang with libc++ compiles this code :
int main () {
std::stringstream stream("1234");
std::stringstream stream2 = std::move(std::stringstream("5678"));
return 0;
}
Live demo
And it also compiles the example with std::stringstream::swap
I have an alternative to moving or swapping, one can also clear and set a stringstream to a new string:
#include <string> // std::string
#include <iostream> // std::cout
#include <sstream> // std::stringstream
int main () {
std::stringstream ss("1234");
ss.clear();
ss.str("5678");
int val;
ss >> val; std::cout << "val: " << val << '\n';
return 0;
}
It's a clean work around that does not require one to refactor code, except for the localized section where the swap is changed to a clear() and str().

Retrieving VolumeDetails of WINDOWS Drives - stuck with 'char []' to 'LPCWSTR' conversion

I am trying to get the VolumeDetails of my WINDOWS system- Drive label plus its respective Volume Serial number. I've tried since an hour and built a code which gone wrong in syntax. At present I am getting the following error with it-
error C2664: 'GetVolumeInformationW' : cannot convert parameter 1 from 'char []' to 'LPCWSTR'
Here is my code:
// getVolDrive.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <direct.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <sstream>
#include <string>
#include <ctype.h>
#include <algorithm>
using namespace std;
//wchar_t mydrives[5];// = " A: ";
char mydrives[] = " A: ";
string retVolSno(char drives[]) //wchar_t drives[]
{
DWORD dwSerial;
stringstream ss;
cout<<drives<<endl;
if(!GetVolumeInformation(drives, NULL, 0, &dwSerial, NULL, NULL, NULL, 0))
{
ss<<"Error: "<<GetLastError();
}
else
{
ss<<hex<<dwSerial;
}
return ss.str();
}
int _tmain(int argc, _TCHAR* argv[])
{
string cVolSno;
ULONG DriveMask = _getdrives();
if(DriveMask == 0)
printf("_getdrives() failed with failure code: %d\n", GetLastError());
else
{
printf("This machine has the following logical drives:\n");
while (DriveMask)
{
cout << "In While" << endl;
if(DriveMask & 1)
printf("%s", mydrives);
wcout << mydrives << endl;
cVolSno = retVolSno(mydrives);
cout<<cVolSno<<endl;
++mydrives[1];
DriveMask >>= 1;
}
}
//std::transform(cVolSno.begin(), cVolSno.end(),cVolSno.begin(), ::toupper);
//cout<<cVolSno<<endl;
_getch();
return 0;
}
I've also tried replacing char with wchar_t, I didn't got any build errors, but while executing the application, got Error Code 3- Path not found!.
CODE MODIFIED:
// getVolDrive.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <direct.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <sstream>
#include <string>
#include <ctype.h>
#include <algorithm>
using namespace std;
//wchar_t mydrives[5];// = " A: ";
char mydrives[] = " A:\\\\ ";
string retVolSno(char drives[]) //wchar_t drives[]
{
DWORD dwSerial;
stringstream ss;
wchar_t text[10];
mbstowcs(text,drives,100); //strlen(drives)+1
LPWSTR ptr = text;
if(!GetVolumeInformation(ptr, NULL, 0, &dwSerial, NULL, NULL, NULL, 0))
{
ss<<"Error: "<<GetLastError();
}
else
{
ss<<hex<<dwSerial;
}
return ss.str();
}
int _tmain(int argc, _TCHAR* argv[])
{
string cVolSno;
ULONG DriveMask = _getdrives();
if(DriveMask == 0)
printf("_getdrives() failed with failure code: %d\n", GetLastError());
else
{
printf("This machine has the following logical drives:\n");
while (DriveMask)
{
if(DriveMask & 1)
printf("%s \n", mydrives);
cVolSno = retVolSno(mydrives);
std::transform(cVolSno.begin(), cVolSno.end(),cVolSno.begin(), ::toupper);
cout<<cVolSno<<endl;
++mydrives[1];
DriveMask >>= 1;
}
}
//std::transform(cVolSno.begin(), cVolSno.end(),cVolSno.begin(), ::toupper);
//cout<<cVolSno<<endl;
_getch();
return 0;
}
OUTPUT:
This machine has the following logical drives:
ERROR: 123
ERROR: 123
C:\\
ERROR: 123
D:\\
ERROR: 123
E:\\
ERROR: 123
I see at least these main issues:
1) wchar_t is the right type because you're compiling for UNICODE, you can write generic code using TCHAR macro or explicitly declare your buffer as wchar_t but that's what to do.
2) You have that error because you're passing wrong path to GetVolumeInformation() (trailing backslash is required so A: must become A:\).
Moreover please note that you have a little bit more easy way to achieve same result, you can use GetLogicalDriveStrings() to directly get a NULL delimited string list. Split it using, for example, this (don't forget UNICODE) and use c_str() with each entry.
EDIT about your modified code:
Why you drive path is A:\\ (escaped to A:\\\\)? Just one trailing backslash is needed so mydrives has to be declared as:
wchar_t mydrives[] = L"A:\\";
EDIT 2: there are more errors in your code so I'll post a reviewed version. There are more things I'd change but I'll point out just what doesn't actually work.
Function retVolSno to read volume serial number. Original version were almost right, in your modified version you perform useless character conversion. What you had to do was just to accept a wchar_t drive path.
Global variable mydrives. You actually don't need any global variable for that. It must be wchar_t and space before/after path are useless. One trailing backslash is needed. Line where you increment character value (++mydrives[0];) must be changed accordingly (index 0 instead of 1).
Check for drive availability. After if(DriveMask & 1) you did forget { then you won't print drive name but you'll perform GetVolumeInformation() even on unavailable drives (error 123). That's why indentation is important...
You're mixing UNICODE/NOT UNICODE and C/C++ stuff. I strongly suggest you pick one of them and you keep it (C or C++? UNICODE or NOT UNICODE?). For example you used C function printf() to print stuff and you have both std::string and wchar_t things.
Let's put everything together to have a working version. First the function to read serial number given drive path:
wstring getVolumeSerialNumber(const wchar_t* drivePath)
{
DWORD dwSerial;
wstringstream ss;
if (!GetVolumeInformation(drivePath, NULL, 0, &dwSerial, NULL, NULL, NULL, 0))
ss << L"Error: " << GetLastError();
else
ss << hex << dwSerial;
return ss.str();
}
It's almost the same as your original version, just changed to work with UNICODE characters. Then main function that cycles through available drives and print out their serial number:
int _tmain(int argc, _TCHAR* argv[])
{
wchar_t drive[] = L"A:\\";
ULONG driveMask = _getdrives();
if (driveMask == 0)
wcout << L"_getdrives() failed with failure code: " << GetLastError() << endl;
else
{
wcout << L"This machine has the following logical drives:" << endl;
while (driveMask)
{
if (driveMask & 1)
{
wcout << drive << endl;
wcout << getVolumeSerialNumber(drive) << endl;
}
++drive[0];
driveMask >>= 1;
}
}
wcin.ignore();
return 0;
}
From the documentation , the first parameters should be with trailing slash if drive letter is passed.
lpRootPathName [in, optional]
A pointer to a string that contains the root directory of the volume to be described.
If this parameter is NULL, the root of the current directory is used.
A trailing backslash is required.
For example, you specify \\MyServer\MyShare as \\MyServer\MyShare\, or the C drive as C:\

How do I handle errors in Lua when executing arbitrary strings?

I'm going for absolute minimalism here. (It's been a while since I've worked with the Lua C API.)
#include <lua.hpp>
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char** argv)
{
lua_State* state = luaL_newstate();
luaL_openlibs(state);
string input;
while (getline(cin, input))
{
auto error = luaL_dostring(state, input.c_str());
if (error)
{
cerr << "Lua Error: " << lua_tostring(state, -1) << '\n';
lua_pop(state, 1);
}
}
lua_close(state);
return 0;
}
This program works fine as long as I feed it perfect Lua. However, if I enter something bad (such as asdf()), the program crashes! Why is it not handling my error gracefully?
I've tried breaking out the calls before. It crashes on the call to lua_pcall itself. I never make it past that line.
The binary download (5.2.1 I believe) has a bug that was corrected in 5.2.3. I rebuilt the library from source, and now my program works fine.

Resources