RESTful WCF at Atlanta Code Camp

gullivers_travels

This past Saturday was the Atlanta Code Camp.  I want to be make a point of thanking Cliff Jacobson, Dan Attis, Doug Ware, Glen Gordon, Jeff Ammons and all the other organizers who made this a brilliant event.

My company, Magenic Technologies, presented at seven of the sessions this year. 

Sergey Barskiy, who was a developer on the CSLA-Lite team, presented on Building Silverlight Business Applications using CSLA.NET for Silverlight.

Whitney Weaver, another of our principal consultants and a master of all things data, spoke on What’s my data doing while I sleep? Tracking data changes in SQL Server 2008.

Colin Whitlatch presented an Introduction to ASP.NET MVC, and is planning to extend his presentation over the next few weeks on his blog, where he will be digging deep into ASP.NET MVC and helping others to do the same.

Jason Rainwater needs a blog, because he is currently one of the best WPF experts in Atlanta and there aren’t enough venues at this point for him to show off everything he knows.  He did two presentations this year: WPF Custom Controls and WPF DataBinding.

I presented on REST-ful WCF (the hyphen is a personal grammatical quirk rather than any attempt to make a philosophical point) and Mocking with Rhino Mocks and TypeMock: A Head-to-head Comparison.  I also got to make a short app-dev appearance in Tejas Patel’s Use of data mining controls with ASP.NET presentation.

As promised to the attendees, I’m making the code samples and slide-decks available.  Please excuse the lack of organization in the code — it’s pretty much just what I was writing while I was on-stage, but should be helpful if anyone is looking for samples.  All the code uses Visual Studio 2008.  The mocking samples will require that you have the appropriate dll’s for Rhino Mocks, which is free,  and TypeMock Isolator, which has a 30-day trial.

I am indebted to Pedram Rezaei for getting me started on the WCF-to-Flikr sample.

The Rest-ful WCF slide-deck and code can be downloaded here.

The Mocking powerpoint and samples are available here.

Meatware

A Gibsonesque cyber-word, meatware refers, somewhat contemptuously, to those aspects of information processing that are neither software nor hardware.  Programming is a grueling mental activity, and there is a tendency among software programmers to, shall we say, not look after themselves.  There is an old adage that one should never trust a thin cook, and this might be extended to programmers also.  The most consummate technologists spend so much of their time in virtual worlds that their bodies often get neglected.  The state of their bodies becomes, consequently, an ironic badge of their devotion to their craft.

It has been said, mainly by its critics, that Modernism in philosophy since Descartes is distorted by the implicit assumption that object of philosophy is strictly rational, conscious, and intellectual.  This trend was turned back, somewhat, by Heidegger’s discussion of Mood in his masterpiece Being and Time.  Maurice Merleau-Ponty’s Phenomenology of Perception, which at times reads like a rewriting of Being and Time much as Sartre’s Being and Nothingness does, takes this battle further by placing himself within the heart of the intellectual tradition, Husserl’s Phenomenology, and emphasizing the point that all perception, all experience, occurs through the medium of our bodies.  This was, strangely enough, a revolutionary insight at the time.

Eventually Feminism (or at least certain branches of Feminist thought) took up this controversy and used it as a central template for understanding the misunderstandings between men and women.  Men misunderstand humanity as a primarily intellectual (and phallic) being.  Women, on the other hand, implicitly understand the role of the body in the same way that tides understand gravity.   It is an inescapable aspect of a woman’s existence, which the scholars of women’s issues tend to call “embodiment“.

It can’t be said that software programmers really learned anything from the insights of Feminism other than the fact that they would prefer to have very little to do with the body.  If there is such a thing as human nature, software programming tends to distort it and encourage anti-social behaviors such as distractedness, obsessiveness and self-medication.  Exemplary programmers need not be exemplary human beings, and perhaps ought not to be, to Aristotle’s dismay.

Stephen Dubner at Our Daily Bleg suggests an economic explanation for the rise in American obesity.  He suggests that the elimination of outhouses and dramatic improvements in indoor plumbing may have led to the rapid increase in median weight.  Our improved ability to vacate our own waste, he avers, has lowered the inconvenience of indulging in the gastronomic pastime, and so we do.  It depends, I imagine, on whether one seeks answers in the superstructure or in the base. 

Vanity Fair, on the other hand, has a series of articles currently online which may provide a glimpse at what the unhealthy have to look forward to.  Though not himself a programmer, Christopher Hitchens has drunk, smoked and eaten himself to the point that he can be mistaken for one.  At 58, he attempts to turn back the clock of desultory living with a check-in at a spa, and writes about it. 

The articles are accompanied by illustrative photos which highlight this cautionary tale about the importance of maintaining your meatware.

Extending the Ajax Control Toolkit Tab Container with Lazy Loading

multi-tabs

 

download source code

ASP.NET has been missing a good, free tab control for a long time.  With the ACT Tab Container, we were finally given one.  It typically runs in client-side only mode, but can interact with server-code if we set its AutoPostback property to true.

Compared to what we had before, it is a huge improvement.  The peculiar thing about it, however, is that it isn’t actually an Ajax control.  It doesn’t use asynchronous postbacks or web service calls to talk to the server — instead you just have these two mode: run it using client script only, or run it using server-side events and code-behind only.

So a few months ago I rectified this for a project, and only found out afterwards that Matt Berseth had already outlined the technique on his blog.  You basically run the tab container in client-side mode, and add update panels to the tab panels that you want to be ajaxy.  You then hook up the client-side ActivePageChanged event in such a way that it spoofs the Update Panel contained in the tab, causing an asynchronous (or partial) postback.

Matt also gave this technique a cool name.  He called it ‘lazy loading the tab panel’.  Like lazy loading in OOP, using this technique the update panels inside each tab panel only do something when its tab is selected.  Information is loaded only when its needed, and not before.

I must admit that I hold some resentment against Matt for coming up with this first, and for coming up with the cool moniker for it.  On the other hand, the solution I came up with encapsulates all of the javascript needed for this into a nice simple extender control that you can drop on your page, which his does not, and I’m rather proud of this.

The VS 2008 project for this extender is linked at the top of this post.  To use it, you need to compile the project and add the compiled assembly to your project, or else just add the project to your solution and add a project reference.

1. Drop the TabContainerExtender control into your page.

2. Set the Extender’s TargetControlID property to your TabContainer’s ID.

3. In the RegisterUpdatePanels element of the Extender, map your tabs to your update panels.  This mapping tells the extender which Update Panels to activate when each tab is selected.

Your markup will look something like this:

    <cc2:TabContainerExtender ID="TabContainerExtender1" 
    runat="server" 
    TargetControlID="TabContainer1" OnActiveTabChanged="ActiveTabChanged">
    <RegisterUpdatePanels>
    <cc2:UpdatePanelInfo TabIndex="0" UpdatePanelID="UpdatePanel1" />
    <cc2:UpdatePanelInfo TabIndex="1" UpdatePanelID="UpdatePanel2" />
    <cc2:UpdatePanelInfo TabIndex="2" UpdatePanelID="UpdatePanel3" />
    </RegisterUpdatePanels>
    </cc2:TabContainerExtender> 

4. If you want to add some code-behind to your active tab changed event, add set the OnActiveTabChanged property of the Extender to the name of your handler.  The thrown event will pass the correct Index number for the active Tab, as well as the ID of the mapped Update Panel.  The handler’s signature looks like this:

        protected void ActiveTabChanged(int index, string panelID)

        {

            …

        }

I highly encourage you to read Matt Berseth’s blog entry (which I have to admit is pretty good) to get a clear idea of the techniques being applied in this ajax extender.  If you just need a quick solution, however, feel free to download this code from the link at the top and use it any way you like with no strings attached.  There is a sample project attached to the solution that will demonstrate how to use the Tab Container Extender, in case you run into any problems with lazy loading your panels.

For reference, here is the code for the sample implementation, which loads controls on the fly based on the tab selected:

    <cc1:TabContainer ID="TabContainer1" runat="server">
    <cc1:TabPanel ID="TabPanel1" runat="server" HeaderText="Tab Panel 1">
    <ContentTemplate>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
        Content 1 ...
        <br />
            <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>      
        </ContentTemplate>
        </asp:UpdatePanel>    
    </ContentTemplate>
    </cc1:TabPanel>
        <cc1:TabPanel ID="TabPanel2" runat="server" HeaderText="Tab Panel 2">
    <ContentTemplate>
        <asp:UpdatePanel ID="UpdatePanel2" runat="server">
        <ContentTemplate>
        Content 2 ...
        <br />
            <asp:PlaceHolder ID="PlaceHolder2" runat="server"></asp:PlaceHolder>      
        </ContentTemplate>
        </asp:UpdatePanel>    
    </ContentTemplate>
    </cc1:TabPanel>
        <cc1:TabPanel ID="TabPanel3" runat="server" HeaderText="Tab Panel 3">
    <ContentTemplate>
        <asp:UpdatePanel ID="UpdatePanel3" runat="server">
        <ContentTemplate>
        Content 3 ...
        <br />
            <asp:PlaceHolder ID="PlaceHolder3" runat="server"></asp:PlaceHolder>      
        </ContentTemplate>
        </asp:UpdatePanel>    
    </ContentTemplate>
    </cc1:TabPanel>
    </cc1:TabContainer>
    <cc2:TabContainerExtender ID="TabContainerExtender1" 
    runat="server" 
    TargetControlID="TabContainer1" OnActiveTabChanged="ActiveTabChanged">
    <RegisterUpdatePanels>
    <cc2:UpdatePanelInfo TabIndex="0" UpdatePanelID="UpdatePanel1" />
    <cc2:UpdatePanelInfo TabIndex="1" UpdatePanelID="UpdatePanel2" />
    <cc2:UpdatePanelInfo TabIndex="2" UpdatePanelID="UpdatePanel3" />
    </RegisterUpdatePanels>
    </cc2:TabContainerExtender> 

Navel Gazing

orestes

In Greek, it is called Omphaloskepsis — though the provenance of this term appears to be fairly recent.  Its origins seem to be found in Eastern meditative practices in which the subject concentrates on his navel, the center of his being, in order to shut out all worldly distractions.  The significance of the navel is also found in the origins of Western culture.  Omphalos, literally "navel", was used to describe the terrestrial location from which all life sprang.  It was also the name of the stone found in the temple at Delphi, purported to be the stone which Rhea swaddled in baby clothes and which the titan Chronos swallowed, believing it to be his son Zeus, prophesied to one day overthrow him.  Because of its divine origins, the Omphalos was believed to be a point of contact between the celestial and the terrestrial, and gazing at the Omphalos was equivalent to gazing into the realm of the gods.

It is also a mode of reflection common in times of uncertainty.  The IT industry is at such a point.  Any software professional who went through the .NET bust at the turn of the millennium and through the financial decline following 9/11 has an ingrained sense that changes in the economy can radically alter the face of IT.  The past five years have seen the emergence and adoption of various tools and methodologies, from Agile to Responsibility Driven Design to Domain Driven Design to alt.net, all premised on the notion that things haven’t been going well, and if we all just put our heads together we will find a better way. 

Marshall McLuhan — I believe it was in The Medium is the Massage — tells an anecdote about a factory that brought in consultants to change their internal processes, resulting in a 10% increase in productivity.  A year later, for unspecified reasons, the factory reverted to its previous processes, and surprisingly increased productivity by another 10%.  This suggested to McLuhan that sometimes what one does to bring about change is not significant in itself.  Sometimes it is simply the message of change, rather than any particular implementation, which provides results.

The various methodologies, philosophies and practices of the past few years seem to have made improvements in some software houses, but it is not clear that this is due to the inherent wisdom of the prescribed techniques rather than, as McLuhan might say, the simple message that things aren’t quite right with our industry.  The acolytes of each methodology that comes along initially cite, quite properly, Fred Brooks’s influential articles The Mythical Man-Month and No Silver Bullet.  What they rarely do, once their movements achieve a certain momentum, is revisit those early arguments and evaluate whether they have accomplished what they set out to do.  Did they solve the problems raised by Fred Brooks?  Or do they just move on and "evolve"?

Besides all the process related changes that have been introduced over the pass few years, Redmond is currently caught up in a flurry activity, and has been releasing not only new versions of their standard development tools, but a slew of alpha and beta frameworks for new technologies such as Presentation Foundation, Silverlight, Entities Framework, and MVP which threaten to radically alter the playing field, and leaves developers in a quandary about whether to become early adopters — risking the investment of lots of energy for technology that may potentially never catch on (DNA and Microsoft’s DHTML come to mind) — or stick with (now) traditional windows forms and web forms development, which may potentially become obsolete.

We also face the problem of too many senior developers.  There was a time when the .NET bubble drove all companies to promote developers rapidly in order to keep them, a tendency that kept expectations high and that did not really end with the collapse of the bubble.  Along with this, companies set the standard for senior developers, generally the highest level developers can attain short of management, as someone with ten years of development experience, a standard which, given the compact time frames of the IT industry, must have seemed a long way off.  But now we have lots of people in lots of IT departments with 10 years of experience, and they expect to be confirmed as senior developers.  Those that are already senior developers are wondering what their career path is, and management is not particularly forthcoming.

The combination of these factors means the IT population is graying but not necessarily maturing.  Management in turn is looking at ways to outsource their IT labor, under the misapprehension that IT fits into an assembly line labor model rather than a professional model in which system and business knowledge should ideally be preserved in-house.  IT, for most companies, falls on the expense side of the ledger, and one expects to find ways to make it more efficient.  The immature state of the profession, compared to medicine or teaching or even engineering, makes these efficiencies difficult to find.

Add to this an economy headed toward a recession or already in recession, and we have all the necessary ingredients for a period of deep navel gazing.  My feed reader recently picked up three, and I suspect this is only the beginning.

 

Martin Fowler’s Bliki contains a recent entry on SchoolsOfSoftwareDevelopment which deals with the problem of competing methodologies, and the Habermassian problem of agreeing on a common set of criteria upon which they may be judged.

Instead what we see is a situation where there are several schools of software development, each with its own definitions and statements of good practice. As a profession we need to recognize that multiple schools exist, and that their approaches to software development are quite different. Different to the point that what one school considers to be exemplary is considered by other schools to be incompetent. Furthermore, we don’t know which schools are right (in part because we CannotMeasureProductivity) although each school thinks of itself as right, with varying degrees of tolerance for the others.

 

A Canadian developer, D’Arcy from Winnipeg, wonders what the new Microsoft technology map entails for him.

For the last 7 years we’ve been learning the web form framework, learning the ins and outs of state management (regardless of your opinion if its good or bad), how to manage postbacks, and how to make the web bend and do summersaults based on what our needs were. And here we are again, looking at the next big paradigm shift: Webforms are still around, Rails-like frameworks are the new trend, and we have a vector based Flash-like framework that we can code in .NET. I find it funny that so many are wondering whether web forms will go away because of the new MVC framework Microsoft is developing, and *totally* ignore the bigger threat: being able to develop winform-like applications that will run on the web using Silverlight.

 

Finally, Shawn Wildermuth, the ADOGuy, has posted an existential rant on the state of the industry and the mercenary mentality on the part of management as well as labor, and the long-term implications of this trend.

In some sense we developers are part of the problem. Quitting your $75K/yr job to be hired back at $75/hr seems like a good deal, but in fact it is not a good deal for either party.  Your loyalty is to the paycheck and when you leave, the domain knowledge goes with you…

At the end of the contract you just move on, forcing you to divest in a personal stake. I miss that part of this business.  I have had more enjoyment about projects that didn’t work than all the mercenary positions I’ve ever held…

So do I have a call for action?  No. I think that domain knowledge is an important idea that both developers and companies need to address, but I don’t have a nice and tidy solution. This is a shift that I think has to happen in software development. Both sides of the table need to look long at the last five to ten years and determine if what we’re doing now is better than before.  Does it only feel better because each line of code is cheaper to produce (mostly a product of better platforms, not better coders). I hope this can change.

 

Something is in the air.  A sense of uneasiness.  A rising dissatisfaction.  An incipient awareness of mauvaise foi.  Or perhaps just a feeling that the world is about to change around us, and we fear being left behind either because we didn’t try hard enough, or because we weren’t paying attention.

Interop Forms Toolkit 2.0 Tutorial

I originally wrote the prize-winning Interop Forms article below for code project.  The prize, an XBox 360 Elite system, was pretty sweet.  Even sweeter, however, was the nod I received from the Microsoft VB Team here: http://blogs.msdn.com/vbteam/archive/2007/06/01/so-what-does-lt-comclass-gt-actually-do.aspx and here: http://blogs.msdn.com/vbteam/archive/2007/06/04/interopforms-2-0-tip-1-font-property.aspx.

The feedback from the VB Team, along with some help from Mike Dooney, led me along the right path to rework my C# templates a bit (the toolkit comes with only VB.NET templates).  The differences between the VB and C# templates highlight the fact that although the two languages are often believed to do the same thing once code is compiled to IL, this is not really the case, and in fact, when it comes to COM, VB does a much better job.

To be specific, the VB compiler creates additional code in IL to make a VB class’s events and properties visible in VB6, while the C# compiler does not. I had written the original templates with the assumption that the two compilers would write similar IL — because of this faulty assumption, the events thrown in a C# UserControl were never received in VB6. This has been corrected in the updated C# templates by including in C# the extra attributes and interfaces required to make the events visible — interfaces which the VB compiler automatically creates for you in IL.

The C# templates linked below also automatically create these interfaces for you. When you add a new event to your UserControl, you will just need to be sure to add it to the appropriate interface, as well.  The naming convention for the interfaces (one for exposed events, one for exposed properties) is simply the name of the UserControl class preceded by two underscores and one underscore, respectively. It’s a little bit more manual labor than the VB templates require — but not too much more.

From the feedback I received at code project, it is apparent that while the toolkit is a big help to developers still maintaining VB6 applications, the big gain is for FoxPro developers (really a very solid development framework) who can now squeeze a little more out of their interfaces when they need to, thanks to the Toolkit.

 

Diagram

Why use the Interop Toolkit?

A few years ago, the enterprise architects at the company I worked for came up with a central login mechanism for all the company’s applications using web services. They even provided code samples in Java, C# and VB.NET for using their new component. It was intended as a language agnostic solution. When we asked the architects what we should do with the VB6 applications that we were still supporting, the architects were nonplussed. They first provided some esoteric white papers on using SOAP with VB6, then they suggested that we upgrade all of our VB6 apps to .NET, and finally they conceded that VB6 apps simply didn’t have a place in their solution.

