= 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.