Developing a Noah Fitting Module Using .NET
Noah 3.0 Fitting Module
Module Installation

Installation types:

  • External Setup Program: Registering the program using the module’s own Setup Program. (Required)
  • Internal Setup Program: Registering the module using the Moduleinstall.ocx Component. (Optional)

Internal Setup

  • Create an install.ini file. For a native Noah 2.0 fitting module:
    [MODULEINFO]
    MODULETYPE=FITTING
    MANUFCODE=044
    MODULENAME=Audina
    ModuleFile=Fit044WM.exe
    HelpFile=FITTING.HLP
    LogoFile=Logo.DLL
     
    [INSTALLATION]
    InstallCmd=NhSetup.exe
  • An install.ini file for a native Noah 3.0 fitting module:
    [Installation]
    ;InstallCmd=NhSetup.exe
    InstallCmd=installer.bat
     
    [Module Install]
    CLSID={241255F6-0B94-4EBE-993A-030A8A4968FE}
    Name=ezFIT 5 for NOAH Fitting Module
    HelpPath=HELP.HLP
    LocalID=1
    LogoDLLPath=LOGO.DLL
    ManufacturerID=044
    Category=Fitting
    CanMake=CanMakeSection
    CanShow=CanShowSection
    [CanMakeSection]
    259,500
    [CanShowSection]
    259,500

    And related installer.bat file:

    copy Release\*.exe %1
    copy Release\*.dll %1
    copy Release\vc\redist\*.dll %1
    copy Release\vc\redist\Microsoft.VC80.MFC.manifest %1
    copy Release\vc\redist\Microsoft.VC80.CRT.manifest %1
     
    cd %1
    FitMod.exe /regserver
    pause

External Setup Program

