Exposing C# COM server events to Delphi client applications - c#

My question is very similar to these two:
C# component events?
C# - writing a COM server - events not firing on client
However, what worked for them is not working for me. The type library file, does not have any hints of events definitions, so Delphi doesn't see it. The class works fine for other C# applications, as you would expect.
COM Server tools:
Visual Studio 2010
.NET 4.0
Delphi applications:
Delphi 2010
Delphi 7
Here's a simplified version of the code:
/// <summary>
/// Call has arrived delegate.
/// </summary>
[ComVisible(false)]
public delegate void CallArrived(object sender, string callData);
/// <summary>
/// Interface to expose SimpleAgent events to COM
/// </summary>
[ComVisible(true)]
[GuidAttribute("1FFBFF09-3AF0-4F06-998D-7F4B6CB978DD")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IAgentEvents
{
///<summary>
/// Handles incoming calls from the predictive manager.
///</summary>
///<param name="sender">The class that initiated this event</param>
///<param name="callData">The data associated with the incoming call.</param>
[DispId(1)]
void OnCallArrived(object sender, string callData);
}
/// <summary>
/// Represents the agent side of the system. This is usually related to UI interactions.
/// </summary>
[ComVisible(true)]
[GuidAttribute("EF00685F-1C14-4D05-9EFA-538B3137D86C")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IAgentEvents))]
public class SimpleAgent
{
/// <summary>
/// Occurs when a call arrives.
/// </summary>
public event CallArrived OnCallArrived;
public SimpleAgent() {}
public string AgentName { get; set; }
public string CurrentPhoneNumber { get; set; }
public void FireOffCall()
{
if (OnCallArrived != null)
{
OnCallArrived(this, "555-123-4567");
}
}
}
The type library file has the definitions for the properties and methods, but no events are visible. I even opened the type library in Delphi's viewer to make sure. The Delphi app can see and use any property, methods, and functions just fine. It just doesn't see the events.
I would appreciate any pointers or articles to read.
Thanks!

I finally got this resolved after much trial and error. There were 2 things that I needed to change on the C# code.
1) [ClassInterface(ClassInterfaceType.None)] needed to be changed to [ClassInterface(ClassInterfaceType.AutoDual)]
2) The class that is the source of the events needs to inherit from MarshalByRefObject. This helps if there is any threading done in the source class.
I only needed one thing on the Delphi side. I needed to make sure to have the "Generate Component Wrapper" checkbox checked. This is what will actually build the event scaffolding on Delphi's side.
This is how you do it in Delphi 7:
Select from the menu Project -> Import Type Library
Make sure that "Generate Component Wrapper" is checked
Select the COM class from the list
Click on the "Add Unit" button
The new unit will have the definitions of your COM events.
Step-By-Step blog post on how to do this

Related

SignalR C# (both client and Hub)-strongly typed example