Had Interop Forms Toolkit 2.0 been available back then, we could have come up with an integration in under an hour. We would have simply copied the sample code into a new .NET User Control, used the Interop Toolkit to wrap it up as an ActiveX control, and then consumed the control in all of our VB6 apps.

Interop Toolkit 2.0 was just released at the beginning of May. The original Interop Toolkit already allowed VB developers to use .NET Forms in their applications. This is still in Toolkit 2.0, and appears not to have changed much.

What makes Toolkit 2.0 standout is its support for using .NET User Controls as ActiveX controls in VB6.

ActiveXControlHelpers

According to Microsoft, the toolkit is intended as part of a migration strategy for upgrading VB6 applications to .NET piece by piece. I am not sure this is how it is likely to be used, however, or even if it necessarily ought to be used in this way.

Toolkit 2.0 makes most sense as a tool that allows VB6 developers to take advantage of .NET features without being forced onto an upgrade path. Most VB6 applications that are still around obviously meet certain needs very well. Why fix something that isn’t broken?

There are times, however, when you may want to leverage .NET features in your VB6 application. For a long time your only two choices were to upgrade the whole application to .NET, or to forego the nifty new features.

Toolkit 2.0 provides a third option. Simply add the .NET feature you need as a control.

This tutorial will lead you through 1. a mock application that implements the sort of technology we would have used to solve the problem outlined above. It will also cover 2. installing the Interop Toolkit, 3. provide a reference app that gives developers the ability to use real multithreading in their VB6 apps, and finally 4. provide a how-to for integrating XAML files into VB6.

Interop for C# developers

Just as with the previous version, Interop Forms Toolkit 2.0 is geared towards VB.NET developers. The wizard, project templates and item templates that come with the Toolkit only come in VB flavors. This makes a certain amount of sense, since it will mostly be VB developers who will implement these .NET/VB6 integrations. Many developers like to work with both languages, however, and there may be integration scenarios where you need to expose pre-existing C# code to VB6.

For those cases, I’ve written the C# item template and C# project template linked above for Interop User Controls. Simply copy the project template zip file into your project templates folder (the default location is ...\My Documents\Visual Studio 2005\Templates\ProjectTemplates\Visual C#) and the item template zip file into your item templates folder (...\My Documents\Visual Studio 2005\Templates\ItemTemplates\Visual C#). I believe that these templates will only work with Visual Studio 2005, but I havn’t yet tested on older versions of Visual Studio to make sure.

For cases where you need to expose a C# Form, you can use the clever wizard and template written by Leon Langleyben, which I was able to get to work with a bit of tweaking — through no fault of Leon’s, since his add-in was written for the previous version of the Interop Toolkit (refer to the CSXamlEmbeddedForm project in the included CSharp Samples to see what the generated wrapper class should look like in C#).

Installing the Toolkit

Installing the toolkit is fairly straightforward. Navigate to the Toolkit Download Site and, of the three downloads available, run the InteropFormsInstaller.msi file. In most cases, this is all you need to do. When you open the Visual Studio.NET IDE, you should find the new templates, VB6 Interop UserControl and VB6 Interop Form Library, available when you create a new VB.NET project. Under your tools menu, you should also find a new wizard labeled “Generate InteropForm Wrapper Classes”.

If the new wizard does not appear in your tools menu, there may have been a problem installing it. Check Tools | Add-in Manager to make sure that this wizard is selected. If it is present and selected in the Add-in Manager, but still does not appear in your tools menu, you can run the following command in your command line to reset it: Devenv /resetaddin Microsoft.InteropFormTools.InteropFormProxyGenerator.Connect.

Installing the Toolkit on Vista

In order to use the Toolkit on Windows Vista, you will need to download both the msi file as well as the setup file to your harddrive. Then run setup. Running the msi file alone will generate an install error.

To use the C# UserControl templates on Vista, you will need to run Visual Studio as an Administrator. Right-click on the link to your Visual Studio IDE and select the Run as administrator popup menu option. This will let Vista’s UAC feature know that it is alright for the UserControl to write to the registry on build events.

Building a User Control

In this first example, the UserControl will take care of all the processing, and will just sit in the VB6 Form, while the example following it will demonstrate how to pass information between VB6 and VB.NET. Any code snippets will be in VB, though the source code samples linked above include both a VB.NET as well as a C# sample of the control.

In this example we use the Daily Dilbert web service to download a cartoon to our control (you will notice that the Daily Dilbert comes up a lot on CodeProject — for good reason; it happens to be one of the few interesting web services that can actually be used in public tutorials, since it does not require a fee or registration).

Open new project

Begin by creating a new VB6 Interop User Control project using the VB6 Interop UserControl project template. Name the project DailyDilbertControl. By default, a UserControl file is created called InteropUserControl.vb. Since this is the control name that will be displayed in your VB6 control panel, you should rename it to DilbertService.vb, to be more descriptive.

In your new project, you will find the following files: ActiveXControlHelpers.vb, InteropUserControl.bmp, InteropUserControl.manifest. ActiveXControlHelpers.vb, as the name suggests, includes several static helper methods that make the conversion of your UserControl into an ActiveX control possible. There are register and unregister methods, which add details about your control to the registry. There are methods that help convert things like color codes between the .NET scheme and the OLE scheme used in VB6. There is also a method that wires up your events to work with VB6. You can have multiple user controls in your project, but should only have one ActiveXControlHelpers file per project.

The InteropUserControl.bmp is used to display your control in the VB6 Toolbox. I will cover how to customize your ActiveX control image later in this tutorial.

Add a new web reference to the Daily Dilbert web service to your project. To do this, right click on your project and select Add Web Reference… A new dialog will pop up. Enter http://www.esynaps.com/WebServices/DailyDiblert.asmx for the URL and click on Go. Finally, rename the web reference to “DDService” and select Add Reference at the lower right of the dialog. Your Solution Explorer should now look like this.

AddWebReference 

Add a 650 by 215 pixel PictureBox called DilbertPictureBox to your UserControl, as well as a button called RetrieveButton. Create a new RetrieveButton_Click event handler by double clicking on RetrieveButton. Paste in the following code, which will call the web service and retrieve today’s Dilbert strip.

    Private Sub RetrieveButton_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles RetrieveButton.Click
        Dim myDilbert As New DDService.DailyDilbert()
        Dim DilbertMemoryStream As _
        New System.IO.MemoryStream(myDilbert.DailyDilbertImage())
        With Me.DilbertPictureBox
            .Image = Image.FromStream(DilbertMemoryStream)
            .BorderStyle = BorderStyle.Fixed3D
        End With
    End Sub

To finish making your control visible from VB6, just press F5 to preview the control, or simply build it. It will look like this in your Visual Studio UserControl Test Container:

DilbertControlPreview 

And that’s all it takes to build build an ActiveX control in Visual Studio.NET.

Adding an ActiveX Control Image

When you build a new control, InteropUserControl.bmp will be used as the default image for your component in the VB6 Toolbar. You can always use a different image, though. For this project, I want to use this image of Dilbert.

Dilbert

To add it, you first have to add the bitmap file you want to use to your project and set its build property to “content”. Now open the InteropUserControl.rc resource script file with notepad. DO NOT use Visual Studio to do this, as this will mess up your resource script file. InteropUserControl.rc can be found under the DailyDilbertControl project folder. Beneath the default 101 BITMAP InteropUserControl.bmp entry, add an additional entry specifying the custom bitmap you want to use.

changeresourcescript 

Save your resource script file. Now open ActiveXHelpers.vb and find the RegisterControl method. This is where registry entries are created. In the section where the bitmap file is specified, replace the default entry, “101”, with a reference to your own bitmap.

replacebitmap

Now rebuild your control to make sure a new compiled resource file is created. The new image should appear in the VB6 Toolbox rather than the default image.

vb6toolbar

More about adding ActiveX Control Images

You can use a different image for each UserControl in your project, but you will have to modify the ActiveXControlHelpers.vb file a bit to make this work. Alter the RegisterControl method signature to take a second string parameter, and then pass this parameter to the line where the code specifies the resource id of the image.

    Public Sub RegisterControl(ByVal t As Type, ByVal BitmapId As String)

    '...
    
    'ToolBoxBitmap32
    Using bitmapKey As RegistryKey = subkey.CreateSubKey("ToolBoxBitmap32")
    bitmapKey.SetValue("", Assembly.GetExecutingAssembly.Location & ", "  _
    & BitmapId, RegistryValueKind.String)
    End Using
    
    '...
    
    End Sub

Then, in each UserControl, add the BitmapId you want to use for your control to the RegisterControl call.

    <EditorBrowsable(EditorBrowsableState.Never)> _
    <ComRegisterFunction()> _
    Private Shared Sub Register(ByVal t As Type)
        ComRegistration.RegisterControl(t, "102")
    End Sub

Rebuild the entire project once more.

Adding the UserControl to a VB6 project

DailyDilbertComponent 

This is actually the easiest part. Create a new VB6 project. Press CTRL+T to add a new component to your form, and check the DailyDilbertControl library. Press OK. (Vista behaves a bit strangely when you try to add your ActiveX control. It will occassionally throw an error the first time you select OK, then will work normally the second time you do so. Just to be safe, click Apply first just to see if there is an error, and then OK.) Any UserControls in your project will now appear on the VB Toolbar. Simply select the control you want to use and draw it onto your VB6 form. Press F5 to see your .NET UserControl run in a Visual Basic 6 application.

