Due to popular request, this lab is the Visual Basic 6.0 version of the ActiveX lab on MSDN (with some differences in the control we generate). The following lab has not been reviewed by the normal MSDN reviewers.
PrerequisitesBefore you begin this lab, you should have a basic understanding of Microsoft® Office InfoPath™ 2003. You should also have some familiarity with the Microsoft Visual Basic® version 6.0 integrated development environment (IDE). In addition, you should have a code-signing certificate so that you can sign your Microsoft ActiveX® controls.
ScenarioAt Contoso Corp., sales representatives use a series of related InfoPath forms to collect sales information. Many of these forms include a list that displays different prescription medicines. This list is used in conjunction with a text box that displays the current price for each medicine. As a form designer in the IT department, you can create a custom ActiveX control that encapsulates both controls into the same custom control. Also, you can embed code into the ActiveX control, which allows the latest price for each drug to be retrieved from an internal pricing database. Creating and distributing your own custom ActiveX control can save other form designers both time and effort. Instead of recreating the same set of controls over and over again, other designers can simply insert the ActiveX control you created from the Controls task pane in InfoPath.
Lab ObjectiveIn this lab you will learn how to do the following:
SetupBefore beginning this lab, install Microsoft Visual Basic 6.0 Professional for Windows® or Microsoft Visual Studio® 6.0. In addition, you will need to obtain a code-signing certificate from a trusted certificate authority (CA).
Exercises
Exercise 1: Create an ActiveX controlContoso uses a combination of list and text box controls on a frequent basis. As a member of the IT department, you need to write an ActiveX control containing both of these controls so form designers can simply insert this one custom control into their forms. You'll integrate the logic related to retrieving the current price for medications into the ActiveX control so that form designers don't have to worry about writing this code in the future.
Create a new ActiveX control
Mark the control as safe for scripting and initialization
[ uuid(C67830E0-D11D-11cf-BD80-00AA00575603), helpstring("VB IObjectSafety Interface"), version(1.0) ] library IObjectSafetyTLB { importlib("stdole2.tlb"); [ uuid(CB5BDC81-93C1-11cf-8F20-00805F2CD064), helpstring("IObjectSafety Interface"), odl ] interface IObjectSafety:IUnknown { [helpstring("GetInterfaceSafetyOptions")] HRESULT GetInterfaceSafetyOptions( [in] long riid, [in] long *pdwSupportedOptions, [in] long *pdwEnabledOptions); [helpstring("SetInterfaceSafetyOptions")] HRESULT SetInterfaceSafetyOptions( [in] long riid, [in] long dwOptionsSetMask, [in] long dwEnabledOptions); } }
MKTYPLIB objsafe.odl /tlb objsafe.tlb
Option Explicit Public Const IID_IDispatch = "{00020400-0000-0000-C000-000000000046}" Public Const IID_IPersistStorage = _ "{0000010A-0000-0000-C000-000000000046}" Public Const IID_IPersistStream = _ "{00000109-0000-0000-C000-000000000046}" Public Const IID_IPersistPropertyBag = _ "{37D84F60-42CB-11CE-8135-00AA004BB851}" Public Const INTERFACESAFE_FOR_UNTRUSTED_CALLER = &H1 Public Const INTERFACESAFE_FOR_UNTRUSTED_DATA = &H2 Public Const E_NOINTERFACE = &H80004002 Public Const E_FAIL = &H80004005 Public Const MAX_GUIDLEN = 40 Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _ (pDest As Any, pSource As Any, ByVal ByteLen As Long) Public Declare Function StringFromGUID2 Lib "ole32.dll" (rguid As _ Any, ByVal lpstrClsId As Long, ByVal cbMax As Integer) As Long Public Type udtGUID Data1 As Long Data2 As Integer Data3 As Integer Data4(7) As Byte End Type Public m_fSafeForScripting As Boolean Public m_fSafeForInitializing As Boolean Sub Main() m_fSafeForScripting = True m_fSafeForInitializing = True End Sub
Implements IObjectSafety
Private Sub IObjectSafety_GetInterfaceSafetyOptions(ByVal riid As _ Long, pdwSupportedOptions As Long, pdwEnabledOptions As Long) Dim Rc As Long Dim rClsId As udtGUID Dim IID As String Dim bIID() As Byte pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER Or _ INTERFACESAFE_FOR_UNTRUSTED_DATA If (riid <> 0) Then CopyMemory rClsId, ByVal riid, Len(rClsId) bIID = String$(MAX_GUIDLEN, 0) Rc = StringFromGUID2(rClsId, VarPtr(bIID(0)), MAX_GUIDLEN) Rc = InStr(1, bIID, vbNullChar) - 1 IID = Left$(UCase(bIID), Rc) Select Case IID Case IID_IDispatch pdwEnabledOptions = IIf(m_fSafeForScripting, _ INTERFACESAFE_FOR_UNTRUSTED_CALLER, 0) Exit Sub Case IID_IPersistStorage, IID_IPersistStream, _ IID_IPersistPropertyBag pdwEnabledOptions = IIf(m_fSafeForInitializing, _ INTERFACESAFE_FOR_UNTRUSTED_DATA, 0) Exit Sub Case Else Err.Raise E_NOINTERFACE Exit Sub End Select End If End Sub Private Sub IObjectSafety_SetInterfaceSafetyOptions(ByVal riid As _ Long, ByVal dwOptionsSetMask As Long, ByVal dwEnabledOptions As Long) Dim Rc As Long Dim rClsId As udtGUID Dim IID As String Dim bIID() As Byte If (riid <> 0) Then CopyMemory rClsId, ByVal riid, Len(rClsId) bIID = String$(MAX_GUIDLEN, 0) Rc = StringFromGUID2(rClsId, VarPtr(bIID(0)), MAX_GUIDLEN) Rc = InStr(1, bIID, vbNullChar) - 1 IID = Left$(UCase(bIID), Rc) Select Case IID Case IID_IDispatch If ((dwEnabledOptions And dwOptionsSetMask) <> _ INTERFACESAFE_FOR_UNTRUSTED_CALLER) Then Err.Raise E_FAIL Exit Sub Else If Not m_fSafeForScripting Then Err.Raise E_FAIL End If Exit Sub End If Case IID_IPersistStorage, IID_IPersistStream, _ IID_IPersistPropertyBag If ((dwEnabledOptions And dwOptionsSetMask) <> _ INTERFACESAFE_FOR_UNTRUSTED_DATA) Then Err.Raise E_FAIL Exit Sub Else If Not m_fSafeForInitializing Then Err.Raise E_FAIL End If Exit Sub End If Case Else Err.Raise E_NOINTERFACE Exit Sub End Select End If End Sub
Add controls to the ActiveX control
Specify properties for an ActiveX control
Add code to retrieve pricing information
Private Sub Combo1_Click() Text1.Text = Combo1.ListIndex End Sub
Compile the ActiveX control
Exercise 2: Making ActiveX controls available to usersIf you use a custom ActiveX control in a form, you must take measures to ensure that the control is installed and registered on users' computers. If you don't do this, users won't be able to open the form to fill it out. You can use InfoPath to package an installation .cab file with your form template. This .cab file installs the ActiveX control on users' computer (with their permission) and registers it for use. The following exercise shows you how to create this file.
Obtain the necessary command-line toolsNote Certain command-line tools are necessary for signing the package file. If you do not have the following files, you can download them from the Web.
Obtain a test certificate
Prepare your certificate for signingIf your certificate file is a .cer file, you will need to generate a .spc file.
Sign the ActiveX control
Exercise 3: Add your ActiveX control to InfoPathNow that the ActiveX control is complete and you have packaged it, it is ready to be used within InfoPath. In the following exercise, you'll learn how to add the control to the Controls task pane.
Add the ActiveX control to the Controls task pane
Add the ActiveX control to your form
SummaryIn the previous exercises, you learned how to write a custom ActiveX control that meets all requirements for use in InfoPath. You also learned how to deploy the control to users' computers, how to add the control to the InfoPath Controls task pane, and how to insert it into the form.