SignalR C# - Checking if someone could forward me to a SIMPLE example of C# SignalR that uses Strongly Typed calls to client methods via Hub (not passing a string) and also does NOT have javascript involved but the client side is in C# and lastly has an API controller endpoint exposed for an external call to the Hub side.
In .NET Core Web App you can inject strongly typed signalR hub context like this
public interface IClient
{
Task ReceiveMessage(string message);
}
public class DevicesHub : Hub<IClient>
{
}
public class HomeController : ControllerBase
{
private readonly IHubContext<DevicesHub, IClient> _devicesHub;
public HomeController(IHubContext<DevicesHub, IClient> devicesHub)
{
_devicesHub = devicesHub;
}
[HttpGet]
public IEnumerable<string> Get()
{
_devicesHub.Clients
.All
.ReceiveMessage("Message from devices.");
return new string[] { "value1", "value2" };
}
}
if you want a tutorial you can use this link
https://darchuk.net/2019/07/19/signalr-strongly-typed-hubs/
communication between client and server. The general process that is followed won't allow to have strongly typed calls on the server and also in client code. We will look into how to make strongly typed calls on the server through interface and client end through TypeScript.
SignalR - making strong type on server side code
Since client-side methods are very dynamic in nature, so calling those from server side behaves similarly to allow any calls.
SignalR Hubs are derived from Microsoft.AspNet.SignalR.Hub class, there is also a generic version available to follow typed items.
Ex:
The interface is the replication of possible calls that would be received on the client end and calling of client-side methods on server code.
/// <summary>
/// Client(JS) side chatting interface callbacks.
/// </summary>
public interface IChatHub
{
/// <summary>
/// Gets the online users.
/// </summary>
/// <param name="chattingUsers">The chatting users.</param>
void GetOnlineUsers(IEnumerable<ChatAvailableUserOrGroup> chattingUsers);
/// <summary>
/// Determines whether the user is typing.
/// </summary>
/// <param name="userFullName">Full name of the user.</param>
void IsTyping(string userFullName);
/// <summary>
/// Member initialization through login.
/// </summary>
/// <param name="isSuccess">if set to <c>true</c> success login.</param>
void Login(bool isSuccess);
}
The above interface can be implemented on generic hub inheritance.
/// <summary>
/// SignalR Hub for chat application.
/// </summary>
public class ChatHub
: Hub<IChatHub>
{
}
After implementation of an interface on Hub, the IntelliSense would come automatically.
Clients.Caller.IsTyping("viku");
That is all on server-side code implementation.
Strong type implementation on the client-side.
The calls of server and client-side callbacks need to be wrapped under the interface. The calls are wrapped with server and client properties. Let's have a look at the interface.
interface SignalRExtended extends SignalR {
chatHub: ChatHub;
}
interface ChatHub
extends HubConnection {
client: { // callbacks on client side through server
GetOnlineUsers(json: string);
ReceiveMessage(message: Model.ChatReceiveMessage);
IsTyping(connectionId: string, message: string);
Login(): boolean;
UserChatHistory(chattingHistory);
};
server: { // Calling of server side hubs
login(): () => void;
sendPrivateMessage(toUserId: string, message: string);
chatHistory(toUserId: number);
groupChatHistory(toGroupId: number);
sendGroupMessage(toGroupId: number, message: string);
userTyping(targettedUserOrGroup: number, requestType: string);
markAsRead(messageIds: Array<number>, targetedUserOrGroup: number, requestType: ChatSourceType);
}
}
The TypeScript gets a property can be created and parsed based on the above interface.
get ChatHub(): ChatHub {
return <ChatHub>(<any>$.connection).chatHub;
}
Now, through the above property, we can access all methods of server and client calls with IntelliSense on TS.
The shown example is Skelton of the implementation but it can give an idea of how to make SignalR strongly typed.

Win form not opening after deploying Outlook VSTO Addin?