vb6form1 

Adding True Multithreading to VB6

When I was working with VB6 on a regular basis, one of the main impetuses for upgrading to .NET was the ability to implement multithreading. Interop UserControls provide an easy way to add true multithreading to a VB6 application. In a common scenario, you may want the users of your VB app to be able to kick off a process and then continue with their work while the processing occurs in the background. To simulate this scenario, the reference code we are about to build will use a BackgroundWorker control that will perform a time-consuming process in the background while updating a progress bar. In the meantime, the users of the VB6 application that consumes the control can continue with their work.

Create a new VB6 Interop UserControl project called MultithreadedControl. Add a BackgroundWorker control named BackgroundWorker1, a label called LabelWarningMessage and ProgressBar called ProgressBar1. Paste in the following code.

    Public Delegate Sub StartEventHandler(ByVal simpleEventText As String)
    Public Delegate Sub FinishAsyncEventHandler(ByVal asyncEventText As String)

    Public Event StartEvent As StartEventHandler
    Public Event FinishAsyncEvent As FinishAsyncEventHandler

    Public Sub StartProcessing()
        Try
            RaiseEvent StartEvent(".NET process starting")
            Me.BackgroundWorker1.RunWorkerAsync()
        Catch
        End Try
    End Sub

    Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, _
    ByVal e As System.ComponentModel.DoWorkEventArgs) _
    Handles BackgroundWorker1.DoWork
        'wait
        Static prog As Integer = 0
        While (prog < 100)
            System.Threading.Thread.Sleep(50)
            prog = prog + 2
            Me.BackgroundWorker1.ReportProgress(prog)
        End While
        prog = 0
    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As System.Object, _
    ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
    Handles BackgroundWorker1.ProgressChanged
        Me.LabelWarningMessage.ForegroundColor = Color.Red
        Me.LabelWarningMessage.Text = "Working in background..."
        Me.LabelWarningMessage.Visible = True
        Me.ProgressBar1.Value = e.ProgressPercentage
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, _
    ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
    Handles BackgroundWorker1.RunWorkerCompleted
        Me.LabelWarningMessage.Visible = False
        RaiseEvent FinishAsyncEvent("Interop User Control process finished.")
    End Sub
    
    Private Sub BackgroundWorker_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load
        Me.ProgressBar1.Value = 0
        Me.LabelWarningMessage.Visible = False
    End Sub

Finally, make sure that the BackgroundWorker events are hooked up to the handlers we’ve written.

backgroundworkerevents 

Also make certain that the BackgroundWorker’s WorkerReportsProgress property is set to true. Build the project.

Open a new VB6 project and add the MultithreadedControl component to your VB6 Form, as you did in the previous example. Also add a multiline TextBox control (Text1), a ListBox (List1), and a CommandButton labeled “Process” (Command1).

In order to receive events from the BackgroundWorker control, you will need to add an additional reference to the control. Click on the menu item Project | References… A reference to MultithreadedControlCtrl will already be checked off from previously adding it as a component. You will now need to also include a reference to the library MultithreadedControl in order to capture events thrown from the .NET Control.

MultithreadedControlReference 

Finally, paste in the following VB6 code. In this code, you declare a new reference to the control, this time decorated with the keyword “WithEvents” in order to expose the control’s events. Wiring up handlers for the events is based only on the names of the procedures, so you have to be careful when typing out the Sub routines that will be used in this way.

Before the underscore, always use the same name you used when you created the second reference (in this case “BackgroundEvents”). Then after the underscore, use the actual event name as it appears in your original .NET control. I’ve seen lots of problems posted to various message boards concerning problems with VB Interop event handling that basically came down to misspelling a handler’s signature — so be careful.

Dim WithEvents BackgroundEvents As MultithreadedControl.BackgroundWorker

Private Sub Command1_Click()
    Me.Text1.Text = ""
    Me.List1.Clear
    Me.List1.AddItem ("Start processing from VB6: " & DateTime.Now)
    Me.BackgroundWorker1.StartProcessing
End Sub

Private Sub Form_Load()
    Set BackgroundEvents = Me.BackgroundWorker1
End Sub

Private Sub BackgroundEvents_StartEvent(ByVal EventText As String)
    Me.List1.AddItem (EventText)
End Sub

Private Sub BackgroundEvents_FinishAsyncEvent(ByVal EventText As String)
    Me.List1.AddItem (EventText)
    Me.List1.AddItem ("Finish processing from VB6:" & DateTime.Now)
End Sub

This reference app basically demonstrates how a .NET BackgroundWorker can be used inside a VB6 application. Even while the processing is occurring, and updating the status bar to let us know how far it has gotten, the end-user can continue typing into the textbox. If you have never programmed in VB6, then this probably seems like a trivial accomplishment.

For those of us who have worked on Visual Basic 6 apps for large portions of our careers, it is a breakthrough.

VB6Multithreaded

Using XAML in VB6

You cannot build a XAML UserControl or a XAML Form and then consume it directly in VB6, unfortunately. You also cannot simply add a XAML Form to a Windows Application project and expose it that way. With a bit of finesse, what you can do is embed a XAML UserControl in a Windows Form and consume that in your VB6 apps. The following walkthrough will show you how.

Create a new VB6 InteropForm Library Project in Visual Studio, and call it XamlEmbeddedForm. Rename the default Windows Form to XamlForm. Now add a second project based on the .NET Framework 3.0 Custom Control Library (WPF) project template and call it XamlUserControl. You can add whatever XAML code you like, at this point. For the reference project, I’ve used the Cube Animation code found in the WPF SDK. Build the XamlUserControl project. In XamlEmbeddedForm, add a reference to the UserControl project. Also add the following four library references: PresentationCore, PresentationFramework, WindowsBase and WindowsFormsIntegration.

NewReferences 

You now will need to add some code to the FormLoad event in order to host the XAML UserControl in your .NET Form. The complete code behind should look like this:

Imports Microsoft.InteropFormTools
Imports System.Windows.Forms.Integration
Imports System.ComponentModel
Imports System.Windows.Forms

<InteropForm()> _
Public Class XamlForm

    Private Sub XamlForm_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) _
        Handles MyBase.Load
        ' Create the ElementHost control for hosting the
        ' WPF UserControl.
        Dim host As New ElementHost()
        host.Dock = DockStyle.Fill

        ' Create the WPF UserControl.
        Dim uc As New XamlUserControl.UserControl1()

        ' Assign the WPF UserControl to the ElementHost
        '  control's Child property.
        host.Child = uc

        ' Add the ElementHost control to the form's
        ' collection of child controls.
        Me.Controls.Add(host)
    End Sub
End Class

Rebuild your solution one more time for good measure. Then go to the Tools menu and select Generate InteropForm Wrapper Classes (if this menu option is missing, refer to the installation instructions above). This will add a new wrapper class to your project that can be exposed to VB6. Rebuild one last time to register your wrapper class in the registry. At this point, your .NET code is complete.

Open a new project in VB6. Open Project | References and check two items to add them to your VB6 project: Microsoft Interop Forms Toolkit Library as well as your .NET project, XAMLEmbeddedForm to add to COM wrapper for your .NET assembly to your VB6 project.

.NET Interop Forms have difficulty knowing when the host VB6 application starts and stops, so some extra code must be added to your VB6 application to handle this. Add the following code snippet to your VB6 Main Form so the .NET code is informed when these events occur.

Public g_InteropToolbox As InteropToolbox

Private Sub Form_Load()
    Set g_InteropToolbox = New InteropToolbox
    g_InteropToolbox.Initialize
    g_InteropToolbox.EventMessenger.RaiseApplicationStartedupEvent
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    g_InteropToolbox.EventMessenger.RaiseApplicationShutdownEvent
End Sub

We are nearly done. To open your .NET Form from VB6, just add a command button to your Main Form and handle its click event with the following code.

Private Sub Command1_Click()
Dim xaml As New XamlEmbeddedForm.XamlForm
    xaml.Show vbModal
End Sub

If everything goes right, when you click the button, you should see an animated cube, written all in XAML.

EmbeddedForm

Conclusion

This tutorial is intended to walk you through the steps needed to create a useful .NET/VB6 integration. It is also intended to give you a sense of the almost limitless possibilities that are open to VB6 developers now that this technology has been made widely available. Half a decade after people were foretelling the doom of VB6 as a development tool, we should come to terms with the idea that VB6 will still be with us for quite a while longer. Interop Toolkit 2.0 ensures that the many years left to VB6 development will be both graceful and productive.

sassy.net: Fall Fashions for .NET Programmers

janerussell

This fall programmers are going to be a little more sassy.  Whereas in the past, trendy branding has involved concepts such as paradigms, patterns and rails, principles such as object-oriented programming, data-driven programming, test-driven programming and model-driven architecture, or tags like web 2.0, web 3.0, e-, i-, xtreme and agile, the new fall line features “alternative” and the prefix of choice: alt-.  The point of this is that programmers who work with Microsoft technologies no longer have to do things the Microsoft way.  Instead, they can do things the “Alternative” way, rather than the “Mainstream” way.  In the concrete, this seems to involve using a lot of open source frameworks like NHibernate that have been ported over from Java … but why quibble when we are on the cusp of a new age.