The module developer must program his own module setup program (for example, Setup.exe) to register module data with the Module Installation Server component.

  • Installer should copy fitting module and related files to path…
    • Register the fitting module interface (if native Noah 3.0 module):
      C:\> C:\Program Files\HIMSA\Modules\Fitting\044>fitmod.exe /regserver 
  • Create a .NET 2.0 (or greater) application.
  • Add COM reference to ModuleInstallationServer 1.0, which is file C:\Program Files\Common Files\HIMSA Shared\ModuleInstallationServer.dll.
  • Add using ModuleInstallationServer; at the top of the files using the Module Installation Server.
  • Create an instance of ModuleInstServer. Eg:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using ModuleInstallationServer;
     
    namespace Nh3FitModInstall
    {
        public partial class frmMain : Form
        {
            ModuleInstServer ModuleInstServer1 = new ModuleInstServer();
     
            public frmMain()
            {
                InitializeComponent();
            }
        }
    }
  • Add code to Install and Uninstall the fitting module. Eg:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using ModuleInstallationServer;
     
    namespace Nh3FitModInstall
    {
        public partial class frmMain : Form
        {
            ModuleInstServer ModuleInstServer1 = new ModuleInstServer();
     
            public frmMain()
            {
                InitializeComponent();
            }
     
            private void frmMain_Load(object sender, EventArgs e)
            {
                ModuleInstServer1.Initialize();
            }
     
            private void btnInstallMod_Click(object sender, EventArgs e)
            {
                // create objects
                Module    oMod   = new Module();
                DataTypes oDTs   = new DataTypes();
                DataType  oDT    = new DataType();
                Protocols oProts = new Protocols();
                Protocol  oProt  = new Protocol();
                SubModule oSub   = new SubModule();
     
                try {
                    // Set datatype that the module can make/show
                    oDT.Code   = 1;
                    oDT.Format = 100;
     
                    // Add datatype to collection
                    oDTs.Add(oDT);
     
                    // Set on-line protocol and related datatype that the module supports
                    oProt.DataTypes = oDTs;
                    oProt.Number    = 1;
                    oProts.Add(oProt);
     
                    // Set submodule
                    oSub.LocalID        = 1;
                    //oSub.ManufacturerID = 044; // 044=Audina, 001=HIMSA;
                    oSub.ManufacturerID = Int16.Parse(txtManufCode.Text); // 044=Audina, 001=HIMSA;
     
                    oMod.CanMake = oDTs;
                    oMod.CanShow = oDTs;
     
                    // ClassID of the module. Remember that the module must also have a valid ProgID entry.
                    //oMod.CLSID          = @"{444DA45F-3BE7-11D1-BAD1-07704FC9C2B9}"; // Noah Sample Module
                    //oMod.CLSID          = @"{241255F6-0B94-4EBE-993A-030A8A4968FE}"; // Audina Fitting Module (for native Noah 3.0 module)
                    //oMod.CLSID          = @"C:\Program Files\HIMSA\Modules\Fitting\044\FIT044WM.EXE"; // Audina Fitting Module (for native Noah 2.0 module)
                    oMod.CLSID          = txtModClassID.Text;
                    //oMod.HelpPath       = @"C:\Program Files\HIMSA\Modules\Fitting\044\FITTING.HLP";
                    oMod.HelpPath       = txtModExeFile.Text;
                    //oMod.KeyName        := "NoKey";
                    oMod.LocalID        = 2;                              // Manufacturer's internal Local Module Identifier
                    //oMod.LogoDLLPath    = @"C:\Program Files\HIMSA\Modules\Fitting\044\Logo044.dll";       // logo.dll
                    oMod.LogoDLLPath    = txtModLogoDll.Text;
                    //oMod.ManufacturerID = 044;                          // 044=Audina, 1=HIMSA;
                    oMod.ManufacturerID = Int16.Parse(txtManufCode.Text); // 044=Audina, 1=HIMSA;
                    //oMod.Name           = "The AudinaTestManuf Module";   // module name
                    oMod.Name           = txtModName.Text;   // module name
                    oMod.Protocols      = oProts;                         // protocols used in Inter-module Communication (IMC)
                    oMod.Show           = true;
     
                    // Set the module category using the ModuleInstallationServer collection of ModuleCategories
                    oMod.Category = ModuleInstServer1.ModuleCategories.Item[1];
                    oMod.SubModules.Add(oSub);
     
                    //ModuleInstServer1.InstallModule("AudinaTestManuf", oMod);
                    ModuleInstServer1.InstallModule(txtManufName.Text, oMod);
     
                } catch (Exception exc){
                    MessageBox.Show(string.Format("Error: {0} {1}", exc.Source, exc.Message));
                }
     
                //finally
                //{
                //  // destroy objects (in reverse order)
                //  oSub   = null;
                //  oProt  = null;
                //  oProts = null;
                //  oDT    = null;
                //  oDTs   = null;
                //  oMod   = null;
                //}
     
     
            }
     
            private void btnUninstallMod_Click(object sender, EventArgs e)
            {
                try
                {
                     ModuleInstServer1.UninstallModule(
                         044, // ManufCode assigned by HIMSA 
                         2    // ModuleCode assigned by Manuf 
                     );
                } catch (Exception exc){
                    MessageBox.Show(string.Format("Error: {0} {1}", exc.Source, exc.Message));
                }
            }
     
            private void btnClose_Click(object sender, EventArgs e)
            {
                Close();
            }
        }
    }
Module Development

Native NOAH 3.0 fitting module using .NET and WPF technologies

Fitting Module Application (FitMod.exe)