I have deployed an Outlook Add-in using Visual Studio Installer project(followed this link) with C#.
The setup is getting installed correctly(.msi) and I am able to see it inside Options -> Add-in, also the ribbon is visible with the controls.(It's a winform)
Unfortunately, when I hit the button(inside Ribbon), nothing happens.
Code in Ribbon.cs:
private void button1_Click(object sender, RibbonControlEventArgs e)
{
Form1 formObj = new Form1();
formObj.FormBorderStyle = FormBorderStyle.FixedDialog;
formObj.MaximizeBox = false;
formObj.MinimizeBox = false;
formObj.StartPosition = FormStartPosition.CenterScreen;
//formObj.ShowDialog();
formObj.Show();
}
The code is working fine while debugging solution.
I searched but found nothing related. What can be the issue here or I am missing something?
Regards,
Ank
Most probably your form is displayed behind the Outlook window. To get it visible on top of Outlook windows you must specify the parent window handle. You can retrieve it by casting the Outlook window instance like Explorer or Inspector to the IOLEWindow interface and using the IOleWindow::GetWindow method which retrieves a handle to one of the windows participating in in-place activation (frame, document, parent, or in-place object window).
The Show or ShowDialog method accepts an instance of the IWin32Window interface which represents your parent window handle.
/// <summary>
/// Implemented and used by containers and objects to obtain window handles
/// and manage context-sensitive help.
/// </summary>
/// <remarks>
/// The IOleWindow interface provides methods that allow an application to obtain
/// the handle to the various windows that participate in in-place activation,
/// and also to enter and exit context-sensitive help mode.
/// </remarks>
[ComImport]
[Guid("00000114-0000-0000-C000-000000000046")]
[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleWindow
{
/// <summary>
/// Returns the window handle to one of the windows participating in in-place activation
/// (frame, document, parent, or in-place object window).
/// </summary>
/// <param name="phwnd">Pointer to where to return the window handle.</param>
void GetWindow (out IntPtr phwnd) ;
/// <summary>
/// Determines whether context-sensitive help mode should be entered during an
/// in-place activation session.
/// </summary>
/// <param name="fEnterMode"><c>true</c> if help mode should be entered;
/// <c>false</c> if it should be exited.</param>
void ContextSensitiveHelp ([In, MarshalAs(UnmanagedType.Bool)] bool fEnterMode) ;
}
public IWin32Window getWindowHandle()
{
dynamic activeWindow = Globals.ThisAddIn.Application.ActiveWindow();
IOleWindow win = activeWindow as IOleWindow;
window = win.GetWindow();
IWin32Window wind = Control.FromHandle(window);
return wind;
}
// and pass it to the Show method
form.Show(getWindowHandle());
I got the issue here.
Actually I have a dependency for win form(Materialskin.dll), which was excluded from the detected dependecies(inside setup project), as mentioned in the tutorial.
I changed the exclude property value to false only for Materialskin.dll and rebuild the solution.
After successful installation of created msi package, the Addin is working fine.

Postsharp AOP MethodInterception Aspect Not Working

I was trying to add a Permission based Attribute to be used by an existing Windows application built with WPF that I should work on.
the idea was to intercept the calls to the Canexecute methods of certain commands and return false -which would disable the buttons-
so I made a simple example on a solution where I have added the Nuget Package of Postsharp and override the OnInvoke Method as follows:
[Serializable]
public class PermissionBasedAttribute : MethodInterceptionAspect
{
public string PermissionName { get; private set; }
public PermissionBasedAttribute (string permissionName)
{
PermissionName = permissionName;
}
/// <summary>
/// Upon invocation of the calling method, the PermissionName is checked.
/// If no exception is thrown, the calling method proceeds as normal.
/// If an exception is thrown, the virtual method MethodOnException is called.
/// </summary>
/// <seealso cref="MethodOnException(SecurityPermissionException)"/>
public override void OnInvoke(MethodInterceptionArgs args)
{
try
{
/*
* some logic to check the eligibility of the user, in case of
not authorised an exception should be thrown.
*/
args.Proceed();
}
catch (SecurityPermissionException ex)
{
args.ReturnValue = false;
// MethodOnException(ex);
}
}
/// <summary>
/// Method can be overridden in a derived attribute class.
/// Allows the user to handle the exception in whichever way they see fit.
/// </summary>
/// <param name="ex">The exception of type SecurityPermissionException</param>
public virtual void MethodOnException(SecurityPermissionException ex)
{
throw ex;
}
}
anyway , every thing works fine within the small example with different approaches in the simple example like using devexpress or not.
but when added to the existing application it does not work at all, to be more precise: the OnInvoke method is never hit, while the Constructor of the Attribute is already being called.
I am not sure what is wrong, but an additional information is that I found out that the existing solution was utilizing Postsharp already for logging aspects.
so I have used the exact same version as the one used by the Project which is 4.2.26 by choosing this version from the Nuget Package Manager.
Another thing I have tried is that i have implemented the CompileTimeValidate method and purposefully added a code that should raise an exception upon build.
in the Small -Working- example it has raised an exception upon build.
while in the Existing application where it has not worked yet. upon build it does not raise any exception !!!.
Update:
I am Using at as follow:
[PermissionBasedAttribute("Permission1") ]
public bool CanShowView()
{
return true;
}
It turns out that Post Sharp was disabled in this project !!! I Installed the Post sharp Extension, then went to Post Sharp Section i found that it was disabled, once enabled it worked out fine

Open a Custom Tool Window on Visual Studio without calling it from the View menu

I've seen that through the Visual Studio Extensibility tools, you can add custom commands such as Light Bulbs, Tool Windows (like the Properties panel) and so on...
Basically I am trying to create a Custom Tool Window that gets opened not from the View -> Other Windows menu but from a button that I have created on my own UI.
For this, I tried to create a delegate that basically calls my PaneResultsPackage class and then calls the Initialize() method that is supposed to triger all the logic. However, it doesn't generate the Pane since the package object appears to be empty.
This is basically the class:
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // Info on this package for Help/About
[ProvideMenuResource("Menus.ctmenu", 1)]
[ProvideToolWindow(typeof(ResourceAnalysisPaneResults))]
[Guid(ResourceAnalysisPaneResultsPackage.PackageGuidString)]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")]
public sealed class ResourceAnalysisPaneResultsPackage : Package
{
/// <summary>
/// ResourceAnalysisPaneResultsPackage GUID string.
/// </summary>
public const string PackageGuidString = "29677396-e861-4672-804e-75cc557f1874";
/// <summary>
/// Initializes a new instance of the <see cref="ResourceAnalysisPaneResults"/> class.
/// </summary>
public ResourceAnalysisPaneResultsPackage()
{
// Inside this method you can place any initialization code that does not require
// any Visual Studio service because at this point the package object is created but
// not sited yet inside Visual Studio environment. The place to do all the other
// initialization is the Initialize method.
}
#region Package Members
/// <summary>
/// Initialization of the package; this method is called right after the package is sited, so this is the place
/// where you can put all the initialization code that rely on services provided by VisualStudio.
/// </summary>
protected override void Initialize()
{
ResourceAnalysisPaneResultsCommand.Initialize(this);
base.Initialize();
}
** Here is the call to the delegate**
public void OnSchemaAnalyzed(object source, EventArgs e)
{
Initialize();
}
#endregion
}
All this code is prepopulated except the OnSchemaAnalyzed method that is for the delegate that I created.
How can I have a package object that does not contain null properties without invoking it through the View -> Windows tab?
What is the right approach then?
You shouldn't call Initialize yourself - it is automatically called by Visual Studio when instantiating your package.
To show a tool window, look at the ShowToolWindow method created by default when you add a tool window to your project:
ToolWindowPane window = this.package.FindToolWindow(typeof(ResourceAnalysisPaneResults), 0, true);
IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame;
windowFrame.Show();

WebDriver Actions.Perform() or Actions.Build().Perform()

I use Selenium2 WebDriver with C#
Actions.Build - returns a composite IAction which can be used to perform the actions. (IActions has a Perform method to execute the actions)
Actions.Perform - Performs the currently built action.
In most of the examles use Actions like this:
new Actions(IWebDriverObj).actions...Build().Perform()
but this works as well
new Actions(IWebDriverObj).actions...Perform() //no Build before Perform
Is it necessary to use the Build() before the Perform() or Build() is for some compatibility purpose only?
Thanks in advance for the answers
Always keep in mind that Selenium is open source.
The source of WebDriver/Interactions/Actions.cs is here, clearly you can see Perform() includes Build(), so the answer is no, you don't need to build before perform, unless you want to pass the built IAction around without performing.
/// <summary>
/// Builds the sequence of actions.
/// </summary>
/// <returns>A composite <see cref="IAction"/> which can be used to perform the actions.</returns>
public IAction Build()
{
CompositeAction toReturn = this.action;
this.action = new CompositeAction();
return toReturn;
}
/// <summary>
/// Performs the currently built action.
/// </summary>
public void Perform()
{
this.Build().Perform();
}
Also, for anyone else reading this post:
Java binding: build() is included in perform(). Source: interactions/Actions.java
Ruby/Python: only have perform, no such thing called build. Source: action_chains.py, action_builder.rb
Actually, the .perform() includes a .build().
So you can only write : new Actions(IWebDriverObj).actions....perform()

Categories

Resources