How to use old ActiveX in C# project? - c#

I have some old ActiveX libraries, that are used in Internet Explorer. There, in JavaScript an object would be created with new ActiveXObject("MyLib.MyClass") and used right away. The class is registered. But in my C# project I have some problems.
My first issue is that I had to add a direct reference to the library, with Browse button. In .csproj file I see <HintPath>..\..\..\..\..\Program Files (x86)\MyLib\MyLib.dll</HintPath>. This doesn't feel good. What if another developer keeps the project on drive D:?
I tried to use RegAsm.exe with /tlb switch, and then the libraries are visible in Add Reference > COM > Type Libraries list. But if I try to add it from there I get this error: The ActiveX type library 'C:\Program Files (x86)\MyLib\MyLib.tlb' was exported from a .NET assembly and cannot be added as a reference.
Aha, this ActiveX is a .NET assembly. No trace of it in Add Reference > Assemblies > Extensions though, so I try to add it to GAC, with gacutil.exe. I get this error when I try: Failure adding assembly to the cache: Attempt to install an assembly without a strong name. We didn't care much for strong names, because it just worked in JavaScript. It is primarily an ActiveX component, not a .NET assembly. It was built in C# because it was easier to do so.
My question: How to "properly" add ActiveX control to the project, what am I missing?
My second issue is that to load the component it has to be in the same folder as the host process. This also raises an alarm to me. It's not how ActiveX should be used. Naively I tried to fix this by adding the path of the library to PATH environment variable, but that didn't work. I assume if I solve the first issue that this second one will also be solved.

Thanks to Fixation's comment this is how I do it. Seems to be working for now:
Type type = Type.GetTypeFromProgID("MyLib.MyClass");
dynamic comObject = Activator.CreateInstance(type);
return comObject.MyMethod(myParam);

Related

Create placeholder .NET assembly [duplicate]

Since version 3.0, .NET installs a bunch of different 'reference assemblies' under C:\Program Files\Reference Assemblies\Microsoft...., to support different profiles (say .NET 3.5 client profile, Silverlight profile). Each of these is a proper .NET assembly that contains only metadata - no IL code - and each assembly is marked with the ReferenceAssemblyAttribute. The metadata is restricted to those types and member available under the applicable profile - that's how intellisense shows a restricted set of types and members. The reference assemblies are not used at runtime.
I learnt a bit about it from this blog post.
I'd like to create and use such a reference assembly for my library.
How do I create a metadata-only assembly - is there some compiler flag or ildasm post-processor?
Are there attributes that control which types are exported to different 'profiles'?
How does the reference assembly resolution at runtime - if I had the reference assembly present in my application directory instead of the 'real' assembly, and not in the GAC at all, would probing continue and my AssemblyResolve event fire so that I can supply the actual assembly at runtime?
Any ideas or pointers to where I could learn more about this would be greatly appreciated.
Update: Looking around a bit, I see the .NET 3.0 'reference assemblies' do seem to have some code, and the Reference Assembly attribute was only added in .NET 4.0. So the behaviour might have changed a bit with the new runtime.
Why? For my Excel-DNA ( http://exceldna.codeplex.com ) add-in library, I create single-file .xll add-in by packing the referenced assemblies into the .xll file as resources. The packed assemblies include the user's add-in code, as well as the Excel-DNA managed library (which might be referenced by the user's assembly).
It sounds rather complicated, but works wonderfully well most of the time - the add-in is a single small file, so no installation of distribution issues. I run into (not unexpected) problems because of different versions - if there is an old version of the Excel-DNA managed library as a file, the runtime will load that instead of the packed one (I never get a chance to interfere with the loading).
I hope to make a reference assembly for my Excel-DNA managed part that users can point to when compiling their add-ins. But if they mistakenly have a version of this assembly at runtime, the runtime should fail to load it, and give me a chance to load the real assembly from resources.
To create a reference assembly, you would add this line to your AssemblyInfo.cs file:
[assembly: ReferenceAssembly]
To load others, you can reference them as usual from your VisualStudio project references, or dynamically at runtime using:
Assembly.ReflectionOnlyLoad()
or
Assembly.ReflectionOnlyLoadFrom()
If you have added a reference to a metadata/reference assembly using VisualStudio, then intellisense and building your project will work just fine, however if you try to execute your application against one, you will get an error:
System.BadImageFormatException: Cannot load a reference assembly for execution.
So the expectation is that at runtime you would substitute in a real assembly that has the same metadata signature.
If you have loaded an assembly dynamically with Assembly.ReflectionOnlyLoad() then you can only do all the reflection operations against it (read the types, methods, properties, attributes, etc, but can not dynamically invoke any of them).
I am curious as to what your use case is for creating a metadata-only assembly. I've never had to do that before, and would love to know if you have found some interesting use for them...
If you are still interested in this possibility, I've made a fork of the il-repack project based on Mono.Cecil which accepts a "/meta" command line argument to generate a metadata only assembly for the public and protected types.
https://github.com/KarimLUCCIN/il-repack/tree/xna
(I tried it on the full XNA Framework and its working afaik ...)
Yes, this is new for .NET 4.0. I'm fairly sure this was done to avoid the nasty versioning problems in the .NET 2.0 service packs. Best example is the WaitHandle.WaitOne(int) overload, added and documented in SP2. A popular overload because it avoids having to guess at the proper value for *exitContext" in the WaitOne(int, bool) overload. Problem is, the program bombs when it is run on a version of 2.0 that's older than SP2. Not a happy diagnostic either. Isolating the reference assemblies ensures that this can't happen again.
I think those reference assemblies were created by starting from a copy of the compiled assemblies (like it was done in previous versions) and running them through a tool that strips the IL from the assembly. That tool is however not available to us, nothing in the bin/netfx 4.0 tools Windows 7.1 SDK subdirectory that could do this. Not exactly a tool that gets used often so it is probably not production quality :)
You might have luck with the Cecil Library (from Mono); I think the implementation allows ILMerge functionality, it might just as well write metadata only assemblies.
I have scanned the code base (documentation is sparse), but haven't found any obvious clues yet...
YYMV

