I have a ActiveX COM control and its source code. I wanted to change one of the method's input parameter, so I changed the IDL etc and generated the COM DLL and TLB.
But when I imported the COM DLL in a .NET project the method had retained its old signature. So I tried to generate the ActiveX DLL using AXIMP (though it is all the same, I wanted to give a try).
Still the method's signature did not change to what I changed to.
But when I generated the interop DLL using TLBIMP from the TLB file generated, the method signature changed correctly.
Where can I be wrong?
Thanks.
There are a lot of manual steps involved so it easy to miss one. It rather depends on how you imported the type library, there's more than one way to do it. If you picked the reference from the Add Reference + COM tab then the likely mistake is that you forgot to re-register the new COM server. Or you accidentally picked the old one instead of the new one, which can happen when you change the guids, like you should, and forgot to cleanup the old one. Cleaning up is pretty important and easily missed since it needs to be done before you rebuild the COM server. You can end up with a lot of garbage in the registry.
And yes, using Tlbimp.exe directly is the most reliable way to avoid accidents. Since you run it directly on the type library and don't use the registry at all.
A recipe for having the least possible amount of trouble could look like this:
Unregister the old COM server first by running regsvr.exe -u
Delete the old DLL and TLB files
Change the IDL to add your new method
Assign a new IID for the interface you changed
Assign a new CLSID for the coclass that uses the interface
Increment the library version
Change the name of the output DLL, favor including the major+minor version in the name
Build the new COM server
Register the server with regsvr32.exe
Run Tlbimp.exe to generate the interop library
Remove the reference to the old interop library in your .NET project
Use Add Reference + Browse to add the new interop library
Skipping any of these steps can invoke build trouble, registry pollution, DLL Hell and having an all-around lousy wrecked day without getting anything done.
Related
I created a test COM project for .Net 4.0. Then I register it with the regasm:
RegAsm /codebase TestCom.dll /TLB
And it works correctly in JavaScript:
var app = new ActiveXObject("TestCom.TestClass");
app.Message1("123");
I want to use TestCom.TestClass from another C# project for .Net 3.5, but when I try to add the reference to this project, I get an error about higher framework version. The "Add Reference" dialog(section COM) only shows reference to a tlb file, not dll.
Is this the way it should be? When I try to add a reference to the tlb file I get the error:
"Add a reference to the .NET assembly instead"
How can I create an instance of TestCom.TestClass from another C# project for .Net 3.5?
The IDE can tell from the type library that you selected that the COM server is implemented in .NET. And it just refuses to let you add it, doing this doesn't make sense. It will work just as well, in fact better, when you add a reference to the .NET assembly instead.
It's not like you can't fool the machine, you can use late-binding with the dynamic keyword. No way for the IDE to interfere with that. But you are not actually testing the COM server the way an unmanaged client is going to use the server. The CLR will discover at runtime that the COM server is in fact a .NET server and shortcuts the plumbing. In other words, it will not create an RCW for the server. And the server won't create a CCW, this now works the exact same way it would have worked if you had added the assembly reference.
So it really does make no sense to do it this way. Just add the assembly reference and enjoy the many advantages you get from having IntelliSense support and static type checking. And test your COM server code the way you test any .NET library. What you don't test, and fundamentally cannot test, is the COM interop layers. Which is okay, it's not like it ever really is a problem. And if it is some for some mysterious reason then it is not like you can ever do something about it, the COM interop inside the CLR is a black box anyway.
All you need to do in addition to testing the server code is to perform an integration test. Just make sure that the actual unmanaged client can in fact use the server. You did, you already know it works well from Javascript.
Situation
I run a build system that executes many builds for many project. To avoid one build impacting another we lock down the build user to only its workspace. Builds run as a non privileged users who only have write ability to the workspace.
Challenge
During our new build we need to use a legacy 3rdparty DLL that exposes its interface through COM. The dev team wants to register the build(regsrv32.exe) but our build security regime blocks this activity. If we relax the regime then the 3rdparty DLL will impact other builds and if I have two build which need two different versions I may have the wrong build compile against the wrong version (a very real possibility).
Question
Are there any other options besides registration to handle legacy DLLs which expose their interface via COM?
Thanks for the help
Peter
For my original answer to a similar question see: TFS Build server and COM references - does this work?
A good way to compile .NET code that references COM components without the COM components being registered on the build server is to use the COMFileReference reference item in your project/build files instead of COMReference. A COMFileReference item looks like this:
<ItemGroup>
<COMFileReference Include="MyComLibrary.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMFileReference>
</ItemGroup>
Since Visual Studio provides no designer support for COMFileReference, you must edit the project/build file by hand.
During a build, MSBuild extracts the type library information from the COM DLL and creates an interop assembly that can be either standalone or embedded in the calling .NET assembly.
Each COMFileReference item can also have a WrapperTool attribute but the default seemed to work for me just fine. The EmbedInteropTypes attribute is not documented as being applicable to COMFileReference, but it seems to work as intended.
See https://learn.microsoft.com/en-ca/visualstudio/msbuild/common-msbuild-project-items#comfilereference for a little more detail. This MSBuild item has been available since .NET 3.5.
It's a shame that no-one seems to know anything about this technique, which to me seems simpler than the alternatives. It's actually not surprising since I could only find just the one above reference to it on-line. I myself discovered this technique by digging into MSBuild's Microsoft.Common.targets file.
There's a walkthrough on registration-free COM here:
http://msdn.microsoft.com/en-us/library/ms973913.aspx
And excruciating detail here:
http://msdn.microsoft.com/en-us/library/aa376414
(the root of that document is actually here: http://msdn.microsoft.com/en-us/library/dd408052 )
Also, for building in general, you should be able to use Tlbimp or tlbexp to create a TLB file that you can use for building, assuming the point of registering is just to be able to compile successfully, and not to run specific tests.
Installation tools such as Installshield can extract the COM interfaces from the DLLs and add them to the registry. It can also use the self-registration process of the DLL (which I believe is what regsvr does), but this is not a Microsoft installer best practice.
in .NET COM is normally done thru Interop in order to register .DLL in .NET they are called Assemblies and that can be done several ways.. by adding references via VS IDE at the project level, or writing code that Loads and unloads the assembly.. by .Config file that haas the reference to the assembly as well as the using of that reference within the project... GAC.
If you have access to the 3rd party .DLL's you can GAC them, and reference them in your project
you can add a using to your .cs file header as well as add the reference to the project by right clicking on reference --> add Reference ...
you can also do the above step as well as set the copy local = true in the properties for that .dll.. I hope that this gives you some ideas.. keep in mind that .NET assemblies are Managed code so there are several ways to Consume those 3rd party .DLL's using other methods within C# like LoadFromAssembly ect..
Thanks for all the help.
We changed from early-binding to late-binding because we never really needed the DLL at compile time. This pushed the registration requirement from the build server to the integration test server (where we execute the installer which handles the registration). We try to keep the build system pristine and have easy-to-reset integration systems.
Thanks again
Peter
I can't find any good resources online about what this is. It's required in one of the steps for deploying a .NET assembly for use by COM, but there is no detail what it is or what it means.
As an example, here is something from the book I'm reading on Interop:
Next you need to create a resource script MyExampleAssembly.res with the following statement:
(This gets run at the VS command prompt in the directory where you just exported your .NET component using the Type Library Exporter Utility)
IDR_TYPELIB1 typelib "MyExampleAssembly.tlb"
What is IDR_TYPELIB1?
Also, it's not working for me. I have tried running this and it doesn't compile. I get a message "'IDR_TYPELIB1' is not recognized as an internal or external command".
It is a macro that's normally auto-generated when you use the unmanaged resource editor. Just use a constant, 1 is fine. You are also using the wrong tool to compile the resource script by the sound of it, you must run rc.exe. It translates your .rc file to a .res file that the C# compiler can use.
Embedding the type library is optional btw, it is not something that COM Automation requires. It is not a very good idea to do this in a managed project since the .tlb is generated after building the project. Instead of before, as happens in a native COM project. You create it with regasm.exe, /tlb option or the tlbexp.exe utility. Your client can do this too.
I have an image editor user control(c# .net v2.0). Its used in thousands of computers as an activex component. I want the component also be available for windows forms and possible other uses.
For activex usage i add <object> tag in html code and call the component with clsid(a static guid). So if i build and distribute a newer version it works without changing any client code.
I want windows forms to be able use the same distributed libraries. And they should not reference a specific version so i can update the component without changing the programs that use it.
I use regasm to register for com. But i dont know how to reference it from visual studio(like referencing with clsid?)
May be in visual stuido when i choose add reference and select COM tab i shuld see my component in the list.
note: i tried to add the assemly to the global cache using these lines but it did not work out-or i coulnt understand if anything has changed :)
"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\tlbexp" ImageEditorComp.dll /out:ImageEditorComp.tlb
regasm /tlb:ImageEditorComp.tlb ImageEditorComp.dll
"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\gacutil" /I ImageEditorComp.dll
Any suggestions appreciated,
Regards
This is not possible, you'll invoke the infamous and dreaded DLL Hell problem. A stone cold hard rule in COM is that you have to change the [Guid] attribute values on public interfaces when you make a breaking change in either the publicly visible interfaces or the implementation of them. Changing the guids ensures that you don't overwrite the registry keys of an old version of your component when you use Regasm.exe. Existing programs that use your component and were not recompiled to use the latest version will continue running without problems. The typical outcome of DLL Hell is a nasty hardware exception like AccessViolation, very difficult to troubleshoot.
None of which applies in your specific case here. There is no point in trying to use the component through COM. It is a .NET assembly, just add the reference to it directly. The IDE will in fact stop you from adding a reference to the interop library. But not the .tlb. The GAC keeps you out of DLL Hell, assuming you properly increment [AssemblyVersion].
I figured out a solution.
To explain step by step:
1- Create the component with all needed properties for com.(Sign the assembly, use interfaces for com, make assembly com visible)
On the client machine
2- Register the assembly with regasm(i recommend adding safety flags too).
3- Add the assembly to the global cache using gacutil(or msi installer)
I figured out when you call a specific version of an assembly gac is searched in the first place so if its installed in GAC, referenced codebase path is never used.
When using as activeX you address the component with GUID. Since regasm adds the assembly name and version the GUID is representing, web browser directly uses component from GAC.
When using from a desktop application, reference the assebmly directly and set copylocal property to false. Similarly, in the client machine windows will find the assembly located at GAC itself.
Here is a useful link about the subject.
http://www.simple-talk.com/dotnet/visual-studio/build-and-deploy-a-.net-com-assembly/
Hope it saves other peoples time :)
There are some bug fixes that have been done in a COM component. Each time I compile, a new interop file is generated along with the dll of the actual component (component.dll) where the fix has gone in. Is it necessary to release the interop file (interop.component.dll) along with the actual dll when releasing the fix to the customer.
Also, I would like to know what exactly does interop files have?
If the COM components interface remain unchanged then you will not need to replace the interop library.
Interop files are containing automatically generated wrapper classes for you COM interfaces, so you can call your COM components without worrying about the correct order and type of your method parameters.