VB6 and CCW's
Hi,
I need to start writing a set of classes that will be used by our VB6 application. Because I have'nt done much in the past with COM interop, I was getting a bit confused as to what attributes I need to use on my managed classes. for example, do I need GuidAttribute and ClassInterface and ProgId, or just a few of these?Remebering that these .NET classes will only be used by our VB6 code, nothing else.
Thanks for your help
Graham
[443 byte] By [
Gravy] at [2007-12-16]
.NET application cannnot speak directly to a COM component.The programming and binary models are totally different.
.NET needs some metadata to figure out what the COM component is all about.
Classic COM components can interoperate with the .NET runtime through an interop layer that will handle all the
plumbing underneath between the unmanaged and managed worlds and vise versa.
Step to use VS.NET DLL in VB6 (CCW - COM Callable Wrapper )
1)create a class file from VS.NET
2)compile it using VS command prompt :
vbc /target:library dotnetclass1.vb
3)export its tlb (Type Library) file by issue command :
regasm /codebase /tlb dotNetClass1.dll
4)open VB6 editor , from the reference menu , add the reference that point to the
tlb file that u have created just now
5)finally , u have succesfully added in the reference to vb6 .
Hi,
The GuidAttribute, ClassInterface and ProgId attributes are optional and if not specified are automatically generated when you generate the CCW using the SDK tools.
Here's a good guide for consuming .NET Assmeblies from COM:
http://builder.com.com/5100-6387_14-1045146.html?tag=sc
Regards,
Vikram
Thanks Vikram, however there is one thing not clear...
I have managed to create a .NET class that is accessable from VB without much trouble. However, the article did mension that the assembly must be registered with the GAC. At first I didn't bother putting my COM visible .NET component in the GAC and it all worked ok - why?
However, a little later I was getting an exception when I used the binary formatter to deserialize an object. The exception was because it could find the assembly in order to deserialize the stream.
So I guess what I wanted to know is: IS the GAC a must when it comes to COM and the CCW, or is there anyway round it?
Graham
Thanks for the link Vikram but if I simply use regasm with the codebase option my VB application throws an exception when my .net component uses a memory stream and a binary formatter to deserialize itself. If however, I register using Regasm without the codebase option and then gacutil /i to install the assembly in the GAC the serialize code works ok.
Would this be because when de-serializing the FCL is using reflection and it doesn't know where to find my type unless it is in the GAC? But there again this works fine (i.e. my type doesn't need to be in the GAC) when used from a managed host rather than through COM and the VB6 host.
Graham
Yes you are right. The reflection seems to be the reason in your case for the error. Having an assembly in the GAC should not be much of an issue I think.
Regards,
Vikram
Thanks for the confirmation.
A quick question then: In VS 2005 what is the best way to develop projects that need GAC registering. Is there a project setting that tell the IDE to register in the GAC once compiled or should I be adding pre and post build steps. Pre-build would unregister using Gacutil /u, and post-build would register the newly compiled assembly?
Again, thank you for your help.
Graham
Oh no, it gets worse. I've been thinking about this and why it is working ok from a managed host and not from COM interop. Here's my thinking...
The first unknown for me right now is what AppDomain is being used when my .NET class is being instantiated through COM? I'm presuming it is some system defined AppDomain. I'm guessing that if I could created the AppDomain for my VB6 com interop session (lifetime of my VB6 app) then I could set the AppBasePath and hopefully the assembly resolver that reflection uses would look in the VB6 application folder to resolved my type first, then try the GAC. Or is it the other way round, looking in the GAC then try the application folder if it's not found. Again I'm guessing but if I put my assembly in the System32 folder then I wouldn't have to use the GAC (don't worry, I'm not going to do that.).
The biggest problem I have with the GAC is that I suspect it's going to be hard developing with.
Is there any truth in my ramblings?
Graham
Hey, I seem to be having a good conversation with myself

I think I have sorted it now. It actually turned out to be relatively simple. Anyway, this is what I believe:
My VB application failed when my managed COM components used reflection to create a type that was not in the GAC. When the assembly resolver tries to resolve a type it looks in the GAC, if it wasn't found there then it will look in the AppDomains BaseDirectory. The AppDomain's BaseDirectory is usually the directory of the exe. Hence, when the managed host went to create a type the resolver fails to find it in the GAC but it does find it in the exe's directory. When used in VB it actually works exactly the same, the only catch is that when the VB app is run from VB6 IDE the exe is actually VB6.EXE in c:\program files\Microsoft visual studio\VB98. If I compiled the VB app to an EXE and put it in the same directory as all my other assemblies then the AppDomain BaseDirectory is as expected and all my types can then be resolved.
Sorry for the long (pointless? Wrong?) posts before.
Graham
Good to know that you have it resolved
The attributes are optional, yes - as is defining a class interface - but these are important things to do or you will have development and deployment hastles.
I've answered many, many questions relating to these problems over the years and after joining Microsoft posted guidelines to follow on my blog at http://blogs.msdn.com/heaths/archive/2005/03/09/391358.aspx.
If you let GUIDs for the classes, interfaces, and typelib (an [assembly: GuidAttribute]) they will eventually change and your VB6 client will be broken. Not defining a class interface explicitly means that refactoring your class - even just re-ordering the methods - could break any early- or late-bound (automation) client calling into your interface.