wisaacs -2 Light Poster

I would like to erase my user id and quit the community permanently.
How do I do this?

wisaacs -2 Light Poster

Nice job on the solution! It works like a charm, as long as your input is lower case. You could use the tolower() function on input to normalize for case.

The least you will need in order to rotate your output to vertical orientation, is to find the maximum count for any character. Once you have that.. (you know the rest).

For ASCII characters, there is a shortcut you can use for counters, which takes advantage of the fact that characters are small numbers:

# include <string>
using namespace std;
int* histogram(string s,int &last_found,int &max)
{
	static int counters[1 << 8* sizeof(char)]; // counter for each possible byte
	memset(counters,0,sizeof(counters));
	last_found= 0;
	max=0;
	for( string::iterator i=s.begin(); i!=s.end(); ++i )
	{
// here is the critical trick:
		unsigned char c= (unsigned char)tolower(*i); // 'unsigned char' is one byte
// .. and one byte is a number between zero and 255 ; perfect for an array index
		if(++counters[c] > max) max = counters[c]; // use the byte as an index
		if( c > last_found ) last_found = c;
	}
	return counters;
}

.. and you can call the function like this:

getline(cin,sentence); // this allows spaces

		int last,max,*name;
		name= histogram(sentence,last,max);

		for (unsigned char r=0; r <= last; r++){
			if (name[r] > 0){
				cout<<r<<" ";
				for(int i=0; i <name[r]; i++)
					cout<<"*";
				cout<<endl;
			} 
		} 
		cout << "Max found for any char: "<< max
			<<".\n\nAnother? [y/n]: ";
wisaacs -2 Light Poster

Often with counting/histogram problems, you can make an array of counters, one for every possible value you are counting.

#include <iostream>
using namespace std;
int main()
{
  unsigned char uc = 'a';  // the unsigned version is just a non-negative number
  cout<< "the numerical value for \'" << uc << "\' is: "<< (int)uc << endl;
  uc= -1;
  cout<< "the maximum number a character can hold is: "<< (int)uc<< endl;
  return 0;
}

.. And remember, any non-negative number can be used as an array index. Already I have said too much.

wisaacs -2 Light Poster

Aaaah, so strings are new to C++ from C. In C you used to have to create a string as an array of char types.

This part of your summary is correct! But...

When you subtract a string from another string, or attempt to access its elements, it likes to break them back down to c style strings first.

I realize the analogy is a bit shaky but:
Kind of like vector images being turned to bitmaps for editing in photoshop, then back into a vector graphic. Or a nurbs > Poly for editing > back to nurbs.

That part of your comment is not right. You can never subtract one std::string from another one. It is iterators you can subtract, because iterators work like pointers. And, like pointers, iterators do not touch the data. There is no conversion, no bit manipulation. An iterator just points to something inside a container. It is an address. It's incredibly fast.

int main(int argc, char* argv[])
{
	std::string x = "some stl string";
	std::string::iterator begin = x.begin();  // this is an iterator
	std::string::iterator end = x.end(); // this is another iterator
	int LEN = end-begin;  // iterators within the same container can be subtracted
	// try subtracting two strings. this will confuse the compiler plenty
	std::string y = "another stl string";
	void * this_will_never_work = x - y ; // subtract two strings?  nope.
	return 0;
}
wisaacs -2 Light Poster

Strings are slightly different in C, because they are not a real separate type. A string in C is defined as an array of plain chars, with a zero for the last character.

char string[] =
	{'I',' ','a','m',' ','a',' ','C',' ','s','t','r','i','n','g', 0 };

int main(int argc, char* argv[])
{
	printf( string ); // this works fine.
	return 0;
}

But, remember about arrays being just pointers (from earlier post) ?
What people call a string, in C, is just a pointer to the first character in an array, where the last character is a zero. If there is no zero at the end, it is not a C string.

#include "stdafx.h"
char* aString = "123";  // this is the same as saying "char aString[4] = {'1','2','3',0};
int main()
	{
	// print the string, the hard way...
	char* pChar;  // a pointer to a character...
	pChar = aString;  // points to the first character in an array of chars.
	while( *pChar != 0 ) // quit when the char is zero
		{
		putchar(*pChar); // print one character
		++ pChar; // move the pointer to the next character
		}
	}

So, a C string is a pointer to a character.
And, you can definitely have an array of those!

// array of strings, which is just an array of pointers
char * array_of_chars[] = { "1", "2", "3" }; // this is an array of pointers