Personally I think sassy.net is more descriptive, but the alt.net moniker has been cemented by the October 5th alt.net conference.  David Laribee is credited with coining the term earlier this year in this blog post, as well as explicating it in the following way:

What does it mean to be to be ALT.NET? In short it signifies:

  1. You’re the type of developer who uses what works while keeping an eye out for a better way.
  2. You reach outside the mainstream to adopt the best of any community: Open Source, Agile, Java, Ruby, etc.
  3. You’re not content with the status quo. Things can always be better expressed, more elegant and simple, more mutable, higher quality, etc.
  4. You know tools are great, but they only take you so far. It’s the principles and knowledge that really matter. The best tools are those that embed the knowledge and encourage the principles (e.g. Resharper.)

This is almost identical to my manifesto for sassy.net, except that I included a fifth item about carbon neutrality and a sixth one about loving puppies.  To Dave’s credit, his manifesto is a bit more succinct.

There are a several historical influences on this new fall line.  One is the suspicion that new Microsoft technologies have been driven by a desire to sell their programming frameworks rather than to create good tools.  An analogy can be drawn with the development of the QWERTY standard for the English-language keyboard.  Why are the keys laid out the way the are?  One likely possibility is that all the keys required to spell out “t-y-p-e-w-r-i-t-e-r” can be found on the top row, which is very convenient for your typical typewriter salesman.  Several of the RAD (Rapid Application Development — an older fall line that is treated with a level of contempt some people reserve for Capri pants) tools that have come out of Microsoft over the past few years have tended to have a similar quality.  They are good for sales presentations but are not particularly useful for real world development.  Examples that come to mind are the call-back event model for .NET Remoting (the official Microsoft code samples didn’t actually work) and the MSDataSetGenerator, which is great for quickly building a data layer for an existing database, and is almost impossible to tweak or customize for even mildly complex business scenarios.

A second influence is java-envy.  Whereas the java development tools have always emphasized complex architectures and a low-level knowledge of the language, Microsoft development tools have always emphasized fast results and abstracting the low-level details of their language so the developer can get on with his job.  This has meant that while Java projects can take up to two years, after which you are lucky if you have a working code base, Microsoft-based projects are typically up and running in under six months.  You would think that this would make the Microsoft solution the one people want to work with, but in fact, among developers, it has created Java-envy.  The Java developers are doing a lot of denken work, making them a sort of aristocracy in the coding world, whereas the Microsoft programmers are more or less laborers for whom Microsoft has done much of the thinking.

Within the Microsoft world, itself, this class distinction has created a sort of mass-migration from VB to C#; these are for the most part equivalent languages, yet VB still has the lingering scent of earth and toil about it.  There are in fact even developers who refuse to use C#, which they see as a still bit prole, and instead prefer to use managed C++.  Whatever, right?

In 2005, this class distinction became codified with the coining of the term Mort, used by Java developers to describe Microsoft developers, and C# developers to describe VB developers, and by VB.NET developers to describe their atavistic VB6 cousins.  You can think of the Morts as Eloi, happily pumping out applications for their businesses, while the much more clever Morlocks plan out coding architectures and frameworks for the next hundred years.  The alt.net movement grows out of the Morlocks, rather than the Morts, and can in turn be sub-divided between those who simply want to distinguish themselves from the mid-level developers, and those who want to work on betterment projects using coding standards and code reviews to bring the Morts up to their own level. (To be fair, most of the alt.net crowd are of the latter variety, rather than the former.)  The alt.net movement sees following Microsoft standards as a sort of serfdom, and would prefer to come up with their own best-practices, and in some cases tools, for building Microsoft-based software.

The third influence on the formation of the alt.net movement is the trend in off-shoring software development.  Off-shoring is based on the philosophy that one piece of software development work is equivalent to another, and implicitly that for a given software requirement, one developer is equivalent to another, given that they know the same technology.  The only difference worth considering, then, is how much money one must spend in order to realize that software requirement.

This has generated a certain amount of soul-searching among developers.  Previously, they had subscribed to the same philosophy, since their usefulness was based on the notion that a piece of software, and implicitly a developer, could do the same work that a roomful of filers (or any other white-collar employee) could do more quickly, more efficiently and hence more cheaply.

Off-shoring challenged this self-justification for software developer, and created in its place a new identity politics for developers.  A good developer, now, is not to be judged on what he knows at a given moment in time — that is he should not be judged on his current productivity — but rather on his potential productivity — his ability to generate better architectures, more elegant solutions, and other better things over the long that cannot be easily measured, run.  In other words, third-world developers will always be Morts.  If you want high-end software, you need first-world solutions architects and senior developers. 

To solidify this distinction, however, it is necessary to have some sort of certifying mechanism that will clearly distinguish elite developers from mere Mort wannabes.  At this point, the distinction is only self-selecting, and depends on true alt.net developers being able to talk the talk (as well as determining what the talk is going to be).  Who knows, however, what the future may hold.

Some mention should also be made concerning the new fall fashions.  Fifties skirts are back in, and the Grace Kelly look will be prevalent.  Whereas last year saw narrow bottom jeans displacing bell bottoms, for this fall anything goes.  This fall we can once again start mixing colors and patterns, rather than stick to a uniform color for an outfit.  This will make accessorizing much more interesting, though you may find yourself spending more time picking out clothes in the morning, since there are now so many more options.  Finally, V-necks are back.  Scoop-necks are out.

In men’s fashion, making this the fifteenth year in a row, golf shirts and khakis are in.

Coding is not a Spectator Sport

matrix

 

This past Tuesday I attended a Microsoft technology event at a local movie theater.  Ever since the Matrix, movie theaters are apparently the convenient place to go to get technology updates these days.  If you’ve never been to one of these events, they involve a presenter or two with a laptop connected to the largest movie screen in the building.  The presenters then promote new Microsoft offerings by writing code on the big screen while software programmers who have somehow gotten the afternoon off from their bosses watch on.

Jim Wooley presented on LINQ, while a Microsoft employee presented on WCF.  The technologies looked pretty cool, but the presentations were rather dull.  I don’t think this was really the fault of the presenters, though.  The truth is, watching other people code is a bit like watching paint dry, and seems to take longer.  Perhaps this is why pair programming, one of the pillars of extreme programming, has never caught on (failing to document your code, however, another pillar of extreme programming, has been widely adopted and, like Monsieur Jourdain, many developers have found that they’d been doing XP for years without even realizing it). 

Within these constraints — that is that you are basically doing the equivalent of demonstrating how to hammer nails into a board for four hours — the presenters did pretty well, although Mr. Wooley appeared to be somewhat nervous and kept insisting he was doing “Extreme Presenting” whenever he made a coding mistake and the greediest members of the audience would compete with one another to point out his failings.  The Microsoft presenter didn’t encounter any compile errors like Mr. Wooley did, but on the other hand he was following a script and kept referring to it as he typed the code that we were all watching.  Why you should need a script to write uninteresting demo code that ultimately just emits “Hello, world” messages is beyond me, but that’s what he did, and he demonstrated that there could be something even less able to hold the attention than watching someone write code — watching someone write code by rote.

But it is easy to criticize, and in truth I never got to see the presentation on Silverlight given by Shawn Wildermuth (aka “adoguy”), which for all I know may have been much more entertaining and might have undermined my mantra that coding is not a spectator sport, but I’ll never know because I had to skip out on it in order to attend a company dinner.  How I got invited to this dinner I’ll never know, because I wasn’t really very involved in the project that the dinner was intended to celebrate.

I arrived fashionably late by an hour, and as I entered I realized the only seat left was squeezed in between my manager, the CFO of the company and the Senior VP of IT.  This is a dreadful spot to be in, and into that spot I deposited myself.  The problem with being situated next to one’s uppers at a social event is that one spends an inordinate amount of time trying to think of something to say that will impress one’s uppers, while simultaneously trying to avoid saying anything to demonstrate one’s utter unfitness for one’s position.  And here I was next to my boss, who was sitting across from his boss, who was sitting across from his boss.  And as I sat, watching what appeared to be scintillating conversation at the opposite end of the table, my end was completely silent with an air of tension about it.

So I picked up a menu and tried to order.  This was a steak and seafood restaurant, and judging by the prices, approximately twice as good as Longhorn or Outback.  I took the highest priced item, divided the cost by half, and ordered the crawfish pasta with a glass of wine.  Then I sat back to listen to the silence.  Finally someone struck up a conversation about insurance (my industry).  If you want to know how dreadfully dull insurance talk is, it’s a bit like — actually, there is nothing as boring as insurance talk because it is the sine qua non against which all boredom should be judged.  Listening to insurance talk is the sort of thing that makes you want to start cutting yourself for distraction (it’s an old POW trick), and just as I was reaching for the butter knife I found myself telling the jazz story.

The jazz story went over well and seemed to break the ice, so I followed it up with the Berlin mussels story, which was also a hit.  I drank more wine and felt like I was really on a roll.  I’d demonstrated my ability to talk entertainingly around my bosses and as the food arrived I was able to maintain the mood with a jaunty disquisition on men’s fashion and how to select a good hunting dog.  But I grew overconfident.  Over dessert, I decided to play the teacup game, which is a conversation game my friend Conrad at The Varieties had taught me, and it was a disaster.  Apparently I set it up wrong, because a look of disgust formed on the CFO’s face.  My manager tried to save with a distracting story about hygiene, but rather than leave things well enough alone, I decided to continue with the asparagus story, and pretty well ruined the evening.  Oh well.  Bye-bye annual bonus.

