C# Dynamic compile and replace/reload of assembly from within same assembly - c#

I have several issues with several SDK's comming from OEM manufacturers for specific devices. SDK is usually based on C or C++ dll, so I have a lot of Marshaling going around (a lot===YOU CAN'T EVEN IMAGINE). Problem start with next version of SDK when they extend some functions or some structures, they effectively break compatibility. In past I have made copy of our library supporting their device and start making changes to support new SDK. But each time our library was only for specific SDK, and upgrades of our systems were tough (Installation script if one heavy thing also ~ 3 GB install).
I have 78 projects in solution, commonly 4-5 libraries for each OEM Manufacturer, this is without any service tools. And Yesterday I said NO MORE. Started research on subject how to recompile C# code in runtime and reload/replace same assembly without quiting App.
And the result is the following:
- Class file that defines external C/C++ dll API was referenced from external Project referencing only System.dll. And me being insane I've already had each SDK version changes wrapped around #if #elif #endif so I could recompile last version of our library to support previous version of SDK. But that was maybe only once done, I've used #defines along with CSharpCodeProvider to recompile this assembly in runtime. Idea was like this:
Application loading ...
Open main SDK file get file version (extract version and identify it).
Load original External Assembly in new AppDomain (so I could destroy domain later).
Extract current version from external assembly.
Destroy new AppDomain to release hook from external assembly.
If versions mismatch, recompile external assembly (source code for external assembly is embedded within parent assembly), and replace original DLL with just compiled one.
Continue loading application...
So far this test approach works on one live demo system, and I was amazed. Switching from one to another SDK was flawless without any hick-ups.
And also code recompiles it self only when SDK version changes. So with safe guard I could say this is my first Metamorphic code I've wrote, that recompiles/changes it self from runtime.
Unfortunately this approach requires me to add one more Project for each OEM Manufacturers SDK. Which effectively kills my first though why I said NO MORE. True I now have only two libraries to maintain per one OEM manufacturer, and there will be no more projects added after this. But...
I wonder is there better approach which could allow me to replace DLL of currently loaded assembly in runtime from true within same assembly? Or change executing code on "fly" each time, this mainly includes Marshaled function, classes, structures, constants, ...?
Please notice code should be maintained from within same project without any externals. Also please notice this project exposes only hard-coded interface to "outside" world (Interface is referenced Interface only project - is more complex than I wrote). But this "outside" world is blind to any OEM specific stuff, which was the point using interface to have exactly same behavior across any OEM Device.
Any ideas? thoughts? suggestions?

Related

Using external DLL's in C# COM-DLL project for MS-Access usage

