How to package and deploy COM component

Web Development Tools Microsoft

I’ll use a walkthrough example to show how to package a web application with speech API COM component using Visual Studio 2010.  I wrote and tested the sample in Win7 x86 with IIS7.5, and packaged and manually installed to win2k3 x86 IIS6 (which only had 3.5 framework installed).

1. Create a C# 3.5 web application

2. Add COM reference to “Microsoft Speech Object Library”

image

3. In Default.aspx, add the following between <div>

        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <asp:TextBox ID="TextBox1" runat="server" Width="274px">Welcome to web deployment</asp:TextBox>
                <asp:Button ID="Button1" runat="server" onclick="Button1_Click" 
                    Text="Play the text" />
                <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
            </ContentTemplate>
        </asp:UpdatePanel>

4. In Default.aspx.cs, write the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using SpeechLib;

namespace WebApplication2
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!String.IsNullOrEmpty(Request.Params["text"]))
            {
                Response.Buffer = false;

                byte[] byteBuffer = MyTextToWav(Request.Params["text"]);
                Response.Clear();
                Response.ClearHeaders();
                Response.ClearContent();
                
                Response.AppendHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(TextBox1.Text + ".wav", System.Text.Encoding.UTF8));
                Response.ContentType = "audio/wav";
                Response.BinaryWrite(byteBuffer);
                Response.End();
            }
        }

        private byte[] MyTextToWav(string inputText)
        {
            byte[] b = null;
            {
                SpMemoryStream spMemStream = new SpMemoryStream();

                //default value is spMemStream.Format.Type = SpeechAudioFormatType.SAFT22kHz16BitMono;
                SpeechLib.SpVoice ispvoice1 = new SpVoice();
                ispvoice1.AudioOutputStream = spMemStream;

                //Note, in IIS, the app pool has to be using LocalSystem to access speechLib COM
                ispvoice1.Speak(inputText, SpeechVoiceSpeakFlags.SVSFDefault);
                ispvoice1.WaitUntilDone(10000);
                spMemStream.Seek(0, SpeechStreamSeekPositionType.SSSPTRelativeToStart);

                byte[] buffer = (byte[])spMemStream.GetData();
                using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
                {
                    System.IO.BinaryWriter writer = new System.IO.BinaryWriter(memoryStream);

                    HeaderWrite(writer, false, 16, buffer.Length / 2, 22100);  //Hack, only works if default as SAFT22kHz16BitMono
                    //HeaderWrite(writer, false, 16, buffer.Length / 2, 44100);
                    writer.Write(buffer);

                    b = memoryStream.GetBuffer();
                }
            }
            return b;
        }

        private void HeaderWrite(System.IO.BinaryWriter writer, bool stereo, short bitsPerSample, int numberOfSamples, int sampleRate)
        {
            writer.Write(0x46464952); // "RIFF" in ASCII
            writer.Write((int)(44 + (numberOfSamples * bitsPerSample * (stereo ? 2 : 1) / 8)) - 8);
            writer.Write(0x45564157); // "WAVE" in ASCII

            writer.Write(0x20746d66); // "fmt " in ASCII
            writer.Write(16);
            writer.Write((short)1);
            writer.Write((short)(stereo ? 2 : 1));
            writer.Write(sampleRate);
            writer.Write(sampleRate * (stereo ? 2 : 1) * bitsPerSample / 8);
            writer.Write((short)((stereo ? 2 : 1) * bitsPerSample / 8));
            writer.Write(bitsPerSample);

            writer.Write(0x61746164); // "data" in ASCII
            writer.Write((int)(numberOfSamples * bitsPerSample * (stereo ? 2 : 1) / 8));
        } 

        protected void Button1_Click(object sender, EventArgs e)
        {
            try
            {
                Response.Redirect(String.Format("Default.aspx?&text={0}", TextBox1.Text ));
            }
            catch (Exception ex)
            {
                PlaceHolder1.Controls.Add(new LiteralControl(ex.ToString()));
            }
        }
    }
}

5. Ctrl-F5 and test, it should work fine with ASP.NET Development Server.

6. Right click the project name in solution explorer, and choose “Build Deployment Package”, it should build a debug package inside project’s objdebugpackage directory. 

