= 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 ==
{{:swdev:howto:ezfit5-nativenoah3fittingmoduleusingdotnetwpf.png?500|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:
///----------------------------------------------------------------------------------------
///
/// Click event handler for btnLoadWpfControl.
///
///
///
///----------------------------------------------------------------------------------------
private void btnLoadWpfControl_Click(object sender, EventArgs e)
{
boxAction.Visible = false; // hide previous controls
LoadWpfUserControl(); // load fitting module usercontrol
}
///----------------------------------------------------------------------------------------
///
/// 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
///
///----------------------------------------------------------------------------------------
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 [[http://drwpf.com/blog/2007/10/05/managing-application-resources-when-wpf-is-hosted/|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:
///----------------------------------------------------------------------------------------
///
/// Register with Noah.
///
///----------------------------------------------------------------------------------------
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.");
}
}
///----------------------------------------------------------------------------------------
///
/// Close the Noah connection before closing application.
///
///----------------------------------------------------------------------------------------
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: [[swdev:howto:develop_a_noah_fitting_module#fitting_module_icon_logodll|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.