Example : Geomview
The following procedure explains how to use AVS/Express 6.0 and Microsoft Visual
C++ 6.0 to create an ActiveX Control (the technology formally known as OCX's).
This is as an alternative to using AVS/Express to generate the OCX directly
using the Save_Compiled_Project options. The advantages of this methodology
are that you have none of the limitations regarding control naming or number
of properties. It is also more robust, works in a wider variety of MS applications
and is transportable.
The application that you will be creating is called Geomview. It is a simple OCX that allows you to display AVS/Express geometry files. This is the same Express application that the MFC example uses with the exception of shortcuts made in regard to access of the readers. The separate class for the reader is kept for future expansion purposes.
Part 1. AVS/Express 6.0
flibrary+global
GEOMVIEW
<out_hdr_file = "geomviewOMX.h", out_src_file = "geomviewOMX.cxx">
{
macro CGeomFileReader <export_cxx=3> {
ACread_geom ACread_geom {
array_flag = 0;
filename => "C:\\express\\data\\geom\\teapot.geo";};
GDM.DataObject DataObject {in => <-.ACread_geom.out_field;
};
olink obj <export=2> => .DataObject.obj;
};
macro C3DViewer <export_cxx=3> {
UIwinHandle handle {
window <export=3>;
widget <export=3>;
event <export=3>;
hdc <export=3>;};
imlink objs <export=2>;
GDM.Mscene3D scene {
Top.child_objs => <-.<-.objs;
View.View.renderer = 0;
View.View.handle => <-.<-.<-.handle;};
GDtrack_edit track {
camera => <-.scene.View.View.picked_camera;
obj => <-.scene.View.View.picked_obj;
view => <-.scene.View.View;
mode <export=3>;
x <export=3>;
y <export=3>;
time <export=3>;
int state <export=3> => .event;};
};
};
Part 2. MS Visual C++ 6.0
geomviewOMX.cxx
geomviewOMX.h
express.c
express.h
v/appl.vo
xexpress.cxx
XP_PATH=c:\projects\geomview
geomviewOMX.cxx
geomviewOMX.h
express.c
xexpress.cxx
In the "Settings for:" list box on the left side of the dialog box, change the selection so that only the Win32 Debug version of the project is selected.
Go back to the C/C++ tab. In the "Preprocessor" category, remove the _DEBUG preprocessor directive and add MSDOS to the list. Go to Project_Options, change the /MDd flag in the Project Options area to /MD.
Go to the link tab.
1. If you choose to use the debug version, you will need to make the changes to the link line. Go to Link->General, change mfc42.lib and mfcs42.lib to mfc42d.lib and mfcs42d.lib and add mfco42d.lib.You may have to specifically exclude mfc42.lib and mfc42s.lib in the Setting/Link/Input dialog.
Add the following in the ignore libraries typein in the Link->Input panel:
libc.lib,mfc42.lib,mfcs42.libOR
2. If you are using non debug then you will need to add the following in the ignore libraries typein in the Link->Input panel:
libc.lib,libcmt.lib,msvcrd.lib
Go to the Link->Input->Additional Library Path and typin:
..,.,$(XP_ROOT)\lib\pc,$(XP_ROOT)\lib\pc\pal,$(XP_ROOT)\lib\pc\fld,$(XP_ROOT)\lib\pc\nt_ui,$(XP_ROOT)\lib\pc\gmod,$(XP_ROOT)\lib\pc\ip_lib\inter,$(XP_ROOT)\lib\pc\ne,$(XP_ROOT)\lib\pc\om,$(XP_ROOT)\lib\pc\ag,$(XP_ROOT)\lib\pc\animator
You are now finished with the Project Settings dialog box.
#include "express.h"
OMmain_init(m_hInstance, m_hPrevInstance, "c:\\geomview\\Debug\\geomview -novcp",SW_SHOWNORMAL);
OMmain_uninit(1);
CGeomFileReader *GetReader() {return(m_reader);}
C3DViewer *m_viewer;
CGeomFileReader *m_reader;
1. WM_DESTROY,
2. WM_CREATE,
3. WM_LBUTTONDOWN,
4. WM_MOUSEMOVE,
5. WM_LBUTTONUP, and
6. WM_SIZE.
m_viewer = NULL;
m_reader = NULL;
if (m_viewer) {
m_viewer->push_ctx();
m_viewer->event = 3;
m_viewer->pop_ctx();
}
m_reader = new CGeomFileReader;
if (!reader) return -1;
m_viewer = new C3DViewer;if (OMadd_obj_ref((OMobj_id) m_viewer->objs,
(OMobj_id) reader->obj, 0) != OM_STAT_SUCCESS) {
MessageBox("Can't connect document and view geometry objects");
return -1;
}m_viewer->push_ctx();
m_viewer->window = (int) GetSafeHwnd();
m_viewer->widget = (int) GetSafeHwnd();
m_viewer->hdc = 0;
m_viewer->event = 1;
m_viewer->pop_ctx();
delete m_viewer;
delete m_reader;
if (m_viewer && GetCapture() != this) {
m_viewer->push_ctx();
if (nFlags & MK_CONTROL)
m_viewer->mode = 2;
else if (nFlags & MK_SHIFT)
m_viewer->mode = 1;
else
m_viewer->mode = 0;
m_viewer->x = (int) point.x;
m_viewer->y = (int) point.y;
m_viewer->state = 1;
m_viewer->time = (int) GetCurrentTime();
m_viewer->pop_ctx();SetCapture();
}
if (GetCapture() == this) {
ReleaseCapture();
m_viewer->push_ctx();
m_viewer->x = (int) point.x;
m_viewer->y = (int) point.y;
m_viewer->state = 3;
m_viewer->time = (int) GetCurrentTime();
m_viewer->pop_ctx();
}
if (GetCapture() == this) {
m_viewer->push_ctx();
m_viewer->x = (int) point.x;
m_viewer->y = (int) point.y;
m_viewer->state = 2;
m_viewer->time = (int) GetCurrentTime();
m_viewer->pop_ctx();
}
if (m_viewer) {
m_viewer->push_ctx();
m_viewer->event = 2;
m_viewer->pop_ctx();
}
You can test the OCX in the MSDEV ActiveX test container located in the Tools menu. The most likely place of error is at CGeomviewApp::InitInstance where OMmainInit is called. If the test container seems to hang, comment out the call to OMmainInit and rebuild and reregister. You should then get an empty control with the appropriate error messages. Make sure the avsenv file has correct pathways and the OMmainInit function has a valid path.
You are now finished with VC++ and MSDEV.
To use it in Visual Basic, startup Visual Basic, select the OLE icon from the toolbar and use the mouse to create the container framework in the default "Form1" and then choose Geomview Control from the list box of available controls.
To use it in MS Internet Explorer, download the MS ActiveX Control Pad from Microsoft (if you don't already have it) and follow directions for inserting an ActiveX control into an html document.
You can rotate, scale, or XY-translate the geometry using the left mouse button. To scale, hold the Shift key down while dragging. To translate, hold the Ctrl key down while dragging.
Additions to your control - Adding properties
The following section will describe how you can go about adding properties to
your control. These will be visible when you use the control in VC++ or VB (or
any other programming environment that will accept ActiveX controls.
Example : Color of DataObject (Scalar
property)
Properties are added to controls using ClassWizard. Start it up from View->ClassWizard
and then click on the Automation tab, making sure that CGeomviewCtrl
is showing in the Class name box. Press the Add Property .. button
to bring up the property dialog, and fill it in as described.
The External name of the property will be RedLight, and containers such as Visual Basic and Internet Explorer will use this when they want to manipulate it. The property will be of type BOOL, which we have chosen from the Type drop-down list that contains all the permissible types. A protected member variable called m_redLight will represent the property in the CGeomViewCtrl class. Choose the property implemented in your C++ code as a Member variable of the class.
Press OK to dismiss the ClassWizard, and go to the control class to look at the code. If you look in the header file, CGeomviewCtl.h, you'll find that Class Wizard has added declarations for the property variable and notification function.
// Dispatch maps
//{{AFX_DISPATCH(CGeomviewCtrl)
BOOL m_redLight;
afx_msg void OnRedLightChanged();
//}}AFX_DISPATCH
To ensure that the property is initialized correctly (redLight will be FALSE by default). So add line m_redLight = FALSE to the constructor :
CGeomviewCtrl::CGeomviewCtrl()
{
InitializeIIDs(&IID_DGeomview, &IID_DGeomviewEvents);
// TODO: Initialize your control's instance data here.
m_viewer = NULL;
m_reader = NULL;m_redLight = FALSE;
}
Using the Property Page
We can now use the property page object that we were given by the ClassWizard
to allow users to modify the values of the properties at design time. The property
page is already associated with the control, but at present it does nothing.
You can see the property page by placing the Geomview Control in the Test Container,
and then selecting the Edit | Properties... menu.
At the moment, the property page only contains the test string "TODO: Place controls to manipulate properties of Geomview control on this dialog". To fix this, open up the project's resources and find the property page dialog resource, which will be called IDD_PROPPAGE_GEOMVIEW. When you open it, you'll see that it is blank apart from the test string, which you can delete.
Add the checkbox to represent the
property, as follows:
Drag and drop a check box. Go to checkbox properties and specify the id as IDC_RED_LIGHT
Give the checkbox suitable labels, and place it on the dialog.
Use ClassWizard to associate variables with the controls on the page. Select the CgeomviewPropPage class, and click on the Member Variables tab. Highlight the IDC_RED_LIGHT ID, and press Add Variable ... to open the following dialog. Type in the values as below.
The member variable in the property page class will be called m_bRedLight, of type Bool.
If you go and look at the code in the property page class, you'll see that the DoDataExchange () function now contains code to transfer data.
void CGeomviewPropPage::DoDataExchange(CDataExchange* pDX)
{
//{{AFX_DATA_MAP(CGeomviewPropPage)
DDP_Check(pDX, IDC_RED_LIGHT, m_bRedLight, _T("RedLight") );
DDX_Check(pDX, IDC_RED_LIGHT, m_bRedLight);
//}}AFX_DATA_MAP
DDP_PostProcessing(pDX);
}
Add the below handler code to OnRedLightChanged method.
void CGeomviewCtrl::OnRedLightChanged()
{
// TODO: Add notification handler code
int status;
OMobj_id grp_id,col_id;/* Get object id of the viewer */
grp_id = m_viewer->obj_id();
/* Get the object id of the scene subobject of the viewer */
grp_id = OMfind_subobj(grp_id,OMstr_to_name("scene"),OM_OBJ_RW);/* Get the object id of Lights */
grp_id = OMfind_subobj(grp_id,OMstr_to_name("Lights"),OM_OBJ_RW);
/*Get tht subobject id of Lights within Lights */
grp_id = OMfind_subobj(grp_id,OMstr_to_name("Lights"),OM_OBJ_RW);
/* Get the first object in the Lights list */
OMget_array_val(grp_id, 0, &grp_id, OM_OBJ_RW);/* Get the col subobject id */
col_id = OMfind_subobj(grp_id,OMstr_to_name("col"),OM_OBJ_RW);/* Constrain the data type. */
int array_type = OM_TYPE_FLOAT;
// force type to float array
float *col = NULL;
/* Don't constrain dims */
int ndims = 0;
int dimension_size[OM_ARRAY_MAXDIM];/* Get color array in write mode */
status = OMget_array(col_id, &array_type,(char **)&col,&ndims, dimension_size, OM_GET_ARRAY_RW);if (status != 1) {
fprintf(stderr,"error occurred\n");
}/* Assign the lighting to red light RGB = {1,0,0}*/
col[0]=1;col[1]=0;col[2]=0;SetModifiedFlag();
ARRfree(col);
}
The above code adds red light to the teapot. Build the control and test it in the Test Container. If you select the red light check box , the Apply button will become enabled. Pressing this will add the red light property to the control.