The user's main application is ms-access (with ms-sql server). Ton of code in vba.
One of the main features i have to manifest is controlling my client's voip telephony. My plan is to make a COM DLL that the ms-access can use.
As of now, i've managed to write a C# project (vs2019 .net framework) that handles the voip pbx well, using json, and do all missions necessary, such as invoking calls, sending sms, get calls list, recodings list, get recordings wav files and convert them to mp3. I use Newtopsoft.Json and NAudio.Lame.
BUT it only works as a standalone .net project or exe.
As a COM DLL (class library) - ms-access can handle and use it UNTIL it gets to the part where the code uses ANY external dll's functionality - and it crashes (with newtonsoft) or just not working (with naudio), although the dll's are in the working folder.
I know i have to found a way to make these external dll's to work under the office (32bit) enviroment.
I was trying and guessing many code samples for a couple of days. I tried to follow the Assembly.Load/From/File examples to dynamically load and include the dll's, but i failed to assimilate it in my code or even to understand it enough.
I hope i'll get a solution here.
Thanks :)
Hum, I am able to create a COM object for use with Access. External library code (in my case Newton soft) for serializing data works just fine when that class code is used as a COM object from Access.
You don't mention if the other libraries are managed code, or external win32 .dll? I have however used un-managed .dll’s with a .net COM object, and then have consumed that from MS-access.
The only issue would be in project explorer is to ensure that you have a copy local setting = true for the external referenced .net .dll libraries. (And that is the default, so I am sure you do.
The next issue of course is of course is there any use of configuration files? (app.config) etc.? The reason for this of course that since your application is being launched with msaccess.exe, then the config and settings will be assumed to be in the office install folder, and not the local application. As a result, it not all so practical to move/copy those app.config files to the office folder where msaccess.exe resides. And the config file name will not only have to be in the same folder, but also named.
Msaccess.exe.config.
In place of
YourDotNet.exe.config
Now of course, if you build your project as a .net exec? Well then of course any config settings (application ones, not user) will be placed in this config file, and of course that file will have to be placed in the same folder as the .exe file.
So, one might add a “test” function (any function is a method of that class) to the class that you test/call from VBA to ensure that such settings are being seen and read (if you are using any settings).
Eg:
System.Reflection.Assembly.GetExecutingAssembly().Location + ".config"
So, return that to access and display this result.
The other issue is of course to FORCE your project to x86. I would not use “any” CPU. While this “may” work, since you are using access x32, then I would force compile your .net class to x86 as opposed to “any” CPU. This would also spit out some errors in case some of the external assemble(ies) are compared to a particular bit size and thus x86 settings will result in your class not consuming some of the external .net .dll’s.
Now you “note” that your project works as an .exe, and then “sort of” works as a class and “.dll” registered as a COM.
However, you don’t mention how you are “flipping” this from an .exe to a class, or do you simply have a .net “test” project that you run as an .exe, and it of course has a reference to that class .dll. And you NOT using “COM” for this testing.
If your test.exe program can use that external assembly?
Then I would create a test2 project, but this time around do NOT reference the assembly (as .net), but try using .net CreateObject(). (I think c# has this choice – vb.net certainly does).
So you could try + use a CreateObject() in this .net test sample, and thus test from .net as COM.
So this test .net program (one that has NOT referenced the assembly) would a intermediate test before you jump all the way into ms-access + VBA test code.
However, I would first test/check if an app.config file is being used here, since as noted when you flip to a COM object, and then create a instance of that object in Access/VBA?
Then the config file will NOT be used. You can in .net code force/change the app.config file in that running assembly, and I can’t seem to find my code example that does this. But I am not going to dig too hard until such time we confirm that you are using app.exe.config file here.
Edit
Out of cuirous, I googled naudio. It is managed code. So, that library should work just fine. If that project is compiled as "ANY CPU", you COULD try re-compile as x86, and also re-compile your test program as x86. Then when you create a COM object, then again compile that as x86.
As noted, I do quite a bit of COM stuff for ms-access, and I have without issue used Newtonsoft (for jason serializing/de-serializing) as a COM object that is consumed by ms-access.
And given that naudio is managed code then I don't see why it would be an issue. Any other 3rd party .dll's you using here? As a quick test, I would set all projects to x86. You seem to "mention" that "some" of the COM object works, but just not all.

Reference multiple versions of the same DLL

I have a project which needs to indirectly use three different versions of a third-party library. These versions are incompatible with each other, so I can't use a binding redirect - it has to be the exact .dll file. (The libraries are Spire.Doc, Spire.XLS & Spire.PDF; the Spire.PDF DLL is referenced by all three)
I have separated the three components into individual wrapper projects, and created classes which wrap direct references to anything in the libraries. However, this doesn't solve my issue: the 'consuming' project still has to copy all of the libraries to the bin folder in order to run. The build process doesn't know which version to copy, and so just copies the latest one. This gives me runtime exceptions due to the wrong DLL being present.
What I've considered/tried:
Adding a binding redirect to a specific version (runtime exception because the exact version of the library is not found)
Using a post-build step to merge the wrapper projects (again a runtime exception complaining about the absence of the library DLL)
Creating separate console applications for each part of the application, then invoking them in a separate - this is a complicated last resort that I'd really rather not do!
I have read that extern alias might be able to help - but as far as I can tell, you can only distinguish between assemblies with different names. The Spire.PDF library has the same name in each project (and the same signed public token).
How can I use these three separate versions of the library independently in the same solution?
Edit:
This issue is slightly different to the suggested duplicate because I don't have the ability to change any code in the dependent libraries. Spire.Doc relies on a different version of Spire.PDF to Spire.XLS
In your consuming project (Project A), create a common interface (ISpiroPdfAlex) that encompasses all the functionality that the 3 versions of your external assembly provides (and you use). You cannot reference anything in Project A from these wrappers in any way, otherwise you'd create a dependency, which is what you're trying to avoid.
Have all 3 wrapper projects import Project A and implement ISpiroPdfAlex. This will give you the ability to call each of the 3 different versions through the same API.
After this, create a subfolder under Project A for each of the versions (so 3 subfolders total) - since Project A has no reference to any of the external assemblies, it cannot load them by itself - you'll have to manually load them when you need the right version. Since your external DLLs may have dependencies with the same name, they cannot all be in the same folder (as you wrote), this is why you need the subfolders.
At run-time when you need one of these versions, you can call Assembly.LoadFile to load a specific version of your assembly from the specified folder and then you can either use Activator.CreateInstance or dependency injection to create an instance of a class that implements your interface. Once you have the instance, you're free to call any of the functions and you'll get version-dependent behavior.
Edit:
OP mentioned in a comment that it's not his code that has the dependency on different versions of the PDF library but the other 3rd-party Spire libraries that his code depends on.
In this case, the 3rd-party code cannot be modified to support dynamic loading of assemblies and they already have a binary dependency. It's not possible to load different versions of the "same" assembly into the same process, especially that you mentioned that these versions are not even backward-compatible with each other.
The only solution I can think of in this situation is to break out all dependent functionality into separate console applications (one for each different version) and call those separate .exe-s through the command-line.
To pass information, you can either pass data directly on the command-line or through stdin. Alternatively, you can just pass the name of a temporary file that has all data necessary to do some processing. To get return data back from the console process, you can either read its stdout or use the same / different file.
This way your main process never loads any of these assemblies and has no dependency on them - each console application has a dependency on just one version so there's no collision.

Test compatibility between DLL in .NET

I'm working with Visual Studio 2010 and WinForms, .Net 4.0 (C#). I'm building an application with a lot of DLL (150). When I provide the application to my client, it's :
The Executable (.exe)
Dll files (.dll)
Each Dll is related to a module of the application, for example :
Ado.dll (provide access to database)
AccesManagement.dll (this module allows to manage users in the application)
Import.dll (this module allows the user to import data to the application)
etc.
When my client find a bug in the application, I correct it and I provide him impacted DLLs (in order to avoid him to test all the application). It can be for example the Import Dll.
The thing is, after some deliveries, we can have compatibility problems between Dll (a method that doesn't exist anymore in a new DLL for example). To avoid this problem, I would like to find a tool capable of checking compatibility between differents DLL.
I would like something like :
I specify the directory of the program to analyse (executable + Dll)
I launch the analyse
The program tells me for example : Error between Import.dll and Ado.dll, there is a class xxx in Import.dll expecting a method named xxx in the class xxx of Ado.dll
I've found some tools able to compare two versions of a Dll and provide added and removed members (Libcheck, ApiChange), but it's too complicated for me to do that because there are to many changes.
I think you may have a configuration management problem here -- at least as much as you've got a "compatibility" problem.
I'd recommend you find a way to track what versions of which assemblies each of your customers is using so that (1) you know what they're using when you decide what to ship, and (2) when they report bugs, you can replicate their setup (and thus, replicate their bug). If that sounds like a lot of work, it is. This is why a lot of software development shops take steps to ensure that there's a limit to the variation in setups among customers. It's nearly certain that you'll end up with some variation from customer-to-customer, but anything you can do to manage that problem will be beneficial.
Beyond the process implications, if you really need to create a "pluggable" environment, you probably need to create some interfaces for your objects to control the points where they connect, and you should probably look at Microsoft's Managed Extensibility Framework (MEF). MEF can help you manage the way objects "demand" behaviors from other objects.
I finally found a solution to my problem.
Since I'm :
Using SourceSafe and adding labels with the version of the application I'm building
Tagging each of my DLL with the version of the application
I built a program which is capable of :
Opening each Dll of a folder to read the version of the application in it
Getting from SourceSafe each project for the version specified in the DLL (With the functionnality "Get Label")
Then I just have to build the projet. If there is any compilation error, there is a compatibility problem.
This solution can avoid big compatibility problems, but you can still have compatibility problems which can't be seen with a compilation...

how to add assemblies in .net application

I have doubt to refer a same name dll in a single project
1.i was create one class library and copy the dll to the start up application path and refer into the project.The DLL version is 1.0
2.That same dll i was register into registry.the DLL Version is 1.1
Is there any possibility to refer a private and global assemble in a single application.but
Assume you have different versions of the exact same assembly? I am confused here as you mention one is in the registry ... do you mean in the GAC (global assembly cache) or have you added some type of interop to the mix?
Assuming you are talking two versions of the same assembly, you do have the option of extern alias, which Scott has mentioned. Another workaround is to set up a WCF "UI" on top of one or both of the versions and then calling that "service endpoint" to utilize the code. This pushes the code into its own process space, which may or may not work. Juval Lowey has pushed this idea, so you should be able to easily find a few examples on the web (or pick up his latest book on WCF).

In .NET, is there a need to register the DLL?

Is it necessary to register a compiled DLL (written in C# .NET) on a target machine.
The target machine will have .NET installed, is it enough to simply drop the DLL onto the target machine?
I think you're confusing things a little. Registering a dll has never been needed in order to use it.
Using a dll requires only to load it (given a known location or if the library is in the system path) and get the address of the function you wanted to use.
Registering the dll was used when distributing COM or ActiveX objects which need to add certain entries to the windows registry. In order to use a COM service (for example) you need to reference a GUID — that is, a unique identifier — which allows you to get a handle to the dll that implements the service (or provide access to it). Sometimes you can make reference to a fully-qualified name and get the same results.
In order for all that to work the dll needed to be registered. This "registration" process just creates several entries in the registry, but mainly these two: one associating a GUID with the location of the dll (so that you can reference it through the GUID without knowing where is it exactly located) and a second one associating the full name with the GUID. But again, this is just for COM or ActiveX objects.
When you develop an application in .NET, the libraries referenced on your project are automatically loaded when they're needed without you having to worry about locating or loading them. In order to to that, the framework checks two locations for the referenced libraries.
The first location is the application path.
The second location is the GAC.
The GAC (Global Assembly Cache) allows you to effectively register a dll to be used throughout the system and works as an evolution of the old registering mechanism.
So basically you just need to put the dll in the same folder of the application.
You need to "drop" it into a directory where the application needing it will find it.
If there are multiple applications, or you want to "drop" the file somewhere other than the application directory, you generally need to either adjust the PATH variable, or register the assembly in the Global Assembly Cache (GAC).
It is usually enough to drop the dll into the folder of your app on the target machine.
If the dll must be available to other applications then you may want to consider the GAC.
If you wish to access the assembly via com+. An example would be using a type defined in a .NET assembly from a non .NET application, such as a VB6 winforms app.
If you plan on accessing the assembly from another .NET application, you don't have to do anything. If your assembly has a strong name, it probably is a good idea to drop it in the GAC. Otherwise, just drop it in the directory of the application that will be referencing it.
One of the great selling points of .NET for the Windows platform when it came onto the scene is that by default, .NET assembly DLLs don't have to be registered and can be consumed privately by an application by merely putting them in the same folder as the EXE file. That was a great stride forward because it enabled developers to avoid the fray of DLL/COM hell.
Shared DLL/COM modules proved to be one of the greatest design mistakes of Windows as it lead to instability of applications that users installed. Installing a new app could well screw up an app that had been working just fine - because the new app introduced newer versions of shared DLL/COM modules. (It proved in practice to be too much of a burden for developers to properly manage fine-grained version dependencies.)
It's one thing to manage versions of modules with a build repository system like Maven. Maven works extremely well doing what it does.
It's an entirely different matter, though, to deal with that problem in an end-user runtime environment spread across a population of millions of users.
The .NET GAC is by no means a sufficient solution to this age-old Windows problem.
Privately consumed DLL assemblies continue to be infinitely preferable. It's a no-brainer way to go as diskspace is extremely cheap these days (~$100 can by a terabyte drive at Fry's these days). There is nothing to be gained with sharing assemblies with other products - and yet company reputation to loose when things go south for the poor user.
Actually there is NO need to register a dll in .NET on the target machine.
If you reference a .dll in your application, click on the referenced .dll under references in your project, look at the properties and set Isolated to TRUE.
This will now automatically include this .dll in your project and your application will use the copy of the .dll included in your project without any need to register it on the target system.
To see a working Example of this look here:
http://code.msdn.microsoft.com/SEHE
The .dll in question will need to be registered on the system where you build your application for this to work properly. However once you build your project, there will not be any need to register the .dll in question on any system you deploy your application or program.
An additional benefit of using this method, is that even if in the future, another .dll is registered with the same name on the target system in question, your project will continue to use the .dll you deployed with. This is very handy where a .dll has many versions and you wish to maintain some stability, like using the one you tested with, yet all other applications will use the registered .dll unless they use the isolated = true method as well.
The example above is one of those cases, there are many versions of Skype4COM which is a Skype API .dll and can change often.
This method allows the above example to use the API .dll that the project was tested with, each time a user installs a new version of Skype, it is possible that a modified version of this .dll is installed.
Also, there are some Skype clients that do not install this .dll, the business version of the Skype client for example, is smaller, and does not include this .dll, so in this case, the project does not fail on that .dll missing and not being registered because it is included in the project as isolated = true.
An application can use a .NET dll by simply having it present in the same folder with the application.
However if you want other third-party applications to find the DLL and use it they would also have to include it in their distribution. This may not be desirable.
An alternative is to have the DLL registered in the GAC (Global Assembly Cache).

Categories