Which all goes to show, entertainment is a damnably difficult business.

eddie

I can probably improve my dinner conversation by reading a bit more P.G. Wodehouse and bit less of The New Yorker (which is where I got the fateful asparagus story) but how to improve a Microsoft presentation is a much trickier nut to crack.  How much can you realistically do to dress up watching other people code?

Then again, it is amazing what passes for a spectator sport these days, from Lumberjack Olympics to Dancing with the Stars.  Perhaps one of the strangest cultural trends is the popularity of poker as a spectator sport — something that would have seemed unimaginable back in the day.  The whole thing revolves around a handful of people dressed up in odd combinations of wigs, sunglasses and baseball caps to hide their tells playing a card game that depends largely on luck, partly on a grasp of probabilities, and partly on being able to guess what your opponents are guessing about you.  Is there anything in this jumble of crazy costumes, luck and skill that can be used to improve a typical Microsoft presentation?

The truth is, even skill isn’t so important in creating a successful spectator sport.  Take quiz shows, which once were devoted to very tough questions that left the audience wondering how the contestants could know so much (it turned out, of course, that often they were cheating).  Over time, these shows became simpler and simpler, until we ended up with shows like Are You Smarter Than a 5th Grader (which makes you wonder how they find contestants so dumb) and the very successful Wheel of Fortune (in which you are challenged to list all the letters of the alphabet until a hidden message becomes legible).  Demonstrating skill is not the essence of these games.

If you have ever seen National Lampoon’s Vegas Vacation (fourth in the series, but my personal favorite), you will recall the scene where, after loosing a large portion of his life savings at a casino, Chevy Chase is taken by his cousin Eddie to a special place with some non-traditional games of luck such as rock-paper-scissors, what-card-am-I-holding, and pick-a-number-between-one-and-ten.  This, it turns out, is actually the premise of one of the most popular American game shows of the year, Deal Or No Deal, hosted by the failed-comedian-actor-turned-gameshow-host Howie Mandel.  The point of this game is to pick a number between one and twenty-six, which has a one in twenty-six chance of being worth a million dollars.  The beauty of the game is that the quick and the slow, the clever and the dim, all have an equal chance of winning.  The game is a great leveler, and the apparent pleasure for the audience is in seeing how the contestants squirm.

I had initially thought that Mr. Wooley’s palpable nervousness detracted from his presentation, but the more I think about it, the more I am convinced that his error was in not being nervous enough.  The problem with the format of Microsoft presentations is that there is not enough at stake.   A presenter may suffer the indignity of having people point out his coding errors on stage or of having bloggers ask why he needs a script to write a simple demo app — but at the end of the day there are no clear stakes, no clear winners, no clear losers.

The secret of the modern spectator sport — and what makes it fascinating to watch — is that it is primarily about moving money around.  Televised poker, Survivor-style Reality shows, and TV game shows are all successful because they deal with large sums of money and give us an opportunity to see what people will do for it.  Perhaps at some low level, it even succeeds at distracting us from what we are obliged to do for money.

And money is the secret ingredient that would liven up these perfunctory Microsoft events.  One could set a timer for each code demonstration, and oblige the presenter to finish his code — making sure it both compiles and passes automated unit tests — in the prescribed period in order to win a set sum of money.  Even better, audience members can be allowed to compete against the official Microsoft presenters for the prize money.  Imagine the excitement this would generate, the unhelpful hints from the audience members to the competitors, the jeering, the side-bets, the tension, the drama, the spectacle.  Imagine how much more enjoyable these events would be.

Microsoft events are not the only places where money could liven things up, either.  What if winning a televised presidential debate could free up additional dollars to presidential candidates?  What if, along with answering policy questions, we threw in geography and world event questions with prize money attached?  Ratings for our presidential debates might even surpass the ratings for Deal Or No Deal.

Academia would also be a wonderful place to use money as a motivator.  Henry Kissinger is reported to have said that academic battles are so vicious because the stakes are so low.  Imagine how much more vicious we could make them if we suddenly raised the stakes, offering cash incentives for crushing intellectual blows against one’s enemies in the pages of the Journal of the History of Philosophy, or a thousand dollars for each undergraduate ego one destroys with a comment on a term paper.  Up till now, of course, academics have always been willing to do this sort of thing gratis, but consider how much more civilized, and how clearer the motives would be, if we simply injected money into these common occurrences.

Do Computers Read Electric Books?

In the comments section of a blog I like to frequent, I have been pointed to an article in the International Herald about Pierre Bayard’s new book,  How to Talk About Books You Haven’t Read.

Bayard recommends strategies such as abstractly praising the book, offering silent empathy regarding someone else’s love for the book, discussing other books related to the book in question, and finally simply talking about oneself.  Additionally, one can usually glean enough information from reviews, book jackets and gossip to sustain the discussion for quite a while.

Students, he noted from experience, are skilled at opining about books they have not read, building on elements he may have provided them in a lecture. This approach can also work in the more exposed arena of social gatherings: the book’s cover, reviews and other public reaction to it, gossip about the author and even the ongoing conversation can all provide food for sounding informed.

I’ve recently been looking through some AI experiments built on language scripts, based on the 1966 software program Eliza, which used a small script of canned questions to maintain a conversation with computer users.  You can play a web version of Eliza here, if you wish.  It should be pointed out that the principles behind Eliza are the same as those that underpin the famous Turing Test.  Turing proposed answering the question can machines think by staging an ongoing experiment to see if machines can imitate thinking.  The proposal was made in his 1950 paper Computing Machinery and Intelligence:

The new form of the problem can be described in terms of a game which we call the ‘imitation game.” It is played with three people, a man (A), a woman (B), and an interrogator (C) who may be of either sex. The interrogator stays in a room apart front the other two. The object of the game for the interrogator is to determine which of the other two is the man and which is the woman. He knows them by labels X and Y, and at the end of the game he says either “X is A and Y is B” or “X is B and Y is A.” The interrogator is allowed to put questions to A and B thus:

C: Will X please tell me the length of his or her hair?

Now suppose X is actually A, then A must answer. It is A’s object in the game to try and cause C to make the wrong identification. His answer might therefore be:

“My hair is shingled, and the longest strands are about nine inches long.”

In order that tones of voice may not help the interrogator the answers should be written, or better still, typewritten. The ideal arrangement is to have a teleprinter communicating between the two rooms. Alternatively the question and answers can be repeated by an intermediary. The object of the game for the third player (B) is to help the interrogator. The best strategy for her is probably to give truthful answers. She can add such things as “I am the woman, don’t listen to him!” to her answers, but it will avail nothing as the man can make similar remarks.

We now ask the question, “What will happen when a machine takes the part of A in this game?” Will the interrogator decide wrongly as often when the game is played like this as he does when the game is played between a man and a woman? These questions replace our original, “Can machines think?”

The standard form of the current Turing experiments is something called a chatterbox application.  Chatterboxes abstract the mechanism for generating dialog from the dialog scripts themselves by utilizing a set of rules written in a common format.  The most popular format happens to be an XML standard called AIML (Artificial Intelligence Markup Language).

What I’m interested in, at the moment, is not so much whether I can write a script that will fool people into thinking they are talking with a real person, but rather whether I can write a script that makes small talk by discussing the latest book.  If I can do this, it should validate Pierre Bayard’s proposal, if not Alan Turing’s.

Speech Recognition And Synthesis Managed APIs In Windows Vista: Part II


Playing with the speech synthesizer is a lot of fun for about five minutes (ten if you have both Microsoft Anna and Microsoft Lila to work with)  — but after typing “Hello World” into your Speechpad document for the umpteenth time, you may want to do something a bit more challenging.  If you do, then it is time to plug in your expensive microphone, since speech recognition really works best with a good expensive microphone.  If you don’t have one, however, then go ahead and plug in a cheap microphone.  My cheap microphone seems to work fine.  If you don’t have a cheap microphone, either, I have heard that you can take a speaker and plug it into the mic jack of your computer, and if that doesn’t cause an explosion, you can try talking into it.


While speech synthesis may be useful for certain specialized applications, voice commands, by cantrast, are a feature that can be used to enrich any current WinForms application. With the SR Managed API, it is also easy to implement once you understand certain concepts such as the Grammar class and the SpeechRecognitionEngine.


We will begin by declaring a local instance of the speech engine and initializing it. 

	#region Local Members

private SpeechSynthesizer synthesizer = null;
private string selectedVoice = string.Empty;
private SpeechRecognitionEngine recognizer = null;

#endregion

public Main()
{
InitializeComponent();
synthesizer = new SpeechSynthesizer();
LoadSelectVoiceMenu();
recognizer = new SpeechRecognitionEngine();
InitializeSpeechRecognitionEngine();
}

private void InitializeSpeechRecognitionEngine()
{
recognizer.SetInputToDefaultAudioDevice();
Grammar customGrammar = CreateCustomGrammar();
recognizer.UnloadAllGrammars();
recognizer.LoadGrammar(customGrammar);
recognizer.SpeechRecognized +=
new EventHandler<SpeechRecognizedEventArgs>(recognizer_SpeechRecognized);
recognizer.SpeechHypothesized +=
new EventHandler<SpeechHypothesizedEventArgs>(recognizer_SpeechHypothesized);
}