This application launches the GUI Wrapper that will contain all the user controls with the actual application functionality.

  • Create C++ MFC (Microsoft Foundation Class) application project.
  • Build application.
  • Register COM server (select one method):
    • By selecting checkbox “Register for COM interop” in project Properties > Build (tab) > Output.
    • By executing the generated executable file. It generates an error “This server can only be run from a container application.”, but just ignore it. Eg:
      C:\> C:\Program Files\MyApp\FitMod.exe

      This should create the following registry entries:

      REGEDIT
      ; This .REG file may be used by your SETUP program.
      ;   If a SETUP program is not available, the entries below will be
      ;   registered in your InitInstance automatically with a call to
      ;   CWinApp::RegisterShellFileTypes and COleObjectFactory::UpdateRegistryAll.
       
       
      HKEY_CLASSES_ROOT\FitMod.Document = FitMod.Document
       
       
      HKEY_CLASSES_ROOT\FitMod.Document\protocol\StdFileEditing\server = C:\Program Files\MyApp\FitMod.EXE
      HKEY_CLASSES_ROOT\FitMod.Document\protocol\StdFileEditing\verb\0 = &Edit
      HKEY_CLASSES_ROOT\FitMod.Document\Insertable =
      HKEY_CLASSES_ROOT\FitMod.Document\CLSID = {241255F6-0B94-4EBE-993A-030A8A4968FE}
       
      HKEY_CLASSES_ROOT\CLSID\{241255F6-0B94-4EBE-993A-030A8A4968FE} = FitMod.Document
      HKEY_CLASSES_ROOT\CLSID\{241255F6-0B94-4EBE-993A-030A8A4968FE}\DefaultIcon = C:\Program Files\MyApp\FitMod.EXE,1
      HKEY_CLASSES_ROOT\CLSID\{241255F6-0B94-4EBE-993A-030A8A4968FE}\LocalServer32 = C:\Program Files\MyApp\FitMod.EXE
      HKEY_CLASSES_ROOT\CLSID\{241255F6-0B94-4EBE-993A-030A8A4968FE}\ProgId = FitMod.Document
      HKEY_CLASSES_ROOT\CLSID\{241255F6-0B94-4EBE-993A-030A8A4968FE}\MiscStatus = 32
      HKEY_CLASSES_ROOT\CLSID\{241255F6-0B94-4EBE-993A-030A8A4968FE}\AuxUserType\3 = FitMod
      HKEY_CLASSES_ROOT\CLSID\{241255F6-0B94-4EBE-993A-030A8A4968FE}\AuxUserType\2 = FitMod
      HKEY_CLASSES_ROOT\CLSID\{241255F6-0B94-4EBE-993A-030A8A4968FE}\Insertable = 
      HKEY_CLASSES_ROOT\CLSID\{241255F6-0B94-4EBE-993A-030A8A4968FE}\verb\1 = &Open,0,2
      HKEY_CLASSES_ROOT\CLSID\{241255F6-0B94-4EBE-993A-030A8A4968FE}\verb\0 = &Edit,0,2
      HKEY_CLASSES_ROOT\CLSID\{241255F6-0B94-4EBE-993A-030A8A4968FE}\InprocHandler32 = ole32.dll

GUI Wrapper (assembly Noah3.FitMod.GUI.dll)

  • Create a C# WinForms project.
  • Create a WinForms user control which will host the WPF GUI control. Eg:
    ///----------------------------------------------------------------------------------------
    /// <summary>
    /// Click event handler for btnLoadWpfControl.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    ///----------------------------------------------------------------------------------------
    private void btnLoadWpfControl_Click(object sender, EventArgs e)
    {
        boxAction.Visible = false;  // hide previous controls
        LoadWpfUserControl();       // load fitting module usercontrol
    }
     
    ///----------------------------------------------------------------------------------------
    /// <summary>
    /// Load and host a WPF user control in WinForms.
    /// Source: "Hosting a WPF Composite Control in Windows Forms"
    ///         http://msdn.microsoft.com/en-us/library/ms745781.aspx
    /// </summary>
    ///----------------------------------------------------------------------------------------
    private void LoadWpfUserControl()
    {
        try
        {
            // Create the ElementHost control for hosting the WPF UserControl.
            ElementHost host = new ElementHost();
            host.Dock = DockStyle.Fill;
     
            // Create the WPF UserControl.
            ezFIT.ucMain uc = new ezFIT.ucMain();
     
            // 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.
            this.Controls.Add(host);
        }
        catch (Exception exc)
        {
            //AHI.App.Logging.TAppLog.LogException("FitModControl.LoadWpfUserControl", exc);
            MessageBox.Show(
                    string.Format("{0}(): Exception:\n\r {1}", "FitModControl.LoadWpfUserControl", exc),
                    "Loading Fitting Module", MessageBoxButtons.OK, MessageBoxIcon.Error
                );
        }
    }
  • Structure code to deal with resources correctly when WPF is hosted in WinForms control (see Dr WPF: Managing Application Resources When WPF is Hosted).

