Fixing param value passing problem into ActiveX control from html web page


This one was a hair-puller, hair-loser, you-name-it kinda problem I was facing the last few days. Visual Basic 6 or VB6, in short, not being my primary language was one of the reasons for this. But the main reason was almost nobody knew why an ActiveX control inside an html page doesn’t get passed the param value although most know how to build a simple ActiveX control in VB6.

Let me guide you through the entire process of creating the ActiveX and embedding it inside Html lest anybody should miss it.

VB6 Code for the ActiveX property getter & setter :

Dim m_propertyNameToBeExposedThroughParam As String

Public Property Get propertyNameToBeExposedThroughParam() As String
propertyNameToBeExposedThroughParam= m_propertyNameToBeExposedThroughParam
End Property

Public Property Let
propertyNameToBeExposedThroughParam(ByVal vNewValue As String)
'Some code
End Property

'The following lines are important for reading the param value from inside html page
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
m_
propertyNameToBeExposedThroughParam = PropBag.ReadProperty("propertyNameToBeExposedThroughParam", "")
End Sub

This is how an ActiveX control is embedded inside an html page:

<OBJECT name="activeXControlName" id="activeXControlID" width="widthInPixels" height="heightInPixels" classid="clsid:YOUR-32-DIGIT-CLASS-ID-FOR-THE-ACTIVEX-CONTROL">
<param name="propertyNameToBeExposedThroughParam" value="SomeValue"/>
<OBJECT>

This is pretty elementary thus far. In an ideal world, that should have sufficed for creating a property for the ActiveX and then exposing/consuming it through html. But hey life’s not perfect and so aren’t we 🙂 Hence read on.

Now, when I ran the html page inside the internet explorer browser window, lo & behold,  the property setters inside my ActiveX control was not firing.

I googled around for 3 days when I began trusting myself more than Googlers :). I observed that my IE was complaining whenever I ran the html inside the browser saying it is a potentially unsafe activeX. Initially, I didn’t pay heed to it for I ‘assumed’ all ActiveX controls give such warnings and guess what, they don’t say ‘Assumption is the root of all ‘f#$%-ups’ for nothing :). Then I started realizing that I might need to sign the control because IE wasn’t passing the parameter to my ActiveX for it was ‘unsigned’. You know what folks, “figuring out a problem is 90% of the solution”, IMHO. Because, after realizing the actual problem, Google asked me to pay a visit to “How To Implement IObjectSafety in Visual Basic Controls” whereby I learnt that I needed to implement IObjectSafety in my ActiveX control.

I am sure most understands implementing an interface in a non object oriented language like VB6 is less than trivial if I may use the euphemism.

According to the above article from Microsoft, one needs to create the following ‘Interface Definition Language File‘ (odl) file:

objsafe.odl

[
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);
}
}

Thereafter, one needs to convert the olb file to a Type Library (tlb) through a command like this:

MKTYPLIB objsafe.odl /tlb objsafe.tlb

Then one needs to do a Project menu -> References -> browse to and add Objsafe.tlb inside VB6.

After you are done making the reference, you need to add the following module to the ActiveX project and set the “Sub Main” as the “Startup Object”:

basSafeCtl

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

Now one needs to go to the code for the ActiveX UserControl itself and paste the following just after the “Option Explicit” statement like this:

Option Explicit

Implements IObjectSafety

And the final task is to paste the following code onto the end of the ActiveX UserControl code:

'Code related to ActiveX Container Signing
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

Now you are all ready to go. Just compile the ActiveX into an OCX and embed it inside the html like it mentioned at the top of the post. Do remember to change the class id in the object tag. The fastest way to get the class id for the ActiveX is to debug start the control inside a browser and take a look into the html source to grab the clsid or class id as it is called.

Advertisements

4 responses to this post.

  1. Posted by Scott Fossum on January 1, 2010 at 2:21 am

    I know this example was awhile ago but do you have all of your work in a zip file. I really need to be able to do this but can not quite follow what I need to do. I have part of it working I can get it now to not but up the error question but the rest of the usercontrol is never firing. Any help is really appreciated

    Reply

  2. Hi! I only reached the step where I have to convert the IObjectSafety to .tld, and don’t know how to the rest. How do you embed the OCX file in the html? and what tool did you use to convert to .OCX??
    Thanks!

    Reply

  3. I have managed to put a ActiveX control into an HTMl File but, how do I put the HTML File into a wordpress.com blog?

    Reply

  4. Posted by leandro on March 20, 2011 at 5:49 am

    Thanks man!!! You save my weekend.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: