The HOOPS/Winforms integration consists of C# wrappers to HOOPS/3dGS, HOOPS/MVO, HOOPS/Stream and HOOPS/Parasolid as well as a connection between HOOPS/3dGS and the Winforms 'Panel' and 'Form' GUI objects. This document describes how to use the HOOPS/Winforms integration to build a .Net Winforms application that incorporates the HOOPS/3dAF components. Some familiarity with .Net Winforms, HOOPS/3dGS and HOOPS/MVO is assumed.
Developers should start by compiling and running the basic csharp_simple or vb_simple applications as the starting point for their application. This is the primary example for .Net developers wishing to incorporate the HOOPS/3dAF components into either existing or new .Net applications. The readable source code is located in your <hoops>/demo/csharp/csharp_simple and <hoops>/demo/visual_basic/vb_simple directories. Note: the programming example below utilize the csharp_simple demo as roadmap, but it should be fairly straightforward for VB.Net developers to follow the closely matching vb_simple example.
The <hoops>/demo/csharp/csharp_simple_3dgs_only and <hoops>/demo/visual_basic/vb_simple_3dgs_only directories contains some very simple applications that only use HOOPS/3dgs, and draws into a user-created window.
The HOOPS/Winforms integration is supported under Microsoft Visual Studio 2005 (VC8).
The following steps are required to compile and run a HOOPS/Winforms based application:
The above files are located in your <hoops>/bin/nt_i386_vc80 directory.
Note: Both C#.Net and VB.Net application code can access these C# wrappers, because Visual Studio 2005 provides built in support for VB apps to access C# interfaces. You simply include the C# references.
The routines and classes of the HOOPS/3dGS, HOOPS/MVO and HOOPS/Stream toolkits are all generally accessible via the C# wrappers, with a few exceptions and notes covered below. Developers should refer to the Reference Manual for each component, and can additionally leverage the Intellisense capability of Visual Studio 2005 to dynamically access listings of class members and function/method argument while programming.
You should also add this "define" to your document somewhere:
#if _M_X64 using HLONG = System.Int64; #else using HLONG = System.Int32; #endif
Then, for 64 bit projects, you should define the _M_X64 (you can name this anything you want). You should use "HLONG" in all places where you would normally use a C "long". This is because C#'s "long" is always 64 bit, whereas C's "long" is often 32 bit, and are incompatible. Using HLONG will help ensure compatability. (You can also simply use ints for 32 bit projects and longs for 64 bit projects, but things may get confusing).
Each routine name, such as Insert_Polyline in your HOOPS program must have a prefix before the name will actually be usable on your computer system. The prefix varies depending on which language you're calling from, and sometimes depending on the brand of the language you're calling from. When calling from C#, all calls to the HOOPS/3dGS API are made through the HCS class, such as:
HCS.Open_Segment("newsegment"); HCS.Insert_Line(0, 0, 0, 1, 1, 1); HCS.Close_Segment();
Most functions available under C are available in C# except for:
Define_Callback_Name
Define_Exit_Handler
UnDefine_Exit_Handler
Define_Callback_Name
UnDefine_Callback_Name
Show_Callback_Name
Set_Driver
QSet_Driver
Two functions are different under C# than C:
UnDefine_Error_Handler
Define_Error_Handler
Both take an HCSU.errfunc object (which is a delegate) as their argument. If null is passed to UnDefine_Error_Handler, it will remove the default HOOPS error handler. If an already registered HCSU.errfunc is passed, that handler will be removed.
Here is an example of declaring an error handler:
public void error_handler(int category, int specific, int severity, int msgc, sbyte ** msgv, int stackc, sbyte ** stackv) { } public void init() { HCSU.errfunc error_hndlr_ptr = new HCSU.errfunc(error_handler); HCS.Define_Error_Handler(error_hndlr_ptr); HCS.Open_Segment("?picture"); // do something HCS.Close_Segment(); }
Note: You may have to set your project to "allow unsafe code" in order to use the error handler, as it uses pointers.
Pointers and write-back values
The largest difference between using HOOPS with C# and C is found when you have a write-back value, i.e. you pass a pointer to a function and it writes a value or values to that location. In HOOPS C#, arrays can typically be written to without any extra help, while single variables need a "ref" keyword before the object name (similar to the "&" in C/C++).
For example, the C HOOPS code that uses a char*:
char ex[1024]; HC_Show_Alias("?picture", ex); printf("?picture=%s\n", ex);
is replaced in C# by StringBuilder:
StringBuilder ex = new StringBuilder(1024); ex.EnsureCapacity(1024); HCS.Show_Alias("?picture", ex); Console.WriteLine("?picture="+ex);
NOTE: The "StringBuilder" type is used for write-back values. Note that before you begin writing into StringBuilder, you should call EnsureCapacity to guarantee your instance has the minimum specified value. You should use the "string" type for constant string values. These are enforced by the function signatures, so you should not be able to pass in the wrong type.
The C HOOPS code that uses &[float_value]:
float x,y,z; HC_QShow_Camera_Position("?picture", &x, &y, &z); printf(" x=%f y=%f z=%f\n", x, y, z);
is used in a similar manner in C#, with the "ref" keyword:
float x = new float(); float y = new float(); float z = new float(); HCS.QShow_Camera_Position("?picture", ref x, ref y, ref z); Console.WriteLine(" x="+ x + " y="+ y + " z="+ z);
Arrays can be used in C# in a similar manner as they are used in C. Thus, the C HOOPS code:
float v1[3] = {1.0f,0.0f,0.0f}; float v2[3] = {0.0f,1.0f,0.0f}; float v3[3]; HC_Compute_Cross_Product(v1,v2,v3); printf("%f %f %f\n", v3[0], v3[1], v3[2]);
is accomplished in C# by:
float[] v1 = {1.0f,0.0f,0.0f}; float[] v2 = {0.0f,1.0f,0.0f}; float[] v3 = new float[3]; HCS.Compute_Cross_Product(v1,v2,v3); Console.WriteLine(v3[0] + " " + v3[1] + " " + v3[2]);
Another difference between using HOOPS with C# and C is found when dealing with HOOPS keys. In C#, you should use HLONG (assuming you defined it as mentioned in the Section 3.1 above). Thus, the C code:
HC_KEY key = 0; key = HC_Open_Segment("?picture"); ... HC_Open_Segment_By_Key(key);
is replaced in C# by:
HLONG key = 0; key = HCS.KOpen_Segment("?picture"); ... HCS.Open_Segment_By_Key(key);
The C# datatypes translate as follows:
| HOOPS | HOOPS (HC) type | C# type |
| 'string' | HC type | string |
| 'writes string' | const char * | StringBuilder |
| 'HC_KEY' | HC_KEY | HLONG (System.Int32 / System.Int64) |
| 'int' | int | int |
| 'writes int' | +" | ref int |
| 'float' | float | float |
| 'writes float' | &float | ref float |
| 'point' | float[3] | float[3] |
| 'writes point' | &float | float[] |
| 'writes bytes' | unsigned char * | sbyte[] |
| 'writes ints' | +" | int[] |
A majority of HOOPS/MVO classes are available in C#. However, a number of HOOPS/MVO classes are not provided due to some inherent challenges with the wrapper process.
HOOPS/MVO provides the ability create and modify animations. The HOOPS/MVO animation classes that are accessible in C# are HBhvAnimation and HBhvBehaviorManager. These classes give you the ability to build and manipulate a wide variety of keyframe based behaviors as described in sections 10.1 Introduction to Behaviors and Animations to 10.3 Defining Behaviors for basic animations in the HOOPS/MVO Programming Guide. The classes associated with animation that not accessible in C# are listed below.
The following operators are also not accessible in C#:
A number of input and output handlers are not available in C#. They are listed below.
Although HObject and HObjectManager are both availabe in C#, the HOOPS/MVO child classes associated with HObject are not. These include the following:
Most utility classes are available in C# except for the following:
The following HOOPS/MVO classes are also not accessible via C#:
The concept of multiple inheritance is not supported in C#. Thus, access to certain HOOPS/MVO classes that utilize multiple inheritance is limited. Specifically, these classes were wrapped with only one class inherited. The following is a list of the C++ MVO classes that use multiple inheritance and what single class they now inherit from in C#.
| HOOPS/MVO Class | Parent Class in C# | Parent Classes in C++ |
| HBaseView | HUpdateListener | HUpdateListener, HMouseListener, HObjectManipulationListener |
| HTCObjectRotate | HBaseOperator | HBaseOperator, HTClient |
Most HOOPS/MVO datatypes are accessible in C# but there are some datatypes that have not gone through the wrapper process. They appear in the C# environment with the SWIGTYPE prefixed to their datatype name. Although methods that use these datatypes can be seen with Intellisense, any code calling these methods will not compile. Below is a list of methods that cannot be called in C# because they used datatypes that have not gone through the wrapper process completely.
| HOOPS/MVO Class | Inaccessible HOOPS/MVO Method |
| HAnimationListener | public virtual int ObjectCollisionEvent(SWIGTYPE_p_HBhvTargetObject tob) |
| HAnimationListener | public virtual int ObjectNoCollisionEvent(SWIGTYPE_p_HBhvTargetObject tob) |
| HAnimationListener | public virtual int AnimationFinishedEvent(SWIGTYPE_p_HBhvAnimation ainst) |
| HBaseModel | public SWIGTYPE_p_HBhvBehaviorManager GetBhvBehaviorManager() |
| HBaseModel | public void SetBhvBehaviorManager(SWIGTYPE_p_HBhvBehaviorManager BehaviorManager) |
| HBaseView | public bool SetStencilProbe(SWIGTYPE_p_HStencilProbe sp) |
| HBaseView | public SWIGTYPE_p_HUtilityAntialiasing GetAntialiasing() |
| HBaseView | public SWIGTYPE_p_HStencilProbe GetStencilProbe() |
| HBaseView | public SWIGTYPE_p_HSharedKey GetSharedKey() |
| HDebugZBuffer | public void OpenglFinishPicture(SWIGTYPE_p_ht_net_rendition nr, bool swap_buffers) |
| HImErrorHandler | public static void CallbackEntryPoint(int category, int specific, int severity, int msgc, SWIGTYPE_p_p_char msgv, int stackc, SWIGTYPE_p_p_char stackv) |
| HImUtility | public static void set_clip_rectangle(SWIGTYPE_p_ht_net_rendition nr, SWIGTYPE_p_ht_segment_info si) |
| HImUtility | public static void suppress_3d_geometry(SWIGTYPE_p_ht_net_rendition rendition, SWIGTYPE_p_ht_geometry geo3) |
| HImUtility | public static void draw_gradiated_window_background(SWIGTYPE_p_ht_net_rendition rendition, SWIGTYPE_p_ht_int_rectangle extent) |
| HImUtility | public static void draw_annotation_lines(SWIGTYPE_p_ht_net_rendition nr, SWIGTYPE_p_ht_polyline poly) |
| HImUtility | public static void draw_segment_in_background(SWIGTYPE_p_ht_net_rendition nr, SWIGTYPE_p_ht_dc_point points, SWIGTYPE_p_ht_rgba colors, SWIGTYPE_p_ht_plane planes, float[] hparams, int param_width, int param_flags) |
| HImUtility | public static void draw_dc_polyline_infront(SWIGTYPE_p_ht_net_rendition rendition, int count, SWIGTYPE_p_ht_dc_point geo3) |
| HImUtility | public static void draw_device_independent_line_weights(SWIGTYPE_p_ht_net_rendition rendition, int count, SWIGTYPE_p_ht_dc_point geo3) |
| HImUtility | public static void drawTextInfront(SWIGTYPE_p_ht_net_rendition rendition, SWIGTYPE_p_ht_text_info text_info) |
| HImUtility | public static void visible_hlr_polyline(SWIGTYPE_p_ht_net_rendition rendition, int count, SWIGTYPE_p_ht_dc_point geo3) |
| HIOConnector | public virtual bool GetHoopsEntities(IntPtr pKernelEntity, SWIGTYPE_p_vlist_s ret_HoopsKeysList) |
| HIOConnector | public virtual bool GetKernelEntities(MVO_POINTER_SIZED_INT key, SWIGTYPE_p_vlist_s ret_KernelEntitiesList) |
| HOpCameraWalk | public static int ImageAction(SWIGTYPE_p_HImageRegion ii, IntPtr data, SWIGTYPE_p_HButtonState keyup) |
| HOpCameraWalk | public void OnButtonWalk(string action, SWIGTYPE_p_HButtonState buttonstate) |
| HSelectionSet | public virtual SWIGTYPE_p_vlist_s GetSelectionList() |
| HSensorListener | public virtual int SensorActivatedEvent(SWIGTYPE_p_HBhvSensor sensor) |
| HUtility | public static void ShowContentsWithPath(string entitytypes, SWIGTYPE_p_p_vlist_s ret_pathlist, bool storePath, bool includeIncludes, bool filterIncludes) |
| HUtility | public static void ShowContentsWithPath(string entitytypes, SWIGTYPE_p_p_vlist_s ret_pathlist, bool storePath, bool includeIncludes) |
| HUtility | public static void ShowContentsWithPath(string entitytypes, SWIGTYPE_p_p_vlist_s ret_pathlist, bool storePath) |
| HUtility | public static void ShowContentsWithPath(string entitytypes, SWIGTYPE_p_p_vlist_s ret_pathlist) |
| HUtility | public static void MakeViewSnapshot(HBaseView view, int width, int height, SWIGTYPE_p_p_char data) |
Note that pointers to vlist and vhash are also not accessible via C#.
MVO macros are not available in C#. Function pointers are also not supported. Although methods with function pointers in their signatures can be seen by Intellisense, any calls to them will not compile. Member variables which are function pointers cannot be accessed either. Generally, if a parameter or variable has a data-type prefixed with SWIGTYPE, it is unlikely that it can be used in C#. In the following example, the HIOManager's RegisterConnector method cannot be used in C# because the parameters Create and Free are function pointers. In the C# function declaration, it can be seen that Create and Free have datatypes prefixed with SWIGTYPE.
public virtual HIOConnector HIOManager::RegisterConnector(string file_type, SWIGTYPE_p_f_p_void__p_void Create, SWIGTYPE_p_f_p_void__void Free)
The following is a list of methods that cannot be accessed in C# because they have functions pointers in their signature:
| HOOPS/MVO Class | HOOPS/MVO Method |
| HBaseView | public SWIGTYPE_p_f_p_q_const__char_unsigned_int_p_void__void GetEmitMessageFunction() |
| HBaseView | public SWIGTYPE_p_f_p_ht_net_rendition__void GetEventCheckerCallback() |
| HBaseView | public SWIGTYPE_p_f_unsigned_int_r_int__bool GetKeyStateCallback() |
| HBaseView | public void SetEmitMessageFunction(SWIGTYPE_p_f_p_q_const__char_unsigned_int_p_void__void new_emit_message_function) |
| HBaseView | public void SetEmitMessageFunction(SWIGTYPE_p_f_p_q_const__char_unsigned_int_p_void__void new_emit_message_function, IntPtr user_data) |
| HBaseView | public void SetEventCheckerCallback(SWIGTYPE_p_f_p_ht_net_rendition__void EventChecker) |
| HBaseView | public void SetKeyStateCallback(SWIGTYPE_p_f_unsigned_int_r_int__bool KeyState) |
| HBaseView | public uint SetSignalNotify(int signal, SWIGTYPE_p_f_int_p_void_p_void__bool callback, IntPtr user_data) |
| HIOManager | public HIOConnector RegisterConnector(string file_type, SWIGTYPE_p_f_p_void__p_void Create, SWIGTYPE_p_f_p_void__void Free) |
| HTClient | HTClient(float interval, HTCStyle style, SWIGTYPE_p_f_float_float_p_void__bool tick_function, IntPtr user_data) |
| HTClient | HTClient(float interval, HTCStyle style, SWIGTYPE_p_f_float_float_p_void__bool tick_function) |
| HTClient | public SWIGTYPE_p_f_float_float_p_void__bool GetTickFunction() |
| HTClient | public void SetTickFunction(SWIGTYPE_p_f_float_float_p_void__bool tick_function) |
Although some HOOPS/MVO classes are availabe in C#, not all their methods can be used because of the design of the C# language. In these cases, HOOPS provides a variant especially designed for C# users. Below is a list of classes, their inaccessible methods and their C# variants.
| Class | Inaccessible Method | C# Variant Method |
| HSelectionItem | virtual const HC_KEY * HSelectionItem::GetFullPathKeys (HBaseView *view) | virtual void HSelectionItem::GetFullPathKeys (HBaseView *view, System.Int32/System.Int64[] path) |
| HSmartSelItem | virtual const HC_KEY *const HSmartSelItem::GetIncludeKeys () const | virtual void HSmartSelItem::GetIncludeKeys (System.Int32/System.Int64[] includes) const |
| HSmartSelItem | virtual const HC_KEY * HSmartSelItem::GetFullPathKeys (HBaseView *view) | virtual void HSmartSelItem::GetFullPathKeys (HBaseView *view, System.Int32/System.Int64[] path) |
| HShellObject | int const * HShellObject::GetFlist () const | void HShellObject::GetFlist (int[] flist) const |
| HShellVertexData | float const *const HShellVertexData::GetFIndexData () const | void HShellVertexData::GetFIndexData (float[] data) const |
Protected members are not wrapped and must be accessed via their public accessor functions. However, not all classes have public accessors for every protected members. Please inform Techsoft support if you encounter a class which does not have the needed public accessors and we will get you a patch.
This section discusses the relationship between .Net Winforms and HOOPS/3dAF components. Building an application with both these toolkits minimally involves using the following objects from each component.
A .Net Winforms application typically has a master Form object, which contains child forms. Each child form may contain a Panel.
At least one HNPanel and HNForm object, where the Panel gets attached to the Form. You usually create a custom panel and form derived from HNPanel and HNForm.
HBaseModel, HBaseView, an Operator class derived from HBaseOperator. Applications that want to implement selection of Geometry will also need a HSelectionSet object. These objects are all connected by private data members which store pointers to other objects in the following manner:
Programming with an object oriented GUI framwork like .Net Winforms involves creating a set of objects and defining the ways in which they are connected, the manner in which they send and receive messages, and then launching the framework's event loop. Building an application using .Net Winforms and HOOPS/3dAF specifically requires creation and initialization of:
A .Net Winforms application usually creates a master Form object which implements the function Main(). The csharp_simple application does this in it's primary csharp_simple.cs source file as follows:
public class SimpleAppForm : Form { ... static void Main() { SimpleAppForm hoops = new SimpleAppForm(); Application.Run(hoops); } ... }
The HOOPS/Winforms integration consists of a customized .Net Winforms 'Form' and 'Panel' called HNForm and HNPanel, as diagramed up above. (Again, the Panel will get attached to the form.) As many pairs of these objects can be created as needed to implement the GUI's design. Your application should define custom HNForm and HNPanel classes, as shown in csharp_simple source.
Custom HNPanel definition taken from the csharp_simple project's SimpleHNPanel.cs source file:
public class SimpleHNPanel : HNPanel { // Constructor which calls the Init() method of the class public SimpleHNPanel(): base() { Init(); } // This method will set up the default HOOPS/MVO view for the panel and attach a HOOPS/MVO default operator public new void Init() { // contents reviewed later } ... }
Here is custom HNForm definition taken from the csharp_simple project's SimpleHNForm.cs source file. Note that it creates the custom HNPanel object and initializes the form:
public class SimpleHNForm : HNForm { ... // Sets up the panel and window public void Init(object sender, EventArgs e) { // set up the panel that HOOPS will draw into m_pHNPanel = new SimpleHNPanel(); Instance = (this.IsMdiChild) ? ((HForm)this.MdiParent).win_count : 0; // first set up the window, then set the axis triad options Init_Form(); m_pHNPanel.m_pHView.Update(); m_pHNPanel.m_pHView.SetAxisMode(AxisMode.AxisOn); m_pHNPanel.m_pHView.AdjustAxisWindow(); ... } ... }
Finally, we can note that the fileNew and fileOpen methods of main app object (SimpleAppForm, again located in csharp_simple.cs) will create the custom HNForm object:
public class SimpleAppForm : Form { ... private void fileNewMenu_Click(object sender, EventArgs e) { SimpleHNForm frmchild = new SimpleHNForm(); frmchild.MdiParent = this; frmchild.Show(); win_count++; } private void fileOpenMenu_Click(object sender, EventArgs e) { OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = "Open"; dlg.Filter = "HMF/HSF files (*.hmf, *.hsf)|*.hmf;*.hsf" + "|All files (*.*)|*.*"; if (dlg.ShowDialog() == DialogResult.OK) { SimpleHNForm frmchild = new SimpleHNForm(); frmchild.MdiParent = this; frmchild.Show(); frmchild.LoadFile(dlg); win_count++; } dlg.Dispose(); } ... }
One global pointer to a HOOPS/MVO HDB object should be declared and initialized in your application's main class. The csharp_simple app does this in the main SimpleAppForm constructor (located in csharp_simple.cs) :
public class SimpleAppForm : Form { ... // Constructor which initializes hoops database and GUI features of the main window */ public SimpleAppForm() { win_count = 1; m_pHDB = new HDB(); m_pHDB.Init(); ... } ... }
Multiple HBaseModel objects can be created as needed. The csharp_simple app creates one for every SimpleHNPanel object (i.e., there is a one-to-one mapping of HBaseModel to SimpleHNPanel objects) and does so in the SimpleHNPanel::Init :
public class SimpleHNPanel : HNPanel
{
...
public new void Init()
{
...
m_pHModel = new HSimpleModel();
m_pHModel.Init();
...
}
}
Multiple HBaseView objects can be created as needed, with one object usually being created for each HNPanel. The HBaseView needs a valid native GUI window id passed to its constructor on object creation; this information is used to connect a HOOPS/3dGS output driver instance to a HNPanel. This requires that the HNPanel, to which the HBaseView will be attached, already exist prior to creating the HBaseView object.
Your app should create the HBaseView object in your overloaded HNPanel::Init method. Here is the csharp_simple example taken from csharp_simple.cs :
public class SimpleHNPanel : HNPanel { ... public new void Init() { ... m_pHView = new HSimpleView(m_pHModel, "?picture" + SimpleHNForm.Instance, "opengl", "", winid); m_pHView.Init(); ... } }
The HBaseView class and HNPanel class contain pointers to a HSelectionSet object. A selection set class would typically be created and initialized during HNPanel initialization. After creation, it's important to pass the HSelectionSet operator into HBaseView by calling HBaseView::SetSelection. The csharp_simple app does this in the overloaded method of HNPanel::Init method:
public class SimpleHNPanel : HNPanel { ... public new void Init() { ... // view and model initialization goes here, and was discussed previously // Set up the custom HSelectionSet object m_pHSelection = new HSimpleSelectionSet(m_pHView); m_pHView.SetSelection(m_pHSelection); m_pHView.GetSelection().Init(); ... } }
The HBaseView class and HNPanel class contains pointers to a HBaseOperator object. A default operator would typically be created and initialized during HNPanel initialization. After creating, you should set it to be the current operator by calling the utility method HNPanel::SetCurrentOperator.. The csharp_simple app does this in the overloaded method of HNPanel::Init method:
public class SimpleHNPanel : HNPanel { ... public new void Init() { ... // Set the default operator m_pHOperator = new HOpCameraManipulate(m_pHView); m_pHView.SetCurrentOperator((HBaseOperator)m_pHOperator); ... } }