COM-Interop assembly not finding a native (.Net) dependancy when called from Vb

I have a C# COM-Interop assembly which I am calling from a Visual Basic 6 application. This assembly makes HTTP requests to send and retrieve JSON.
The assembly works fine when being testing with a C# test client.
However, when using it from with the VB6 app, the following error is returned:
"Could not load file or assembly 'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified."
The Newtonsoft.Json.dll is located within the same folder as the COM-Interop DLL (TLB).
Does the Newtonsoft.Json.dll need to be explicitly loaded? Or maybe placed in the GAC?
Hans provided a great explanation for why this happens. Let me offer a workaround for making this work without having to register the Json DLL in the GAC or copying it to the VB6 EXE directory.
In your COM-visible C# library, we can tell the .NET runtime environment to search for the Json DLL in the directory of the C# library instead of the "usual" paths. We do that by attaching our own handler to the AssemblyResolve event:
AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
{
// We only want this workaround for one particular DLL
if (e.Name != "Newtonsoft.Json")
return null;
var myLibraryFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var path = Path.Combine(myLibraryFolder, "Newtonsoft.Json.dll");
return Assembly.LoadFrom(path);
};
Notes about this workaround:
This code only works if it is executed in your C# library before doing anything that might cause the jitter to load the JSON library. For example, neither your library nor any other .NET library in your VB6 process must call any method referencing types from the JSON library before this code is executed.
You modify the the behaviour of the whole process, not just your library. If your VB6 process uses another library using JSON, your "redirect" affects the other library as well.
This is a standard DLL Hell problem, it is caused by using the /codepage option for Regasm.exe. Or, more commonly, the Project > Properties > Build tab > "Register for COM interop" checkbox. Both do the same thing, they write the path to the DLL into the registry. It is a very good option to use when you are busy developing and testing the project, it avoids having to re-register the DLL into the GAC every single time you make a change.
But what it does not do is help the CLR find any dependencies. The normal probing rules remain in effect, it looks for an appname.exe.config file in the directory where the EXE is stored. And first looks in the GAC, next in the EXE path for dependencies. Configuration remains under control of the usual victim of DLL Hell, whomever has to maintain the EXE. Frequently the end-user. So, explicitly, it does not look in the directory where your [ComVisible] DLL is stored.
It is the mild kind of DLL Hell, just a plain file-not-found mishap. Much milder than the nasty kind, finding a file with the right name but the wrong version. In general a strong problem with Newtonsoft.Json.dll, there are about 35 versions in the wild. Having so many versions and it being such a popular library also begets the other kind of nastiness, the program using another COM server that also uses the DLL. But almost inevitably a different version. Tends to happen long after you declared your project finished. One of them is going to lose, 50-50 odds that it is you. 100% odds for the end-user.
Yes, the GAC solves this problem. Each library gets the version they ask for. Ideally Newtonsoft would solve this problem for you with an installer that deploys the DLL into the GAC. But it is not the kind of commitment that open source library writers ever want to provide. They want (and need) to make it your problem. Microsoft does this, but they also have Windows Update to ensure that critical bug and security fixes get deployed. And have a large number of people working on making sure that any new revisions are always backwards compatible with the original release so the version number doesn't have to change.
Do note that you can take advantage of Microsoft's commitment. You can also use the DataContractJsonSerializer and JavascriptSerializer classes to get this job done. Part of the framework, they rarely get it wrong.
Meanwhile, do keep mind that is just a file-not-found problem. You don't have to use the GAC on your dev machine, and it is better if you don't, it is just as easy to copy the file into the right place to keep the CLR happy. Which is the same directory as your VB6 test program. And, extra quirk with VB6, into C:\Program Files (x86)\Visual Studio\VB6 if you want to use the VB6 debugger. Do use the GAC when you deploy.

Visual Studio 2012 C# - Importing a DLL

I am actually trying to use a DLL on a C# project. The problem is, whenever I try to import it in my project by adding it as a reference, I get this error message :
A reference to "C:\FilePath\LnkEMP.dll" could not be added.
Check that the file is an assembly or a valid COM component
The library is "LnkEMP.dll", used for a program called Expert/M Plus. I need this library to interract with this program.
I think that this DLL is made in C++, which could be one of the reason that my C# project can't load it.
I tried to make a C++ Library and importing it, but this time I had another error message :
A reference to "C:\FilePath\LnkEMP.dll" could not be added,
because it is not an assembly .NET, or not a registered ActiveX control
Do you have any idea of what should I do to get it work ?
You can fall back on dynamic loading of dll using Win32 api calls. There should be lots of examples of dynamical loading/calling of external dll. Hopefully your dll comes with doc on how to use it.

How can C# use a legacy DLL simply without registration(regsvr32)

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

How to create a generic use library

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 :)

Categories