private Grammar CreateCustomGrammar()
{
GrammarBuilder grammarBuilder = new GrammarBuilder();
grammarBuilder.Append(new Choices(“cut”, “copy”, “paste”, “delete”));
return new Grammar(grammarBuilder);
}


The speech recognition engine is the main workhorse of the speech recognition functionality.  At one end, we configure the input device that the engine will listen on.  In this case, we use the default device (whatever you have plugged in), though we can also select other inputs, such as specific wave files.  At the other end, we capture two events thrown by our speech recognition engine.  As the engine attempts to interpret the incoming sound stream, it will throw various “hypotheses” about what it thinks is the correct rendering of the speech input.  When it finally determines the correct value, and matches it to a value in the associated grammar objects, it throws a speech recognized event, rather than a speech hypothesized event.  If the determined word or phrase does not have a match in any associated grammar, a speech recognition rejected event (which we do not use in the present project) will be thrown instead.


In between, we set up rules to determine which words and phrases will throw a speech recognized event by configuring a Grammar object and associating it with our instance of the speech recognition engine.  In the sample code above, we configure a very simple rule which states that a speech recognized event will be thrown if any of the following words: “cut“, “copy“, “paste“, and “delete“, is uttered.  Note that we use a GrammarBuilder class to construct our custom grammar, and that the syntax of the GrammarBuilder class closely resembles the syntax of the StringBuilder class.


This is the basic code for enabling voice commands for a WinForms application.  We will now enhance the Speechpad application by adding a menu item to turn speech recognition on and off,  a status bar so we can watch as the speech recognition engine interprets our words, and a function that will determine what action to take if one of our key words is captured by the engine.


Add a new menu item labeled “Speech Recognition” under the “Speech” menu item, below “Read Selected Text” and “Read Document”.  For convenience, name it speechRecognitionMenuItem.  Add a handler to the new menu item, and use the following code to turn speech recognition on and off, as well as toggle the speech recognition menu item.  Besides the RecognizeAsync() method that we use here, it is also possible to start the engine synchronously or, by passing it a RecognizeMode.Single parameter, cause the engine to stop after the first phrase it recognizes. The method we use to stop the engine, RecognizeAsyncStop(), is basically a polite way to stop the engine, since it will wait for the engine to finish any phrases it is currently processing before quitting. An impolite method, RecognizeAsyncCancel(), is also available — to be used in emergency situations, perhaps.

        private void speechRecognitionMenuItem_Click(object sender, EventArgs e)
{
if (this.speechRecognitionMenuItem.Checked)
{
TurnSpeechRecognitionOff();
}
else
{
TurnSpeechRecognitionOn();
}
}

private void TurnSpeechRecognitionOn()
{
recognizer.RecognizeAsync(RecognizeMode.Multiple);
this.speechRecognitionMenuItem.Checked = true;
}

private void TurnSpeechRecognitionOff()
{
if (recognizer != null)
{
recognizer.RecognizeAsyncStop();
this.speechRecognitionMenuItem.Checked = false;
}
}


We are actually going to use the RecognizeAsyncCancel() method now, since there is an emergency situation. The speech synthesizer, it turns out, cannot operate if the speech recognizer is still running. To get around this, we will need to disable the speech recognizer at the last possible moment, and then reactivate it once the synthesizer has completed its tasks. We will modify the ReadAloud() method to handle this.


private void ReadAloud(string speakText)
{
try
{
SetVoice();
recognizer.RecognizeAsyncCancel();
synthesizer.Speak(speakText);
recognizer.RecognizeAsync(RecognizeMode.Multiple);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

}

The user now has the ability to turn speech recognition on and off. We can make the application more interesting by capturing the speech hypothesize event and displaying the results to a status bar on the Main form.  Add a StatusStrip control to the Main form, and a ToolStripStatusLabel to the StatusStrip with its Spring property set to true.  For convenience, call this label toolStripStatusLabel1.  Use the following code to handle the speech hypothesized event and display the results:

private void recognizer_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
{
GuessText(e.Result.Text);
}

private void GuessText(string guess)
{
toolStripStatusLabel1.Text = guess;
this.toolStripStatusLabel1.ForeColor = Color.DarkSalmon;
}


Now that we can turn speech recognition on and off, as well as capture misinterpretations of the input stream, it is time to capture the speech recognized event and do something with it.  The SpeechToAction() method will evaluate the recognized text and then call the appropriate method in the child form (these methods are accessible because we scoped them internal in the Textpad code above).  In addition, we display the recognized text in the status bar, just as we did with hypothesized text, but in a different color in order to distinguish the two events.


private void recognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
string text = e.Result.Text;
SpeechToAction(text);
}

private void SpeechToAction(string text)
{
TextDocument document = ActiveMdiChild as TextDocument;
if (document != null)
{
DetermineText(text);

switch (text)
{
case “cut”:
document.Cut();
break;
case “copy”:
document.Copy();
break;
case “paste”:
document.Paste();
break;
case “delete”:
document.Delete();
break;
}
}
}

private void DetermineText(string text)
{
this.toolStripStatusLabel1.Text = text;
this.toolStripStatusLabel1.ForeColor = Color.SteelBlue;
}


Now let’s take Speechpad for a spin.  Fire up the application and, if it compiles, create a new document.  Type “Hello world.”  So far, so good.  Turn on speech recognition by selecting the Speech Recognition item under the Speech menu.  Highlight “Hello” and say the following phrase into your expensive microphone, inexpensive microphone, or speaker: delete.  Now type “Save the cheerleader, save the”.  Not bad at all.

Speech Recognition And Synthesis Managed APIs In Windows Vista: Part I




VistaSpeechAPIDemo.zip – 45.7 Kb


VistaSpeechAPISource.zip – 405 Kb


Introduction


One of the coolest features to be introduced with Windows Vista is the new built in speech recognition facility.  To be fair, it has been there in previous versions of Windows, but not in the useful form in which it is now available.  Best of all, Microsoft provides a managed API with which developers can start digging into this rich technology.  For a fuller explanation of the underlying technology, I highly recommend the Microsoft whitepaper. This tutorial will walk the user through building a common text pad application, which we will then trick out with a speech synthesizer and a speech recognizer using the .Net managed API wrapper for SAPI 5.3. By the end of this tutorial, you will have a working application that reads your text back to you, obeys your voice commands, and takes dictation. But first, a word of caution: this code will only work for Visual Studio 2005 installed on Windows Vista. It does not work on XP, even with .NET 3.0 installed.

Background


Because Windows Vista has only recently been released, there are, as of this writing, several extant problems relating to developing on the platform.  The biggest hurdle is that there are known compatibility problems between Visual Studio and Vista.  Visual Studio.NET 2003 is not supported on Vista, and there are currently no plans to resolve any compatibility issues there.  Visual Studio 2005 is supported,  but in order to get it working well, you will need to make sure you also install service pack 1 for Visual Studio 2005.  After this, you will also need to install a beta update for Vista called, somewhat confusingly, “Visual Studio 2005 Service Pack 1 Update for Windows Vista Beta”.  Even after doing all this, you will find that all the new cool assemblies that come with Vista, such as the System.Speech assembly, still do not show up in your Add References dialog in Visual Studio.  If you want to have them show up, you will finally need to add a registry entry indicating where the Vista dll’s are to be found.  Open the Vista registry UI by running regedit.exe in your Vista search bar.  Add the following registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders\v3.0 Assemblies with this value: C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\v3.0. (You can also install it under HKEY_CURRENT_USER, if you prefer.)  Now, we are ready to start programming in Windows Vista.

Before working with the speech recognition and synthesis functionality, we need to prepare the ground with a decent text pad application to which we will add on our cool new toys. Since this does not involve Vista, you do not really have to follow through this step in order to learn the speech recognition API.  If you already have a good base application, you can skip ahead to the next section, Speechpad, and use the code there to trick out your app.  If you do not have a suitable application at hand, but also have no interest in walking through the construction of a text pad application, you can just unzip the source code linked above and pull out the included Textpad project.  The source code contains two Visual Studio 2005 projects, the Textpad project, which is the base application for the SR functionality, and Speechpad, which includes the final code.


All the same, for those with the time to do so, I feel there is much to gain from building an application from the ground up. The best way to learn a new technology is to use it oneself and to get one’s hands dirty, as it were, since knowledge is always more than simply knowing that something is possible; it also involves knowing how to put that knowledge to work. We know by doing, or as Giambattista Vico put it, verum et factum convertuntur.


Textpad


Textpad is an MDI application containing two forms: a container, called Main.cs, and a child form, called TextDocument.csTextDocument.cs, in turn, contains a RichTextBox control.


Create a new project called Textpad.  Add the “Main” and “TextDocument” forms to your project.  Set the IsMdiContainer property of Main to true.  Add a MainMenu control and an OpenFileDialog control (name it “openFileDialog1”) to Main.  Set the Filter property of the OpenFileDialog to “Text Files | *.txt”, since we will only be working with text files in this project.  Add a RichTextBox control to “TextDocument”, name it “richTextBox1”; set its Dock property to “Fill” and its Modifiers property to “Internal”.


Add a MenuItem control to MainMenu called “File” by clicking on the MainMenu control in Designer mode and typing “File” where the control prompts you to “type here”.  Set the File item’s MergeType property to “MergeItems”. Add a second MenuItem called “Window“.  Under the “File” menu item, add three more Items: “New“, “Open“, and “Exit“.  Set the MergeOrder property of the “Exit” control to 2.  When we start building the “TextDocument” form, these merge properties will allow us to insert menu items from child forms between “Open” and “Exit”.