// which is the same as:

// three strings, i.e. arrays …
wisaacs -2 Light Poster
// plain old unprotected variable called 'price'
    class A
    {
        public double price =0;
    } // end, class A

// this class works just the same, but uses a property instead
    class B
    {
        private double _price =0;
        public double price
        {
            get { return _price; }
            set
            {
                if (value < 0 || value > 100.00)
                    throw new Exception("bad price");
                _price = value;
            }
        }
    } // end, class B

    class Program
    {
        public static void Main(string[] args)
        {
            A a = new A();
            a.price = -99999999.00234;   // you can set this to anything.. 
            Console.WriteLine("{0} for a book?  No Problem!",a.price);
            B b = new B();
            b.price = 99999999.00234;   // try a property!
            // KABOOM
            Console.WriteLine("happy ending"); // we never get here
        }
    } // end Program

Now, rather than blowing up, you might want to catch the exception...

class B
    {
        private double _price = 0;
        public double price
        {
            get { return _price; }
            set
            {
                if (value < 0 || value > 100.00)
                    throw new Exception(
                        string.Format(
                        "What!  {0} (?) Bad price. What were you thinking?"
                        , value
                        ));
                _price = value;
            }
        }
    } // end, class B

    class Program
    {
        public static void Main(string[] args)
        {
            B b = new B();
            try // no KABOOM, KABOOM bad
            {
                b.price = 99999999.00234;   // now try it..
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            // normal happy continuation
            b.price = 99.50;
            Console.WriteLine("happy ending");
        }
    } // end Program
wisaacs -2 Light Poster

correction in STARTUPINFO initialization block:

// Set up the start up info struct.
	ZeroMemory(&si,sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESTDHANDLES;
	si.hStdOutput = hOutputWrite;
	si.hStdInput  = hInputRead; // I didn't give you this one.. exercise!
	// THIS IS THE CORRECTION:
	si.hStdError  = hErrorWrite; // use the duplicate handle created for stderr
wisaacs -2 Light Poster

Here is the real answer, and it was very tough to discover.

Named Pipes normally fail for redirection because the default handles must be modified before they are inherited by the second process.

Your server-side handles need to be non-inheritable, even though you must create the pipe using an inherit-handle attribute. The solution is, you first create the pipe, then duplicate both handles with the attributes fixed, then close the original handle.

// you need this for the client to inherit the handles
	SECURITY_ATTRIBUTES sa;
	// Set up the security attributes struct.
	sa.nLength= sizeof(SECURITY_ATTRIBUTES);
	sa.bInheritHandle = TRUE; // this is the critical bit
	sa.lpSecurityDescriptor = NULL;
	//
	// Create the child output named pipe.
	//
	//  anon. pipe would be:
	//  if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0)) DisplayError("CreatePipe");
	//
//
// this creates a inheritable, one-way handle for the server to read
//
	if((hOutputReadTmp= CreateNamedPipe(
		"\\\\.\\pipe\\outstreamPipe",
		PIPE_ACCESS_INBOUND,
		PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT,
		1,
		4096,
		4096,
		0,
		&sa
		))==INVALID_HANDLE_VALUE) DisplayError("CreateNamedPipe");
//
// however, the client can not use an inbound server handle.  Client needs a write-only,
// outgoing handle.
// So, you create another handle to the same named pipe, only write-only.
// Again, must be created with the inheritable attribute, and the options are important.
//
// use CreateFile to open a new handle to the existing pipe...
//
	hOutputWrite = CreateFile(
		"\\\\.\\pipe\\outstreamPipe",
		FILE_WRITE_DATA|SYNCHRONIZE,
		0,
		&sa,
		OPEN_EXISTING, // very important flag!
		FILE_ATTRIBUTE_NORMAL,
		0 // no template file for OPEN_EXISTING
		);
	// we're going to give the same pipe for stderr, so duplicate for that:
	// …
Dennis M. commented: Awesome post! Really helped me understand named pipes much better! +1
wisaacs -2 Light Poster

MSDN will always give you Microsoft-specific things to use. It's part of the Microsoft business model. MSDN code will only work on MS systems.
"Standard C Library" functions that are ancient and portable have names like
printf, scanf, fopen, strcmp, puts, getchar, fwrite, strcspn.
Stick to these, and you will be able to write on any system in existence.

They are not graphical though. No mouse, no buttons. You will need special libraries for those no matter what system you use. Java has graphics built in, and it runs everywhere, but it's not C++. It is based on C++.

wisaacs -2 Light Poster

If you are using the VS2008 publish wizard, here is what you need:
There is a window called Project->Properties.
That window has lots of tabs.
Project->Properties->Application is the first tab.
On that screen, there is a combobox, third line from the top, called "Target Framework". That box lets you select which .NET Framework your program needs to run.
Also in Project->Properties there is another tab to control what happens automatically when the user clicks your setup. That is called Project->Properties->Publish, which is the last tab, on the bottom.
Project->Properties->Publish has a button called "Prerequisites". That button lets you choose which free programs and .NET Frameworks will automatically install on the user's computer, as part of your setup.

wisaacs -2 Light Poster

I just found a few links on this topic:

http://msdn.microsoft.com/en-us/library/abx4dbyh.aspx

.. says you should compile with static options /MT or /MTd.
I read also that if you don't use .NET Framework 4.0, setting the target framework to an older version might also change this dependency.

wisaacs -2 Light Poster

Forget iostream.

#include <conio.h>
#define IOFLUSH {while(_kbhit())_getch();}
#define PAUSE {if(!_getch())_getch();}
Nick Evan commented: That's horrible advice -3
wisaacs -2 Light Poster

Check the DLL for corruption.
Try switching your compile target (for every module you build) to 32-bit, rather than x64.
Sometimes the compiler will default to build for the current processor, and this breaks libraries compiled for the older versions.

wisaacs -2 Light Poster

There are actually several ways for your code to work in a Windows program. You can cause a windows program to also have a console display by changing the subsystem type in Visual Studio. Then std::cout will work, but you need a separate thread, independent from WinMain to get input. Another option is to write a stream class derived from ostream which sends your text to a Windows TextBox. Both of these tricks require advanced skills.

wisaacs -2 Light Poster

Windows programs are different from console programs in three important ways.
1. the 'main' function is hidden from you. Windows programs start with a function called "WinMain". Instead of you calling functions to use, a Windows program calls you when something happens.
2. Windows programs have no console display. The console display is that familiar old black DOS/Unix screen, which is actually a pretty complicated and useful tool. When your console program says cout<< "hello";, that means 'ConsoleOUT', or, send this string to the console display. A window is much simpler than a console. It is just a frame with no picture inside. To see your text, the window must have a textbox or something else to hold text.
3. A Windows program operates through messaging, and must have a "message loop" in the WinMain. Newer versions of Windows, like Forms and WPF, hide that loop from you, but it is still there. So, the idea when you write a Windows program is, handle all the messages. All buttons, menu items, tab controls, etc. become messages, and you have to write code to deal with each one of them.

Windows programming, and to some extent all UI programming, is about handling messages.

wisaacs -2 Light Poster

Coplien's "Advanced C++ Programming Styles and Idioms".
This is the original book that shows how C++ was used in the years of testing and experimentation, before official release; how the language is designed to be used.
It was published long before STL, Patterns, or Boost, so separate specialized sources are better for those important areas.

wisaacs -2 Light Poster

I'm not currently running a Unix build, but aren't msgrcv/msgsnd routines extremely sensitive to correct buffer size? What line, which call, causes the error?

wisaacs -2 Light Poster

This gets some basic device information.

//handle to the drive to be examined
	HANDLE hDevice	= CreateFile(TEXT("\\\\.\\G:"),	//Drive to open
		GENERIC_READ|GENERIC_WRITE,//Access to the drive
		FILE_SHARE_READ|FILE_SHARE_WRITE,//Share mode
		NULL,//Security
		OPEN_EXISTING,0,// no file attributes
		NULL);
	if (hDevice == INVALID_HANDLE_VALUE)//Cannot open the drive
		return 0;

	CDROM_TOC val; // table of contents for a generic CDROM
	DWORD nBytesReturned;

	BOOL bResult= DeviceIoControl(
		hDevice, 
		IOCTL_CDROM_READ_TOC,//operation to perform
		&val, sizeof(val),//no input buffer
		&val, sizeof(val),//output buffer
		&nBytesReturned,//#bytes returned
		(LPOVERLAPPED) NULL);//synchronous I/O		

	CloseHandle(hDevice);
wisaacs -2 Light Poster

Create a console project, and use
#define WIN32_LEAN_AND_MEAN

wisaacs -2 Light Poster

Can you run program on PC A ?
Or, you only want to run program on PC B ?

wisaacs -2 Light Poster

I don't know of an automatic way. At Form-load, you have to open the file, read stuff into memory, create objects, and place them into the combobox.

wisaacs -2 Light Poster

Two old, standard tools for compiler writing are lex and yacc, (flex/Bison for Windows). It's a profound topic.

wisaacs -2 Light Poster

Almost certainly this is not a socket issue. What are the two applications?
If it is C/C++ and you are using 'spawn', you need to use one of the 'p' suffix spawn variants.

wisaacs -2 Light Poster

The Eval function loops through tokens without remembering what it did last and returns a bool value rather than double.
For parsing, there is no replacement for strtok, or something that does the same.
So, you get the string, tokenize it into an array of small tokens.
Then, you use standard parsing functions for the numerics.
You might want to look into the standard parsing functions atof, atod, or atoi.

wisaacs -2 Light Poster

Named pipes will work. Nothing you said is wrong, so you will have to post code. The assumption that pipes are required for asynchrony is wrong though. Pipes have no relationship to asynchrony. You could do everything in one process without IPC (interprocess communication), by launching a thread from your WPF or Forms application. You can enable the console system on a GUI project, and you automatically get a console window which responds to standard io commands.

wisaacs -2 Light Poster

wups, sorry. My bad.

wisaacs -2 Light Poster

It's plain wrong

wisaacs -2 Light Poster

CrazyDieter is right. This is not a language question. Every operating system has special functions which tell you about devices, and they are different for every system.
In Windows, the functions for managed code in the .NET framework are different from the ones you use with regular code.

wisaacs -2 Light Poster

Nice elegant function for 'palindrome'.
Your string buffer is completely unprotected.
Writing safe string code in C isn't too hard, but you have to change most of your function calls. First, if you allocate fixed arrays, 'sizeof' is your friend.

// use a reasonable buffer-size.  RAM is cheap.  Any single variable can go to 4096 without thinking.
#define BUFFERSIZE 250
	assert( BUFFERSIZE > 1 );
	char a[BUFFERSIZE];
	size_t last= sizeof(a)-1; // with a fixed array this always gets last byte

The scanf function is dangerous. One way to protect your buffer is to provide a length limiter in the "%s" string format, as in "%20s". Here is some code to do that:
Also, always terminate a string buffer after input, because some functions don't do it for you.

char fmt[100]; // enough to hold any number
	sprintf_s(fmt,sizeof(fmt),"%%%ds",last); // an array of size 10 will yield "%9s".
// always terminate a string returned from a function
	fmt[ sizeof(fmt)-1 ] =0; 
	printf("generated format string: \"%s\"\n",fmt);

Now you have a format string that guarantees 'scanf' won't clobber your buffer.

scanf(fmt,a); // get user input
         // now, to get the length of user-generated input.
         // strlen, right?  not so fast..
         // size_t n= strlen(a); // no! very dangerous. 'a' could be unterminated
	a[last]=0; // force-terminate the string.  It's cheap and easy.
         size_t n= strlen(a); // okay now, because string will never overrun.
	printf("input: \"%s\"\n",a);
wisaacs -2 Light Poster
const char* code_already_supplied_as_a_starting_point = 0;
for(;;) 
{
  printf( "give me code " );
 if( ! code_already_supplied_as_a_starting_point )
  printf ("you first " );
}
wisaacs -2 Light Poster

In other words, the new machine must have Framework, but your program can automatically install that also, if you tell it to.

wisaacs -2 Light Poster

Computer memory is numbered. Every slot can hold one byte. An address is the count of that particular storage place.
And, iterators are like pointers, that is, addresses in computer memory.

int i=32; // the value of 'i' is 32, but where is that '32' stored?
int* ptr= &i; // 'ptr' holds the address of 'i'
cout<< ptr<< endl; // .. some big number like 0012FF24.  A spot in your computer's memory

It usually does no good to show where the item is located in RAM.
If you want the ordinal, that is the position, in order, of the element in memory,
you can subtract one pointer from another:

int arr[10]= {10,20,30,40,50,60,70,80,90,100};
int * begin = &arr[0]; // the address of the first element
int * end = &arr[10]; // the address of the 11th item (don't try to use!)
cout<< "the offset of item 11 is: "<< end-begin << endl;

No matter how many bytes each item requires, when you subtract two pointers, the C/C++ compiler converts the result into numbers of that type. Good old C pointer arithmetic.
.. And all this is simpler if we remember that an array in C/C++ is just a pointer to the first item. You actually never need to define a pointer-to-begin:

int ar[10]= {10,20,30,40,50,60,70,80,90,100};
// int * ptr1 = &ar[0]; // the address of the first item, but who needs it? 
int * ptr2 = &ar[9]; // the address of the 10th element
cout<< …
wisaacs -2 Light Poster

firstPerson is right. Iterators are designed to work like array pointers.
So, in C, you would say:

// this is standard C usage
int a[10]; // the array we want to use
int* begin = a;

// end-pointer, to test when our pointer has gone too far
int* end= a+10; // must NEVER actually try to use this pointer
// 'end' points to hellish unknown memory beyond the array

int* iter = begin;
while( iter != end ) // use 'end' only to test when we have gone too far
  {
   *iter = 42; // use the pointer to write the array
   ++iter; // count the pointer forward
  }

.. becomes, for C++ STL :

// this is standard C++ usage
#include <vector>
using namespace std;

vector<int> a(10); // the array we want to use

vector<int>::iterator iter = a.begin();
while(  iter != a.end() ) // use 'end' only to test
  {
   *iter = 42; // use the iterator to write the array
   ++iter; // count the iterator forward
  }
wisaacs -2 Light Poster

My system can't read the Word attachment.
Word .doc files are virus vectors.
You might want to post the math doc in hypertext.

wisaacs -2 Light Poster

If you really need to propagate the exception to global scope, you can add a String.Format:

throw new System.InvalidOperationException(
				String.Format(
				"For {0} the maximum price allowed for a {1} page is {2}$"
				, book1.Title, book1.Pages, book1.Pages * 0.10)
				);

.. but, your basic handler might be enough. The System exception re-throw is only if the problem is so bad you have nothing left to do but crash out of your whole program.

wisaacs -2 Light Poster

The key word is "Deployment". This is very difficult, even for professionals.
Small companies have one deployment specialist. Big companies have a whole department.
Microsoft Visual Studio does help you.
1. For SQL in small projects on one computer, there are two free options.
One is called SQL-Compact. The other is called SQL-Express. Both of these are free. SQL-Compact does not require separate installation, but it is harder to use than regular SQL. SQL-Express a full-blown server, but the customer must download it.
2. .NET Framework version is an option for your deployment project. So, when you click "publish", there is an option to include the most recent version of .NET Framework, together with your program.

wisaacs -2 Light Poster

I oversimplified, a little. It's more accurate to say "static class variables need to be declared in non-local scope", but, the given explanation will work until you learn why it's slightly wrong.

wisaacs -2 Light Poster

This is a problem of understanding memory allocation, and scope.

As you probably know, class Instance variables (or "Properties", in the uselessly new parlance) are automatically allocated when you create a class object, and do not need to be separately declared:

class B
	{
	public:
	int k; // a NON-STATIC (i.e. "Instance") variable
	};

void main()
	{
	B b; // All class instance variables are created here...
	b.k= 42; // .. and can be used.
	}

.. But "static" class variables must be declared like this:

class A
	{
	public:
	static int i; // this is a promise, that somewhere, you will allocate one of these
	};

int A::i = 42; // this is where you allocate (make space for) the static variable

void main()
	{
	A::i = 900; // you can use it anywhere, but it must be allocated outside all functions.
	}

You must allocate all static variables outside the body of the class, but in the same scope as the class itself.

.. And, what is scope?

// in C++, braces begin and end "Scope", or, neighborhood.
// ALL VARIABLES ARE DECLARED INSIDE SOME SCOPE

// a class has braces.. and thus defines a scope:
class A
	{
	public:
	static int c; // this 'c' is only for class A.
	};

// '::' is called "scope resolution operator"
int A::c= 42; // means, allocate 'c' in class A's scope

void main()
	{
		// here is one scope...
		{
		int c= 47; // …
wisaacs -2 Light Poster

Ha! An intentional fork-bomb. I thought that was the student code.

wisaacs -2 Light Poster

C/C++ logic is always just testing for zero.
In C/C++, zero is false, anything else is true.

int i = 3;
while ( i ) // test if i is non-zero
{
 i= i-1; // this executes 3 times
}

One result of this is that the term " .. != 0 " .. is always redundant in C.
You can leave it out, because C/C++ always tests for nonzero.
Testing a number for true/false is perfectly valid C/C++:

int i = 42;
if ( i ) i = i-1; // "if(i)" is same as "if( i != 0 )"

I use this feature of C/C++ all the time in my work.
for instance, the cleanest way to count through a list backwards is:

int i= listSize;
while(i--) // quit on zero, countdown
 list[i]= 0;

.. or, for system functions that return zero/NULL when they fail:

//
// open a file
//
FILE *f = fopen( "c:\\input.txt", "r" );
//
// did the file open okay?
//
if(f) // this works because 'fopen' returns zero on failure
 {
 // .. (do some stuff)

 // close the file
 fclose(f);
 }
else printf( "could not open the file" );

Also remember that most operators in C, like '+' or '<' are "binary operators", meaning, they only work for two numbers; the one on the left, and the one on the right.

So, when you have a string of binary operators all in a …

wisaacs -2 Light Poster

I think you are forking more threads than you intend. You realize that in each loop, you wait for the first thread, but fork three more, which all complete, and go back into the loop ?
That's 3 * 3 * 3 *... a hundred times, or, 3 to the hundredth power.
The system dies from handle starvation, massive memory swaps and context switching.
You need to wait on all but one thread.

wisaacs -2 Light Poster

It sounds like a typical introductory programming assignment.
No cheating!

Just to get past language issues, here are some tips:
An array of characters in most languages is called a "string".

1. a fixed, unchanging string in C/C++ can be declared like this:

static const char* fixed_array_of_characters= "fubar" ; // a few characters
static const int howmany = sizeof(fixed_array_of_characters)-1; // in this case, 5

2. you can get a character out of the string like this:

char B= fixed_array_of_characters[0]; // item zero is always first in a C array

3. you compare two single characters like this:

if( B == 'x' ) printf( "match!" );

4. you walk through a string like this:

char A;
for( int i= 0; i< howmany; ++i )
  A= fixed_array_of_characters[i] ; // this doesn't do anything except get one character
wisaacs -2 Light Poster

The two new streaming commands only work for version 2 of microsoft's SCSI-3/MMC driver (cdrom.sys), which shipped with Vista.
http://msdn.microsoft.com/en-us/library/ff551396(VS.85).aspx

wisaacs -2 Light Poster

LBA==Logical Block Address, for commands that specify physical locations on the drive. I can't find any place that mentions if they are used for this command or not. I max them both out and it doesn't seem to matter.
If the "Persistent" flag is set, then the changes stay active even when you change media. The "RestoreDefaults" flag seems to ignore everything else and reset the device to its normal values.

wisaacs -2 Light Poster

CDROM_SET_SPEED may only be useful for the same process reading or writing the data. I have had better luck with SET_STREAMING:


HANDLE hDevice= CreateFile(TEXT("\\\\.\\D:"),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,//Security
OPEN_EXISTING,
0,//file attributes
NULL);
CDROM_SET_STREAMING s;
s.RequestType=CdromSetStreaming;
s.ReadSize= 0x2000; // in kbytes (0xffff==optimal)
s.ReadTime= 1000; // in ms
s.WriteSize= 0xffff;
s.WriteTime=1000;
s.StartLba= ~0x0i64;
s.EndLba= ~0x0i64;
s.RotationControl=CdromDefaultRotation;
s.RestoreDefaults= false;
s.SetExact= false;
s.RandomAccess= false;
s.Persistent= true;

// results variables
BOOL bResult;
DWORD dwBytesReturned;

bResult = DeviceIoControl(hDevice
, IOCTL_CDROM_SET_SPEED
, &s, sizeof(s)
, NULL, 0
, &dwBytesReturned
, NULL // (blocking call)
);

wisaacs -2 Light Poster

Your code runs without error on my Win7/vs2010 system, although the cdrom drive doesn't seem to do anything.
This command is new. Your cdrom.sys driver may not have the new commands.

For explanation of system errors, I find the following function useful:

static inline void showError(const _TCHAR *source=0)
{
    DWORD e=GetLastError();
    LPTSTR buf=0;
    DWORD fmt= FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM
        ,0,e,0, (LPTSTR)&buf,1,0);
    if(fmt)
    {
        if(!source){_tprintf(L"%s\n",buf);}
        else _tprintf(TEXT("%s: %s\n"),source,buf);
    }
    else _tprintf(TEXT("problem formatting message code: %ld\n"),e);
    if(buf)LocalFree(buf);
}