It is well known that you can have functions in a dll, reference the DLL and then call the functions from your main executable.
I like to know if the reverse way is also possible?
So I like to call a function in the main executable from the dll, without having the actual function that should be called inside the dll.
Reason: I am working on a pluginsystem.
Yes, executables can be added as reference in your project and you can use them same way you call functions from referenced dlls
You're sort of comparing apples and oranges: referencing a dll by the build system is completely different from a plugin system where everything happens at runtime. Typically a plugin system where you would want to call some functions from the plugin host (your exe) would be like this (simplified):
//in a common project
//functions from the host that will be callable by the plugin
public interface PluginHost
{
void Foo();
}
//the plugin
public interface Plugin
{
void DoSomething( PluginHost host );
}
//in the exe
class ThePluginHost : PluginHost
{
//implement Foo
}
//in the plugin
class ThePlugin : Plugin
{
//implement DoSomething,
//has access to exe methods through PluginHost
}
//now al that's left is loading the plugin dll dynamically,
//and creating a Plugin object from it.
//Can be done using Prism/MEF etc, that's too broad of a scope for this answer
PluginHost host = new ThePluginHost();
Plugin plugin = CreatePluginInstance( "/path/to/dll" );
plugin.DoSomething( host );
Related
I have a WCF service written in C# what works fine. It's the usual self-hosted type in a Win32 console application. But now, for reasons of special updating (it's not a simple desktop computer where you simply update a program using the usual installers but a special embedded system) I'd like to move the actual type implementing the service to a DLL which is loaded by a very simple loader executable using reflection:
string DllFilename = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "myservice.dll");
var ServicesDll = Assembly.LoadFrom(DllFilename);
var ServicesType = ServicesDll.GetType("MyNamespace.MyServices");
var Instance = (ServiceBase)Activator.CreateInstance(ServicesType);
ServiceBase.Run(new ServiceBase[] { Instance });
I can deploy the service all three ways, either installing it normally via MSI, or using sc.exe, or even with a self-managing code like this:
ServicesType.GetMethod("InstallService").Invoke(null, null);
ServicesType.GetMethod("StartService").Invoke(null, null);
where
public class MyServices : ServiceBase {
//...
public static void InstallService() {
if (!IsInstalled())
using (AssemblyInstaller installer = GetInstaller()) {
var state = new Hashtable();
try {
installer.Install(state);
installer.Commit(state);
}
catch {
installer.Rollback(state);
throw;
}
}
}
public static void StartService() {
if (IsInstalled())
using (var controller = new ServiceController("MyService")) {
if (controller.Status != ServiceControllerStatus.Running) {
controller.Start(); // fails here
controller.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(10));
}
}
}
//...
}
As I mentioned in the beginning, everything works just fine in an EXE. But as soon as the MyService type is in the DLL, InstallService is still OK but StartService fails with System.ComponentModel.Win32Exception: %1 is not a valid Win32 application. It seems that Windows expects the service executable (which it stores itself not as the EXE I actually installed but as the DLL the service was implemented in) to be an executable to be called at its leisure.
How can I circumvent this limitation, maybe to signal somehow that it's my loader EXE that I want to be called, not the dependent DLL?
Split your MyServices into two classes - one a thin wrapper inherited from ServiceBase (to live in .EXE) and another doing real work including self-hosting to live in updatable .DLL. The first one can take an instance of the second as a .ctor argument and delegate start/stop calls to it.
You may also want to look at Topshelf.
I finally went with Rene's suggestion, the bulk of the service stays in an EXE and loaded as an assembly into the loader. Fortunately, loading an assembly can be done both from EXE and DLL.
I created a wrapper for an existing project that currently only officially supports Lua for server-side coding, the server is coded in C# so it wasn't a real issue to use reflection to access the methods provided in Lua.
I'm loading "scripts" into a new AppDomain, which works just fine, however, as soon as I'm passing a delegate (delegates are used as event handlers by the server code, passed through one of the methods), the host domain attempts to load the script's assembly, which defeats the original purpose of separating the scripts from the host domain so they could be unloaded.
if I do provide the host domain with the assembly, everything works fine, until I edit the code and add/remove/modify the delegates, which then breaks every reference to the calling methods since it relies on an older copy of the assembly, as I'm loading it from a byte array so the assembly file could be modified at run time.
How can I pass delegates without having to load the assembly passing them into the host AppDomain so the scripts could remain truly isolated from the hosting AppDomain and be unloaded/loaded at will?
EDIT: Using the classes SeparateAppDomain and MefLoader from the Plugin framework project on https://www.codeproject.com/Articles/831823/Plugin-framework I load scripts like so:
Load() on the hosting domain
MefLoader mefLoader = SeparateAppDomain.CreateInstance<MefLoader>(path, path);
List<IServerScript> scripts = mefLoader.Load<IServerScript>();
foreach (IServerScript script in scripts)
{
ServerScript ss = ((ServerScript)script);
ss.CreateProxy(AppDomain.CurrentDomain);
}
In the ServerScript class (which is loaded on the new AppDomain by the MefLoader class)
private Wrapper w = null;
internal void CreateProxy(AppDomain HostDomain)
{
Type wrappertype = typeof(Wrapper);
w = (Wrapper)HostDomain.CreateInstanceAndUnwrap(wrappertype.Assembly.FullName, wrappertype.FullName, false, BindingFlags.CreateInstance, null, new object[] { }, CultureInfo.InvariantCulture, null);
}
w is the way back to the hosting domain, which handles everything regarding reflection to the server assembly.
The issue is reproduced like so:
In ServerScript
public void Test(Delegate d)
{
if (w != null) w.Test(d);
}
In any class inheriting ServerScript that would be loaded by MefLoader
Test(new Action(() => { });
In Wrapper
public void Test(Delegate d)
{
}
The hosting domain does not attempt to load the script assembly until the call w.Test(d) is made in ServerScript.
EDIT 2: After further tests, the cause isn't the separate appdomains, but the separate assemblies, unless I pass a delegate that's defined in the wrapper assembly I end up with the issue described above, this is more than likely why the wrapper attempts to load the script assembly, is there any way I could pass delegates (would often be an Action<> with a varying amount of parameters depending on the use case) from the script assembly to the wrapper assembly without loading the script assembly into the wrapper's domain?
I just came across an issue with assembly references I haven't seen before. In my usual production code, my loader application loads the main application assembly (and other references) by passing the raw bytes to Assembly.Load and then calling the entry point.
Today I needed to have the main application dynamically load another reference (henceforth called the 'dll'), which contains a class that inherits from a base class in the main program. This works fine when running the main application directly. When running it through the loader, the main application loads the dll just fine, but it doesn't seem to know that the currently loaded main application is the same as the one the dll references. So casting to the base class obviously won't work. And I'm stuck.
I'm assuming the main application assembly is losing it's identity somehow.
Here's some code that illustrates the issue:
// main application
namespace Program1
{
public class BaseClass { }
class Program
{
static void Main( string[] args )
{
string s = "Library1.Class1, Library1";
var t = Type.GetType( s, true );
Debug.Assert( t.IsSubclassOf( typeof( BaseClass ) ) );
}
}
}
// dll
namespace Library1
{
public class Class1 : Program1.BaseClass { }
}
// loader
class Program
{
static void Main( string[] args )
{
var bytes = File.ReadAllBytes( "Program1.exe" );
var asm = Assembly.Load( bytes );
var e = asm.EntryPoint;
e.Invoke( null, new object[] { null } );
}
}
When running Program1 directly it works, when running it through the loader the assert fails.
So, could anyone explain what's going on here - and if there is a possible way around it?
If (let's call dllA and dllB your libraries), dllA has a reference to dllB and you load dllA and it doesn't crashes it means .net has autoresolved and loaded dllB.
Then when you load again dllB it's another assembly, and then the types don't match, I ran myself a lot of times with that situation with a dll-loading system, at the end is better to add the referenced libraries (in this case dllB) in the .exe folder and let the system load it automatically when you load dllA.
Another option if you need the reference to the library is to attach to the AssemblyResolve event before loading DllA, then when you load it the event will fire requiring you to load DllB and thus you can store the reference to the library.
I am working on an extension for a project that will allow hosting the CLR inside the core application. With this I plan to allow this extension to manage managed extensions that it loads/unloads inside itself. That being said, I need to use separate AppDomains to ensure unloading is possible.
Currently, I am able to get the domains setup and get the plugin file loaded but at that point I'm stuck. I'm not sure how to call functions inside the domains loaded assembly at will and so on.
Here is my loading setup so far, minus error checking and such:
ICorRuntimeHost* lpRuntimeHost = NULL;
CorBindToRuntimeEx( L"v4.0.30319", L"wks", 0, CLSID_CorRuntimeHost, IID_PPV_ARGS( &lpRuntimeHost ) );
lpRuntimeHost->Start();
// File data read from disk.
// Dll file just CreateFile/ReadFile and insert into pluginFileData.
CComSafeArray<BYTE> pluginFileData;
IUnknown* lpUnknown = NULL;
lpRuntimeHost->CreateDomain( wstrPlugin.c_str(), NULL, &lpUnknown );
CComPtr<_AppDomain> appDomain = NULL;
lpUnknown->QueryInterface( &appDomain.p );
CComPtr<_Assembly> appAssembly = NULL;
hResult = appDomain->Load_3( pluginFileData, &appAssembly );
I have a class library that all plugins must reference and use in order to be considered a plugin. Which so far is nothing more than a base class to inherit:
namespace FrameworkAPI
{
public class IFrameworkPlugin
{
public override bool Initialize(IntPtr interfaceObj)
{
return false;
}
}
}
And then an extension would reference that class library and use that as its base:
namespace ClassLibrary1
{
public class Main : IFrameworkPlugin
{
public override bool Initialize(IntPtr interfaceObj)
{
// Return true to stay loaded.
return true;
}
}
}
What I am stuck at is how to do a few things:
How can I obtain the main class but as the base to invoke methods in the base that allow the main class to still handle?
How can I ensure that the main class inherits the base so I can ensure its a valid plugin file?
How I can freely invoke methods from the C++ side to fire events in the C# plugin.
For the firing events, the C++ plugin will call more things in the C# plugins once they are loaded, such as rendering events, command handling, etc.
Most of the examples I find online are specific to requiring the entire C# side to be static which I don't want. Also most do not use separate AppDomains and rather all execute in the default. I don't want this since it limits being able to unload a specific plugin.
If any other info is missing and needed feel free to let me know.
I resolved this issue by using a COM exposed interface for the C# side of things.
I have placed the FrameworkAPI inside a separate DLL and exposed it's main interface to COM then reference it in the plugins that will use it.
With it compiled with COM enabled, I can import the .tlb generated file to use the interface in C++ easily.
I have a VB6 project(windows application) and I have to redevelop a module in the existing VB6 project in C#.net.
The module that I develop in C#.net should be a dll and should contain some windows forms. I was able to successfully call a c# console applicaiton dll from my vb6 project but I am facing issues when i try to call a C# class library with winforms from my VB6 project.
This is what I have done for my Proof Of Concept - This is a class file in my C#.net class library project.
namespace TestDll
{
public interface IClass1
{
void DisplayMessage();
}
public class Class1:IClass1
{
void IClass1.DisplayMessage()
{
MessageBox.Show ("Displyaing message");
}
}
}
I have a form in the same nemspace and I plan to instantiate Class1 and use its object on the page_load event of the C# winform.
In my VB6 project I want to display the form I have in my C#.net dll. I am calling it by this code -
Private Declare Sub DislayMessage Lib "TestDll.dll" ()
Private Sub Command1_Click() //On button click event of the VB6 windows form
DislayMessage
End Sub
I get an error - "Can't find a DLL entry point in DisplayMessage in TestDll.dll"
I am not sure how to solve this error. I am even skeptical if this is the way a C#.net dll which contains some winforms should be called from a VB6.0 windows applicaiton.
Should I instantiate Class1 in my VB6 code? How do I resolve this error?
Is my approach correct? Are there better ways to do this?
TIA.
You have to make your class COM-Visible. Here's how I would change your code:
namespace TestDll
{
[Guid("FB8AB9B9-6986-4130-BD74-4439776D1A3D")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface IClass1
{
[DispId(50)]
void DisplayMessage();
}
[Guid("74201338-6927-421d-A095-3BE4FD1EF0B4")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[ProgId("TestDll.Class1")]
public class Class1:IClass1
{
void IClass1.DisplayMessage()
{
MessageBox.Show ("Displyaing message");
}
}
}
Note the [DispId(50)]. You want to specify the dispatch ID for your COM-visible methods, properties, and events. If you don't, the compiler will do it for you and you may end up breaking compatibility every time you compile. The number doesn't matter so much as it doesn't change between compiles.
You might want to check out Building COM Objects in C#. It's a pretty good getting started tutorial.
Some highlights:
Exposing the VC# objects to the COM
world requires the following …
* The class must be public
* Properties, methods, and events must be public.
* Properties and methods must be declared on the class interface.
* Events must be declared in the event interface.
Every Interface needs a GUID property
set before the interface name. To
generate the unique Guid , use the
guidgen.exe utility and select the
Registry Format.
The only way to do it is to expose your C# class as a COM object (also called a CCW - COM Callable Wrapper), and create an instance of that COM object in your VB6 code.
This should help you get started:
http://www.bing.com/search?q=C%23+CCW&go=&form=QBRE&qs=n
There some excellent help on msdn here:
https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/com-interop/