Search code examples
c++comvb6olesxs

Strange Case of the missing method: SXS and Controls.Add results in "object doesn't support this property or method"?


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:

  1. Create an OCX project in VB6
  2. Add a user control
  3. Add a method, e.g. DoSomething to the control
  4. Create an exe project
  5. Add the control to form, e.g. UserControl1
  6. In an event call DoSomething
  7. 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>

Solution

  • 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.