I have a project written in VB6 that uses a UserControl, The project runs fine when the OCX is registered, but if I run the same project with a side by side manifest it results in an error.
I can use the Control with no problem as long as it's loaded statically (added before on the form) but if I add a dynamic control to form on any use of the new control (property or method) I get this error:
Object doesn't support this property or method
This error can be reproduced this way:
- Create an OCX project in VB6
- Add a user control
- Add a method, e.g.
DoSomething
to the control - Create an exe project
- Add the control to form, e.g.
UserControl1
- In an event call
DoSomething
Load dynamically Like:
Dim y As Control UserControl1.DoSomething '<-------- CASE(1) THIS IS ALLRIGHT!' Set y = Controls.Add("Project1.UserControl1", "y") y.DoSomething '<---- (CASE 2) THIS WILL FAIL USING SXS'
I tracked the error in WinDbg back to IDispatch::GetIDsOfNames
that when called in second case will fail.
Any Idea?!
Edit: My bad, Here is the manifest.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="client.exe" version="1.0.0.0" type="win32" processorArchitecture="x86"/>
<file name="Project1.ocx">
<comClass
clsid="{C8CF7991-A8F2-4360-9404-03C9A052C245}"
description="Project1.UserControl1"
tlbid="{47853CCC-7BE6-4377-9C82-38A0B7755F65}"
threadingModel="apartment"
miscStatusContent="recomposeonresize,cantlinkinside,insideout,activatewhenvisible,setclientsitefirst"
progid="Project1.UserControl1"/>
<typelib
tlbid="{47853CCC-7BE6-4377-9C82-38A0B7755F65}"
version="1.0"
helpdir=""
flags="control,hasdiskimage"/>
</file>
<comInterfaceExternalProxyStub
iid="{0E4F313E-7EF3-4FE6-9591-9F7D2D819AEE}"
name="UserControl1"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"/>
<comInterfaceExternalProxyStub
iid="{53307849-4F14-4A59-B0CA-DE4950CE499D}"
name="UserControl1"
proxyStubClsid32="{00020420-0000-0000-C000-000000000046}"/>
</assembly>
The short answer: This is a known problem with VB6. The "Controls.Add" statement does not work with side-by-side. Try using a "Load" statement instead.
The long answer: VB6 compiles the CLSID of the control into the executable even though you use a ProgID to create the control. Execution of "Control.Add" verifies that the CLSID is the same. Don't ask why, this is a mystery better not touched. At the same time win32 side-by-side (as opposed by .Net side-by-side -- another topic) must be prepared to handle the same ProgID being used by more than one manifest (when you flip-flop between activation contexts, for example) so it internally generates a new, temporary, CLSID for each ProgID. In the end when you call CLSIDFromProgID you will get the temporary CLSID. If you then call CoCreateInstance it works fine - sxs honors the CLSID. But if you go looking for the CLSID anywhere (registry, your internal table) you will not find it. And here comes the VB6 program calling CLSIDFromProgID then checking if the CLSID it receives is in the internal table. Fail.
This is a case of VB magic that is incompatible with manifests. The problem stems from VB's behavior with user control references. Even if you strongly Dim a variable
As UserControl
, accessing methods/properties on this reference is late-bound! VB creates an extension class on each referenced user control to expose common methods (like SetFocus, Move, etc) so when you Dim somethingAs UserControl
this is compiled not as a reference to theUserControl
in the controls typelib, but to aVBControlExtender
inherited class, in any case an auto-generated wrapper onUserControl
.What I'm doing since discovering the chapter on user controls in Curland's Advanced Visual Basic 6 book is to create a custom
Direct User Controls
typelib that forces VB not to use wrappers. Basically it looks like this:In my projects a keep "direct" references, calling methods early-bound. I use
Controls.Add
like thiswhere casting helper is something like this
This snippet works as expected in the three cases: in VBIDE, with registered controls and with registration free controls.