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/
Related
I have a main application with a class "Table" which implements the interface "ITable" from the dll "Lib":
Main Application:
using Lib;
namespace One
{
public class Main
{
// Here the method "Work" from the plugin is called with first
// parameter a string and second parameter a dictionary with "Table"
// objects and then it shows here the fault message
plugin.Work("My Input", tableDictionary)
}
public class Table : ITable
{
}
}
Class library: "Lib"
namespace Two
{
public interface ITable
{
}
}
Plugin-Project:
using Lib;
namespace Three
{
public class Example
{
public string Work(string input, Dictionary<string, ITable> tableDict)
{
// Here i want to use table objects from the main application
// Do something with the table objects
}
}
}
A plugin for the main application is created in a second project "Plugin-Project" which uses the dll (because i want to use objects/class "table" of the main application in this second "plugin"-project). I follow here this plugin-approach:
Creating a simple plugin mechanism
But when i use the table-object in the plugin project, import it then to my main application and run the main application, i get the error which says that it "Cannot convert from "Table" to "ITable"" It is not an exception while runtime. It is just a fault shown in the fault list of VisualStudio.
The problem is i want to use an Table-object within the plugin just like the way i can use it in the main application. Therefore i use the interface. But then i just can use an ITable object while my main application uses Table objects?
Update: I edited the problem description a bit, hope it is better understandable now. (Deleted also the ITable object test in the plugin, it was just not right). In "Main" of the main application the plugin is imported etc. (works fine and is not shown in the code above) and then the method "Work" from the plugin is called.
My simplified main concern is: How can i use the table objects of the main application within the plugin?
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 );
So we are working on new app for Windows Store and we're utilizing DirectX on C++/CX part of app and C# on other. As we needed some sort of communication, we created interface in shared library (C#) and class that implements it on C# side of the project. We then pass instance of that class to the C++. When C++ need to communicate with c#, it calls methods from shared interface and c# executes what was implemented in the class.
The problem arises when C++ calls methods from another thread than then object was passed on to it. Error:
The application called an interface that was marshalled for a different thread.
Here's how it works:
C# instance of shared interface (some class)
C# -> C++ \/ passed in parameter of public C++ method
C++ saves to variable
C++ creates new thread
C++ calls instance->SayHello();
C# exception
Shared interface: (C#)
public interface IUserMessageService
{
void WriteMessage(string message, MessageTypes type);
}
Implementation: (C#)
public sealed class UserMessageService : IUserMessageService
{ ... }
Assign method (C++)
void AssignUserMessageService(...::IUserMessageService ^service) { m_service = service; }
I then call AssignUserMessageService with instance of UserMessageService from UI thread. Error occurs when I try to access data passed by C++ when calling WriteMessage in another thread.
Is there any other way to pass instance or call method?
The problem was that I was using CoreApplication.MainWindow.CoreWindow.Dispatcher instead of Window.Current.Dispatcher
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.
if this possible to write a com control or activex in C# and use it in MFC ?
Yes. First, you need to create COM object. Below is a very simple example.
[Guid("123565C4-C5FA-4512-A560-1D47F9FDFA20")]
public interface IDoSomething
{
[DispId(1)]
string Name { get; }
[DispId(2)]
int DoSomething();
}
[ComVisible(true)]
[Guid("12AC8095-BD27-4de8-A30B-991940666927")]
[ClassInterface(ClassInterfaceType.None)]
public sealed class DoSomething: IDoSomething
{
public DoSomething()
{
}
public string Name
{
get { return ""; }
}
public int DoSomething()
{
return 4; //random number
}
}
After that you need to regasm your assembly. The regasm tool will add the necessary registry COM entries:
regasm.exe /tlb component.dll
/tlb is necessary to generate the type library to be imported in your MFC application.
Once your assembly is registered, you can call DoSomething in your MFC application like any other COM objects.
Check this link for more information.
This is a bit outside my normal territory, as I don't interact with .NET Interop technology all that much.
It is possible to create what's called a COM Callable Wrapper around your C# control/class to make it accessible to any COM-aware program. I won't duplicate Francis B's answer because it's fairly complete as it stands.
The big question is whether a visual C# control works seamlessly within an MFC window. That's not something I can answer, but my best advice would be to prepare for a bumpy road ahead. Please see http://bytes.com/topic/net/answers/430618-c-control-mfc-window-frame for more detail.