7. In a admin command prompt, we can go to that directory, and deploy the package locally to IIS by running “<ProjectName>.deploy.cmd /y”.  Note, we can also publish it using publish dialog by using localhost as the service URL.

8. Since SpeechAPI does not work with IIS application pools that runs with “Network Service” or “ApplicationPoolIdentity”, we’ve to add a new 2.0 application pool, and set its identity to “LocalSystem”.  Then set the deployed website’s application pool to use it.  Test it out, it should work in IIS7 as well.  (Security risk, make sure you change it back after demo.)

image image

9. Let’s try to package and deploy to a win2k3 machine.  To install the corresponding Speech API on the win2k3 machine, I can download/install Microsoft Speech SDK, or copy the corresponding speech API dll from my win7 machine and do regsvr32 on the machine.  But for demonstrating purpose, I decide to use web deploy  provider comObject32 to package the COM component along with my application, so that the package can be easily deployed to many win2k3 machines.  We extend our project similarly to the one stated in the how to package registry blog.  The extended projectName.wpp.targets file looks like following:

<!--******************************************************************** -->
<!-- Task CollectcomObjectForPackage -->
<!-- comObject32 Provider reference: http://technet.microsoft.com/en-us/library/dd569107(WS.10).aspx -->
<!-- comObject64 Provider reference: http://technet.microsoft.com/en-us/library/dd569022(WS.10).aspx -->
<!--********************************************************************-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <!--Targets get execute before this Target-->
    <OnBeforeCollectcomObjectForPackage Condition="'$(OnBeforeCollectcomObjectForPackage)'==''">
    </OnBeforeCollectcomObjectForPackage>
    <!--Targets get execute after this Target-->
    <OnAfterCollectcomObjectForPackage Condition="'$(OnAfterCollectcomObjectForPackage)'==''">
    </OnAfterCollectcomObjectForPackage>

    <CollectcomObjectForPackageDependsOn Condition="'$(CollectcomObjectForPackageDependsOn)'==''">
      $(OnBeforeCollectcomObjectForPackage);
      Build;
    </CollectcomObjectForPackageDependsOn>
  </PropertyGroup>

  <PropertyGroup>
    <IncludecomObjectForMyProject Condition="'$(IncludecomObjectForMyProject)'==''">False</IncludecomObjectForMyProject>
    <MyComObject32s Condition="'$(MyComObject32s)'==''"></MyComObject32s>
    <AfterAddContentPathToSourceManifest Condition="'$(AfterAddContentPathToSourceManifest)'==''">
      $(AfterAddContentPathToSourceManifest);
      CollectcomObjectForPackage;
    </AfterAddContentPathToSourceManifest>
  </PropertyGroup>

  <ItemGroup>
    <MycomObject Include = "$(MyComObject32s)"/>
  </ItemGroup>

  <Target Name="CollectcomObjectForPackage"
          DependsOnTargets="$(CollectcomObjectForPackageDependsOn)"
          Condition="$(IncludecomObjectForMyProject) AND '$(MyComObject32s)'!=''">

    <Message Text="Adding %(MycomObject.Identity)" />
    <ItemGroup>
      <MsDeploySourceManifest Include="comObject32"
                                 Condition="$(IncludecomObjectForMyProject)">
        <Path>%(MycomObject.Identity)</Path>
      </MsDeploySourceManifest>
    </ItemGroup>
    <CallTarget Targets="$(OnAfterCollectcomObjectForPackage)" RunEachTargetSeparately="false" />
  </Target>
</Project>

10. On my sample win2k3 machine, I manually copied WindowsSystem32SpeechCommonsapi.dll and did a registry export before and after regsvr32 it.  It turns out we need to include many registry node for speech API COM component.  I put it in the following package command line (really long).  Run it, you get a package with the speech API COM component.

