Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29423 Discussions

Handling COM events in Visual Fortran

John_B__Walter
Beginner
620 Views

Successful Intel Visual Fortran code for controlling a laboratory device that has a COM (ActiveX) interface

My device, from Protean Instrument Corporation, is controlled via an OCX file which is registered on the computer. It includes a sample changer and and alpha/beta counter, and has regular COM methods to control these features and several COM events it can issue for things such as sample changer done and count done.

The Fortran Module Wizard is used to create a module for the device, but in order to be able to access the events the device must be added to a dialog, and the dialog must be active. To add the device to a dialog, in the Dialog Editor right click in the Dialog box and select Insert ActiveX Control... Scroll down the list of registered controls until you find the one you want. Since you probably want to provide access to the methods of the device, add additional components to the dialog for those, and hook those (with DLGSETSUB) to your code which has USE module line for the device module, and calls the appropriate subroutine in the module. Your code will need the pointer for the device, but to get this the dialog must already exist (either with dlgmodeless or dlgmodal) so you will need to assign a dialog init routine to the dialog box with dlgsetsub. When that routine is called with callbacktype==dlg_init, you can usedlgget(dlg,idc,OBJ,dlg_idispatch), where dlg is the dialog structure, idc is the ActiveX object in the dialog, and OBJ is the returned IDispatch pointer. OBJ will be used in all calls to the object, and also to connect the COM events to your event handlers. The module code gives the calling convention that each of your event handlers must have. For instance, for count done my module has
INTERFACE
! MEMBERID = 3
SUBROUTINE $DPIC9550frEvents_CountDone($OBJECT, nAlphaCounts, nBetaCounts, nGuardCounts, nTenthSeconds)
INTEGER(INT_PTR_KIND()), INTENT(IN) :: $OBJECT ! Object Pointer
!DEC$ ATTRIBUTES VALUE :: $OBJECT
INTEGER(4) :: nAlphaCounts
!DEC$ ATTRIBUTES VALUE :: nAlphaCounts
INTEGER(4) :: nBetaCounts
!DEC$ ATTRIBUTES VALUE :: nBetaCounts
INTEGER(4) :: nGuardCounts
!DEC$ ATTRIBUTES VALUE :: nGuardCounts
INTEGER(4) :: nTenthSeconds
!DEC$ ATTRIBUTES VALUE :: nTenthSeconds
!DEC$ ATTRIBUTES STDCALL :: $DPIC9550frEvents_CountDone
END SUBROUTINE $DPIC9550frEvents_CountDone
END INTERFACE

so my code has
if (DlgSetCtrlEventHandler(DLG,IDC, Ev_CntDon, mID_cntDon)/=0) &
call post_msg("didn't hook Ev_cntDon") ! post user message

and includes subroutine Ev_CntDon :

SUBROUTINE Ev_CntDon($OBJECT, Alph, Beta, Guar, tent)
USE PIC9550fr
implicit none

INTEGER(INT_PTR_KIND()), INTENT(IN) :: $OBJECT ! Object Pointer
Integer*4, INTENT(IN) :: Alph, Beta, Guar, tent
!DEC$ ATTRIBUTES VALUE :: $OBJECT, Alph, Beta, Guar, tent
!DEC$ ATTRIBUTES STDCALL :: Ev_CntDon1

call update_counts(alph, Beta, Guar, tent)
call Ev_CntDonX
end subroutine Ev_CntDon

As the documentation notes, the object in the event handler is the event object, not the ActiveX object. Presently I find no value in this argument, since there is no way to connect the two. In that I have multiple objects of the same type, I actually have multiple copies of the event routines, one for each of my objects, so that I know which ActiveX object is involved.

Since the Dialog has to remain active for the ActiveX object to be available, you may need to have your code start another thread so that you can have an idle loop to handle details that you can't connect to various controls on your dialog. My routine post_msg saves the messages in a queue. My Update_threadloopchecks to see if any messages have been posted and displays them one by one. Also method calls are queued in case the device is not ready to proccess them, and the update_thread passes them onto the device as it is ready. I start my update_thread from the dlg_init routine since it accesses the ActiveX objects and that requires themto be available. My main program ends with the calls to dlgmodal and dlgUnInit.

Also as the documentation points out, the code must call COMInitialize prior to calling DlgInit.

0 Kudos
1 Reply
Steven_L_Intel1
Employee
620 Views
Thanks for the great contribution!
0 Kudos
Reply