Subscribing to COM events in managed COM assembly from C# - c#

I have a .NET 3.5 application, and need to use .NET 4 for a certain part of it. I have thus exposed the .NET 4 part as a registration free COM assembly.
In a .NET 3.5 assembly, I have the following code, which works fine:
Type comClassType = Type.GetTypeFromProgID("A.ProgID");
object comObject = Activator.CreateInstance(comClassType);
var managedObj = (IManagedInterface)comObject;
managedObj.MyMethod(); // call .NET 4 method from .NET 3.5
The declaration of IManagedInterface is as follows:
[Guid("ED915810-1B19-4357-9FD4-564CFC0AFAFF")]
[ComVisible(true)]
public interface IManagedInterface
{
void MyMethod();
...
}
I also have an events interface, declared like this:
[Guid("D3633346-EB8B-4F62-A806-3C393D40F694")]
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMyComEvents
{
void MyEvent()
}
This is, as far as I can tell, according to this article: http://msdn.microsoft.com/en-us/library/dd8bf0x3.aspx
My .NET 4 COM class is as follows:
[Guid("7BA49B2D-A359-4B70-BEB1-2A61EA63EFA4")]
[ComVisible(true)]
[ComSourceInterfaces(typeof(IMyComEvents))]
public class ManagedComWrapper : IManagedInterface
{
...
}
The question then is: how can I, in the .NET 3.5 client shown at the top, subscribe to/handle the events from IMyComEvents?

Related

Call (C#) .NET Core method from C++

I have 2 components:
- .Net Core Application running on Ubuntu OS.
- C++ shared library (.so)
Now I want C++ component to be able to call .Net Core method, either passing interface to C++ component which will use this interface to callback method implementation or passing method as a parameter to C++ component.
High-level example what I am trying to achieve:
C# component :
public interface IDevice
{
void OnDataAvailable(string data);
}
public class Device: IDevice
{
[DllImport("sampleCPPLibrary.so")]
private static extern int SetReceiver(IDevice receiver);
public void OnDataAvailable(string data)
{
Console.WriteLine(data);
}
public void Initialize()
{
SetReceiver(IDevice(this))
}
}
C++ component:
extern "C" {
void SetReceiver(IReceiver * receiver)
{
receiver->OnDataAvailable(10);
}
}
Basically, what I am trying to do is just to pass some kind of "callback" to C++ component and call this "callback" when some event occurs in C++ component.
See See this issue from comments I constructed code where C# calls C and gives it callback delegate. Thus from C then it calls C#, and passes additional int type argument. See comments here from endurox project, and attached c-callback.tar.gz for working example.

Why is this C# COM class usable from VBScript but not JScript?

Consider the automation-compatible COM library in C#, given below. It follows a common COM pattern of having a visible factory coclass FooFactory implementing ICreateFoos which creates an object of type IFoo. FooFactory is the only coclass in the type library. (The factory pattern is particularly useful with COM, as it does not allow for parameterized constructors).
In the code below, I'm finding that I cannot access the returned IFoo interface from jscript unless I make the FooImpl class ComVisible (by uncommenting commented lines; this causes it to appear as a coclass in the type library). There is no such problem accessing this from VBscript.
That is, I can run this VBScript:
set ff = CreateObject("jstest.FooFactory")
set foo = ff.CreateFoo(0)
foo.Foo
But this functionally identical JScript fails, with the error "C:\temp\jstest\jstest.js(4, 1) Microsoft JScript runtime error: 'foo' is null or not an object":
var ff = new ActiveXObject("jstest.FooFactory");
var foo = ff.CreateFoo(0)
//WScript.Stdout.WriteLine(null==foo)
foo.Foo();
If I uncomment the line, I can see that null==foo is false.
Why does this happen? Is it a bug? Note that I think this is a problem is a combination of JScript and the C#/.net-specific implementation (possibly of IDispatch), because I have other similar COM servers - implemented in C++ - that do not exhibit this problem from JScript.
The problem goes away if I uncomment the commented lines in the code below, making FooImpl visible as a coclass - but I specifically do not want to do this as I don't want to expose implementation details. A workaround seems to be to make FooImpl ComVisible, but mark its constructor internal, which prevents clients from being able to CoCreate it, but that's hardly elegant.
I'm running on WinXP SP3 with Visual Studio 2005, .net 2, and have been able to reproduce the issue on a completely fresh install of TinyXP on a VirtualBox (both with Windows Script Host 5.7), and also on Windows 7 Ultimate using .net SDKs 2.0, 3.0, 3.5 and 4.0 (WSH 5.8). All OSes were 32-bit.
The library code:
using System;
using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
namespace jstest
{
[ComVisible(true)]
public interface ICreateFoos
{
IFoo CreateFoo(int importantNumber);
}
[ComVisible(true)]
public interface IFoo
{
void Foo();
}
[ComVisible(true)]
public class FooFactory : ICreateFoos
{
public IFoo CreateFoo(int importantNumber)
{ // in *this* version, we don't use importantNumber yet
return new FooImpl();
}
}
//[ComVisible(true)]
public class FooImpl : IFoo
{
public void Foo()
{
Console.WriteLine("Foo");
}
}
}
You can compile and register (you may have to run as admin to regasm) this with
csc /target:library jstest.cs
regasm /codebase jstest.dll
When QueryInterface is called against the IFoo object returned from CreateFoo for the IDispatch GUID it returns E_NOINTERFACE unless ComVisible is set for the actual implementing class.
When jscript prepares to call the Foo method it calls QueryInterface several times, including with this specific GUID, and since an error is returns it doesn't try to use Invoke.
When vbscript prepares to call the Foo method it does not check the interface supports IDispatch. QueryInterface is called, once, with the GUID for IDispatchEx but it seems to simply assume IDispatch will be supported.

help: Call C# winforms dll from VB6 project?

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/

write a com control in c# and use it in MFC

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.

COM+ events with c# and .NET

I'm trying to get access to certain parts of my application through COM, mainly because I need a Delphi application to be able to invoke certain services on the .NET application.
I've managed to expose some methods via COM inheriting from ServicedComponent class and its working fine.
Now I need to be able to raise events to the client so I can alert him when certain things occur. I currently have something like this:
public interface IEvents
{
void OnMessage();
}
[EventClass]
[ComVisible(true)]
public class MyEventsClass : ServicedComponent, IEvents
{
public void OnMessage()
{
}
}
but when I import the TypeLibrary from Delphi I get this:
IEvents = interface(IDispatch)
['{E7605303-F968-3509-829B-BF70084053C4}']
procedure OnMessage; safecall;
end;
IEventsDisp = dispinterface
['{E7605303-F968-3509-829B-BF70084053C4}']
procedure OnMessage; dispid 1610743808;
end;
which I don't really know how to use. I was expecting an IUnknown interface that I can implement and provide to the service through a MyService.Register(IEvents events)...
I think I'm completely lost here... Any advice or reference about how to properly implement COM+ events with .NET?
I'm still unsure of what exactly is the use for EventClass by I've discovered you don't need to use it. I simply have declared this:
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMyEventIface
{
}
And with that the interface is exported as an IUnknown interface to Delphi.
Then you define a method on your ServicedComponent class which a connect signature like this:
public void Connect(IMyEventIface eventHandler)
{
mEvents.Add(eventHandler);
}
which left you to manually handle the invoke of each client event handler but is the only way I've found.

Categories

Resources