GUI

  • Create a C# WPF project.
  • Add references to project:
    • AHI.App.Noah.dll (Noah fitting module libraries)
    • AHI.App.Resources.dll (resource dictionary with all defined styles)
  • Create a WPF user control with all the required functionality.
    • If using custom styles, load them from AHI.App.Resources.dll
  • Register control or application with Noah Module Server. Typically the GUI control would call the AppViewModel to do so. Eg: The GUI code:
    // Connect
    TAppViewModel AppVM = new TAppViewModel();
    AppVM.ApplicationExecMode = TAppExecMode.Noah;
    AppVM.cmdOpenNoahConnection();
     
    // Do work...
    . . .
    // Disconnect
    AppVM.cmdCloseNoahConnection();

    The AppViewMode.cs methods:

    ///----------------------------------------------------------------------------------------
    /// <summary>
    /// Register with Noah.
    /// </summary>
    ///----------------------------------------------------------------------------------------
    private void cmdOpenNoahConnection()
    {
        if (m_ApplicationExecMode == TAppExecMode.Noah)
        {
            // Initialize the module server and register the module with module server.
            TAppLog.LogMessage("TAppViewModel.cmdOpenNoahConnection", "ApplicationExecMode = Noah.");
            TAppLog.LogMessage("TAppViewModel.cmdOpenNoahConnection", "Initializing NoahApp...");
            m_NoahApp = new TNoahApp();
        }
        else
        {
            TAppLog.LogMessage("TAppViewModel.cmdOpenNoahConnection", "ApplicationExecMode = Stand-alone.");
        }
    }  
     
    ///----------------------------------------------------------------------------------------
    /// <summary>
    /// Close the Noah connection before closing application.
    /// </summary>
    ///----------------------------------------------------------------------------------------
    public void cmdCloseNoahConnection()
    {
        TAppLog.LogMessage("TAppViewModel.cmdClose", "Start.");
     
        if (m_ApplicationExecMode == TAppExecMode.Noah)
        {
            TAppLog.LogMessage("TAppViewModel.cmdCloseNoahConnection", "ApplicationExecMode = Noah.");
     
            // Close the module server and unregister the module with module server.
            if (m_NoahApp != null)
            {
                TAppLog.LogMessage("TAppViewModel.cmdCloseNoahConnection", "Calling NoahApp.cmdClose()...");
                m_NoahApp.cmdClose();
            }
        }
        else
        {
            TAppLog.LogMessage("TAppViewModel.cmdCloseNoahConnection", "ApplicationExecMode = Stand-alone.");
        }
     
        TAppLog.LogMessage("TAppViewModel.cmdCloseNoahConnection", "End.");
    }

Noah Fitting Module Library (assembly AHI.App.Noah.dll)

  • Create Class (assembly) project.
  • Add references to project:
    • ModuleServer (COM server)
  • Add all the Noah constants and methods that need Noah interaction, and make these available to the application.
  • Methods:
    • InitializeNoahModServer(): Instantiating the Module Server component, and registering the module with the Noah Module Server.
    • WriteBinaryDataToFile(): Write specified actions's public data (binary format) to file.
    • GetActionByActionID(): Retrieve an action from current session.
    • cmdAddAction(): Add an action into current session.
    • cmdClose(): Close the Noah Module Server.
    • NhDisplayInfo(): Display some NOAH parameters, to test whether it reads NOAH.
    • AddPatient(): Add a new Noah Patient record.
    • SaveFitting(): Save fitting to current Client's (Patient's) current session.
    • GetCurrentPatient(): Get current patient information.
    • GetCurrentAction(): Get current action information.

Noah Fitting Module Logo (assembly Noah3.FitMod.Logo.dll)

  • Create Class (assembly) project.
  • Create LogoRes.rc resource file (for more details: Logo Format). NOTE: Do not #define the LOGO001 and LOGO002 resources. Create file as follows:
    LOGO001      BITMAP "images/logo16.bmp"
    LOGO002      BITMAP "images/logo256.bmp"
    STRINGTABLE
    {
      1001, "Acme"
    }
  • Compile resource file LogoRes.rc into LogoRes.res. Using the Visual Studio Command Prompt (VS2015: Start > All Applications > Visual Studio 2015 > Visual Studio Tools > Developer Command Prompt for VS2015):
    C:\> rc C:\MyApp\LogoRes.rc 
  • Alternatively, use GoRC Resource Compiler (http://www.godevtool.com) from any command prompt:
    C:\> GoRC.exe C:\MyApp\LogoRes.rc
  • Add res file to assembly. Project > Properties > Application (tab) > Resource File, and select LogoRes.res.
  • Build assembly.