msbuild WebApplication5.csproj /target:package /p:IncludecomObjectForMyProject=True;MyComObject32s="SAPI.SpAudioFormat;SAPI.SpAudioFormat.1;SAPI.SpCompressedLexicon;SAPI.SpCompressedLexicon.1;SAPI.SpCustomStream;SAPI.SpCustomStream.1;SAPI.SpDataKey;SAPI.SpDataKey.1;SAPI.SpFileStream;SAPI.SpFileStream.1;SAPI.SpGramCompBackEnd;SAPI.SpGramCompBackEnd.1;SAPI.SpGrammarCompiler;SAPI.SpGrammarCompiler.1;SAPI.SpInProcRecoContext;SAPI.SpInProcRecoContext.1;SAPI.SpInprocRecognizer;SAPI.SpInprocRecognizer.1;SAPI.SpITNProcessor;SAPI.SpITNProcessor.1;SAPI.SpLexicon;SAPI.SpLexicon.1;SAPI.SpMemoryStream;SAPI.SpMemoryStream.1;SAPI.SpMMAudioEnum;SAPI.SpMMAudioEnum.1;SAPI.SpMMAudioIn;SAPI.SpMMAudioIn.1;SAPI.SpMMAudioOut;SAPI.SpMMAudioOut.1;SAPI.SPNotify;SAPI.SPNotify.1;SAPI.SpNotifyTranslator;SAPI.SpNotifyTranslator.1;SAPI.SpNullPhoneConverter;SAPI.SpNullPhoneConverter.1;SAPI.SpObjectToken;SAPI.SpObjectToken.1;SAPI.SpObjectTokenCategory;SAPI.SpObjectTokenCategory.1;SAPI.SpObjectTokenEnum;SAPI.SpObjectTokenEnum.1;SAPI.SpPhoneConverter;SAPI.SpPhoneConverter.1;SAPI.SpPhrase;SAPI.SpPhrase.1;SAPI.SpPhraseBuilder;SAPI.SpPhraseBuilder.1;SAPI.SpPhraseInfoBuilder;SAPI.SpPhraseInfoBuilder.1;SAPI.SpResourceManager;SAPI.SpResourceManager.1;SAPI.SpSharedRecoContext;SAPI.SpSharedRecoContext.1;SAPI.SpSharedRecognizer;SAPI.SpSharedRecognizer.1;SAPI.SpShortcut;SAPI.SpShortcut.1;SAPI.SpStream;SAPI.SpStream.1;SAPI.SpStreamFormatConverter;SAPI.SpStreamFormatConverter.1;SAPI.SpTextSelectionInformation;SAPI.SpTextSelectionInformation.1;SAPI.SpUncompressedLexicon;SAPI.SpUncompressedLexicon.1;SAPI.SpVoice;SAPI.SpVoice.1;SAPI.SpWaveFormatEx;SAPI.SpWaveFormatEx.1;SAPIEngine.TTSEngine;SAPIEngine.TTSEngine.1;{1B2AFB92-0B5E-4A30-B5CC-353DB4F9E150};{1B57B2A1-E763-4676-9064-297F1B413632};{2D12DD17-6C4E-456E-A953-D210E3C64176};{4C6F940C-3CFE-11D2-9EE7-00C04F797396};{4F414126-DFE3-4629-99EE-797978317EAD};{9CDAEA40-A7D3-4CC5-AED0-B5E35AD0F169};{C02F29F0-DFCA-4DC1-8B80-ED3216E1B5E0};{CA0AC604-8EF2-448A-AE97-62A2C2CC3C46};{D2C13906-51EF-454E-BC67-A52475FF074C};{D36D2090-4DF3-4BD4-A22F-0D91975F7964};{D6AD10F3-70AB-41E1-96B3-4C36E35D333C};{DC626A64-D684-4627-83CB-44420ABDBD1A};{F3D3F924-11FC-11D3-BB97-00C04F8EE6C0}"

11. copy the package and deploy command file to win2k3 x86 machine with IIS6, using “<ProjectName>.deploy.cmd /y” from command line.

12. In IIS Manager, make sure the new deployed website is running with asp.net 2.0.  Create a new application pool and change its Identity to “Local System” (Security risk, make sure you change it back after demo).  Assign the deployed website to use this application pool.  Test it out, it should work.

 

Note, for IIS7 to IIS7 package and deployment, we can include the application pool setting so that when deploy, a new application pool will be created (or existing be overwritten if app pool name already exists) and “Local System” is granted as identity. (VS2010 beta1 version’s topic is in http://blogs.msdn.com/webdevtools/archive/2009/02/09/web-packaging-creating-a-web-package-using-vs-2010.aspx , the UI has changed a bit, but still the same mostly.)

 

Xinyang Qiu | Visual Web Developer

0 comments

Discussion is closed.

Feedback usabilla icon