Set the MDIList property of the Window menu item to true.  This automatically allows it to keep track of your various child documents during runtime.


Next, we need some operations that will be triggered off by our menu commands.  The NewMDIChild() function will create a new instance of the Document object that is also a child of the Main container.  OpenFile() uses the OpenFileDialog control to retrieve the path to a text file selected by the user.  OpenFile() uses a StreamReader to extract the text of the file (make sure you add a using declaration for System.IO at the top of your form). It then calls an overloaded version of NewMDIChild() that takes the file name and displays it as the current document name, and then injects the text from the source file into the RichTextBox control in the current Document object.  The Exit() method closes our Main form.  Add handlers for the File menu items (by double clicking on them) and then have each handler call the appropriate operation: NewMDIChild(), OpenFile(), or Exit().  That takes care of your Main form.

        #region Main File Operations

private void NewMDIChild()
{
NewMDIChild(“Untitled”);
}

private void NewMDIChild(string filename)
{
TextDocument newMDIChild = new TextDocument();
newMDIChild.MdiParent = this;
newMDIChild.Text = filename;
newMDIChild.WindowState = FormWindowState.Maximized;
newMDIChild.Show();
}

private void OpenFile()
{
try
{
openFileDialog1.FileName = “”;
DialogResult dr = openFileDialog1.ShowDialog();
if (dr == DialogResult.Cancel)
{
return;
}
string fileName = openFileDialog1.FileName;
using (StreamReader sr = new StreamReader(fileName))
{
string text = sr.ReadToEnd();
NewMDIChild(fileName, text);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

private void NewMDIChild(string filename, string text)
{
NewMDIChild(filename);
LoadTextToActiveDocument(text);
}

private void LoadTextToActiveDocument(string text)
{
TextDocument doc = (TextDocument)ActiveMdiChild;
doc.richTextBox1.Text = text;
}

private void Exit()
{
Dispose();
}

#endregion


To the TextDocument form, add a SaveFileDialog control, a MainMenu control, and a ContextMenuStrip control (set the ContextMenuStrip property of richTextBox1 to this new ContextMenuStrip).  Set the SaveFileDialog’s defaultExt property to “txt” and its Filter property to “Text File | *.txt”.  Add “Cut”, “Copy”, “Paste”, and “Delete” items to your ContextMenuStrip.  Add a “File” menu item to your MainMenu, and then “Save“, Save As“, and “Close” menu items to the “File” menu item.  Set the MergeType for “File” to “MergeItems”. Set the MergeType properties of “Save”, “Save As” and “Close” to “Add”, and their MergeOrder properties to 1.  This creates a nice effect in which the File menu of the child MDI form merges with the parent File menu.


The following methods will be called by the handlers for each of these menu items: Save(), SaveAs(), CloseDocument(), Cut(), Copy(), Paste(), Delete(), and InsertText(). Please note that the last five methods are scoped as internal, so they can be called by the parent form. This will be particularly important as we move on to the Speechpad project.


#region Document File Operations

private void SaveAs(string fileName)
{
try
{
saveFileDialog1.FileName = fileName;
DialogResult dr = saveFileDialog1.ShowDialog();
if (dr == DialogResult.Cancel)
{
return;
}
string saveFileName = saveFileDialog1.FileName;
Save(saveFileName);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

private void SaveAs()
{
string fileName = this.Text;
SaveAs(fileName);
}

internal void Save()
{
string fileName = this.Text;
Save(fileName);
}

private void Save(string fileName)
{
string text = this.richTextBox1.Text;
Save(fileName, text);
}

private void Save(string fileName, string text)
{
try
{
using (StreamWriter sw = new StreamWriter(fileName, false))
{
sw.Write(text);
sw.Flush();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

private void CloseDocument()
{
Dispose();
}

internal void Paste()
{
try
{
IDataObject data = Clipboard.GetDataObject();
if (data.GetDataPresent(DataFormats.Text))
{
InsertText(data.GetData(DataFormats.Text).ToString());
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

internal void InsertText(string text)
{
RichTextBox theBox = richTextBox1;
theBox.SelectedText = text;
}

internal void Copy()
{
try
{
RichTextBox theBox = richTextBox1;
Clipboard.Clear();
Clipboard.SetDataObject(theBox.SelectedText);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

internal void Cut()
{
Copy();
Delete();
}

internal void Delete()
{
richTextBox1.SelectedText = string.Empty;
}

#endregion


Once you hook up your menu item event handlers to the methods listed above, you should have a rather nice text pad application. With our base prepared, we are now in a position to start building some SR features.


Speechpad


Add a reference to the System.Speech assembly to your project.  You should be able to find it in C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\.  Add using declarations for System.Speech, System.Speech.Recognition, and System.Speech.Synthesis to your Main form. The top of your Main.cs file should now look something like this:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Speech;
using System.Speech.Synthesis;
using System.Speech.Recognition;

In design view, add two new menu item to the main menu in your Main form labeled “Select Voice” and “Speech“.  For easy reference, name the first item selectVoiceMenuItem.  We will use the “Select Voice” menu to programmatically list the synthetic voices that are available for reading Speechpad documents.  To programmatically list out all the synthetic voices, use the following three methods found in the code sample below.  LoadSelectVoiceMenu() loops through all voices that are installed on the operating system and creates a new menu item for each.  VoiceMenuItem_Click() is simply a handler that passes the click event on to the SelectVoice() method. SelectVoice() handles the toggling of the voices we have added to the “Select Voice” menu.  Whenever a voice is selected, all others are deselected.  If all voices are deselected, then we default to the first one.


Now that we have gotten this far, I should mention that all this trouble is a little silly if there is only one synthetic voice available, as there is when you first install Vista. Her name is Microsoft Anna, by the way. If you have Vista Ultimate or Vista Enterprise, you can use the Vista Updater to download an additional voice, named Microsoft Lila, which is contained in the Simple Chinese MUI.  She has a bit of an accent, but I am coming to find it rather charming.  If you don’t have one of the high-end flavors of Vista, however, you might consider leaving the voice selection code out of your project.


private void LoadSelectVoiceMenu()
{
foreach (InstalledVoice voice in synthesizer.GetInstalledVoices())
{
MenuItem voiceMenuItem = new MenuItem(voice.VoiceInfo.Name);
voiceMenuItem.RadioCheck = true;
voiceMenuItem.Click += new EventHandler(voiceMenuItem_Click);
this.selectVoiceMenuItem.MenuItems.Add(voiceMenuItem);
}
if (this.selectVoiceMenuItem.MenuItems.Count > 0)
{
this.selectVoiceMenuItem.MenuItems[0].Checked = true;
selectedVoice = this.selectVoiceMenuItem.MenuItems[0].Text;
}
}

private void voiceMenuItem_Click(object sender, EventArgs e)
{
SelectVoice(sender);
}

private void SelectVoice(object sender)
{
MenuItem mi = sender as MenuItem;
if (mi != null)
{
//toggle checked value
mi.Checked = !mi.Checked;

if (mi.Checked)
{
//set selectedVoice variable
selectedVoice = mi.Text;
//clear all other checked items
foreach (MenuItem voiceMi in this.selectVoiceMenuItem.MenuItems)
{
if (!voiceMi.Equals(mi))
{
voiceMi.Checked = false;
}
}
}
else
{
//if deselecting, make first value checked,
//so there is always a default value
this.selectVoiceMenuItem.MenuItems[0].Checked = true;
}
}
}


We have not declared the selectedVoice class level variable yet (your Intellisense may have complained about it), so the next step is to do just that.  While we are at it, we will also declare a private instance of the System.Speech.Synthesis.SpeechSynthesizer class and initialize it, along with a call to the LoadSelectVoiceMenu() method from above, in your constructor:


#region Local Members

private SpeechSynthesizer synthesizer = null;
private string selectedVoice = string.Empty;

#endregion

public Main()
{
InitializeComponent();
synthesizer = new SpeechSynthesizer();
LoadSelectVoiceMenu();
}


To allow the user to utilize the speech synthesizer, we will add two new menu items under the “Speech” menu labeled “Read Selected Text” and “Read Document“.  In truth, there isn’t really much to using the Vista speech synthesizer.  All we do is pass a text string to our local SpeechSynthesizer object and let the operating system do the rest.  Hook up event handlers for the click events of these two menu items to the following methods and you will be up and running with an SR enabled application:


#region Speech Synthesizer Commands

private void ReadSelectedText()
{
TextDocument doc = ActiveMdiChild as TextDocument;
if (doc != null)
{
RichTextBox textBox = doc.richTextBox1;
if (textBox != null)
{
string speakText = textBox.SelectedText;
ReadAloud(speakText);
}
}
}

private void ReadDocument()
{
TextDocument doc = ActiveMdiChild as TextDocument;
if (doc != null)
{
RichTextBox textBox = doc.richTextBox1;
if (textBox != null)
{
string speakText = textBox.Text;
ReadAloud(speakText);
}
}
}

private void ReadAloud(string speakText)
{
try
{
SetVoice();
synthesizer.Speak(speakText);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

}

private void SetVoice()
{
try
{
synthesizer.SelectVoice(selectedVoice);
}
catch (Exception)
{
MessageBox.Show(selectedVoice + “\” is not available.);
}
}

#endregion