xsharp.eu • Creating COM modules/ActiveX controls in X# for use from other languages
Page 1 of 1

Creating COM modules/ActiveX controls in X# for use from other languages

Posted: Mon Sep 09, 2024 4:25 pm
by Chris
Hello all,

As per the subject, there's some good information in Wolfgang's wiki, about creating COM modules in X# and using them form VO, written by Wolfgang and Dick:

https://docs.xsharp.it/doku.php?id=com_module_sample
https://docs.xsharp.it/doku.php?id=com_module_sample_vs

I know Irwin has had some success with creating Windows.Forms controls to be used from VFP, I hope he can share this with us!

Re: Creating COM modules/ActiveX controls in X# for use from other languages

Posted: Tue Sep 10, 2024 2:55 am
by xinjie
Chris wrote: Mon Sep 09, 2024 4:25 pm I know Irwin has had some success with creating Windows.Forms controls to be used from VFP, I hope he can share this with us!
WOW,I hope so too.

Re: Creating COM modules/ActiveX controls in X# for use from other languages

Posted: Tue Sep 10, 2024 3:32 am
by wriedmann
Hi Xinjie,
after all, X# is a descendant from C# with a lot of things added to be compatible with xBase dialects and to boost the programmers productivity, so it can also produce COM libraries to be used in Win32 applications.
Nearly all my legacy Win32 applications are using a least one X# COM library to add functionalities otherwise impossible to implement.
Wolfgang

Re: Creating COM modules/ActiveX controls in X# for use from other languages

Posted: Tue Sep 10, 2024 12:25 pm
by Irwin
Hi everybody,

Hope this guide explains the steps necessary to expose a .NET control to COM, making it accessible from environments like Visual FoxPro/VO/VB6, etc. The example focuses on creating a custom button control.

When exposing a control via COM, interfaces play an important role, especially when using ClassInterfaceType.None. This type of class interface means you must define the methods yourself and explicitly expose them.

a) Define Event Interface
This interface (IControlEvents) will handle the events that your control will raise. For example, a MyButtonClick event can be defined like this:

Code: Select all

[Guid("ea567180-887c-48f0-8867-c51aaef20750")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
INTERFACE IControlEvents
    [DispId(1)]
    METHOD MyButtonClick() AS VOID
END INTERFACE
[Guid]: Assigns a unique identifier for the interface.
[InterfaceType]: InterfaceIsIDispatch allows the interface to be accessed via late binding, which is useful in environments like Visual FoxPro.
[DispId]: Assigns a dispatch ID to the method for COM.

b) Define Method Interface
If you are using ClassInterfaceType.None, you need to define the methods manually using an interface like ICustomButton. This interface ensures that any method you want to expose is explicitly declared. Here is an example for a Button_Click method:

Code: Select all

INTERFACE ICustomButton
    METHOD Button_Click(sender AS OBJECT, e AS EventArgs) AS VOID
END INTERFACE
Obligatory for ClassInterfaceType.None: When using ClassInterfaceType.None, no automatic interface is generated. This means you must explicitly declare any methods you want to expose through COM.

Now that the interfaces are defined, you can implement your custom control. The control class will inherit from UserControl (or any other Windows Forms control) and implement the ICustomButton interface.

Code: Select all

[Guid("6e3a579a-59a4-42c9-b9c7-3b162f5c0005")]
[ComSourceInterfaces(typeof(IControlEvents))]
[ProgId("ActiveX.MySample.MyCustomButton")]
[ClassInterface(ClassInterfaceType.None)]
PARTIAL CLASS MyCustomButton INHERIT System.Windows.Forms.UserControl ;
    IMPLEMENTS ICustomButton
[ComSourceInterfaces]: Specifies which interfaces (like IControlEvents) will be used to trigger events accessible via COM.
[ProgId]: The Programmatic Identifier that COM clients like Visual FoxPro will use to instantiate the control.
[ClassInterface(ClassInterfaceType.None)]: Disables automatic generation of COM interfaces for the class. This is why you must define your own interfaces, like ICustomButton, to expose the necessary methods.

In this step, you will implement the event handler and expose the necessary methods in your control.

a) Implement Button Click Event
The Button_Click method, defined in the ICustomButton interface, is implemented as follows:

Code: Select all

METHOD Button_Click(sender AS System.Object , e AS System.EventArgs) AS VOID
    IF MyButtonClick != NULL
        MyButtonClick()
    ENDIF
END METHOD
This method triggers the MyButtonClick event whenever the button is clicked.

b) Declare the Event
To make the event accessible via COM, define a delegate and an event:

Code: Select all

DELEGATE MyButtonClickEvent AS VOID
EVENT MyButtonClick AS MyButtonClickEvent

Once the control is implemented, it must be registered in the Windows registry so that COM clients like FoxPro can find and use it.

a) Register the Control
This is done using the ComRegisterFunction and ComUnregisterFunction attributes. These methods modify the Windows registry to register or unregister the control.

Code: Select all

[ComRegisterFunction()]
PUBLIC STATIC METHOD RegisterClass(tcKey AS STRING) AS VOID
    VAR loSb := StringBuilder{tcKey}
    loSb:Replace("HKEY_CLASSES_ROOT\", "")
    
    VAR k := Registry.ClassesRoot:OpenSubKey(loSb:ToString(), TRUE)
    VAR ctrl := k:CreateSubKey("Control")
    ctrl:Close()
    
    VAR inprocServer32 := k:OpenSubKey("InprocServer32", TRUE)
    inprocServer32:SetValue("CodeBase", Assembly.GetExecutingAssembly():CodeBase)
    inprocServer32:Close()
    
    k:Close()
END METHOD

[ComUnregisterFunction()]
PUBLIC STATIC METHOD UnregisterClass(tcKey AS STRING) AS VOID
    VAR loSb := StringBuilder{tcKey}
    loSb:Replace("HKEY_CLASSES_ROOT\", "")
    
    VAR k := Registry.ClassesRoot:OpenSubKey(loSb:ToString(), TRUE)
    k:DeleteSubKey("Control", FALSE)
    
    k:OpenSubKey("InprocServer32", TRUE)
    k:DeleteSubKey("CodeBase", FALSE)
    
    k:Close()
END METHOD
The RegisterClass method adds the control to the Windows registry so it appears in COM.
The UnregisterClass method removes it from the registry.

1. Don't forget to add the ComVisible(true) attribute in the AssemblyInfo.prg file.
2. You have to convert your compiled assembly in the COM equivalent by using the Assembly Registration Tool (RegAsm)
3. Use your own guids, I've built a plugin that adds an option in the Tools menu (Generate Guid) that injects the guid in your editor caret current position. @Chris where can we share all the plugin we build?

Re: Creating COM modules/ActiveX controls in X# for use from other languages

Posted: Tue Sep 10, 2024 1:02 pm
by xinjie
Hi, Irwin
Thank you!

Re: Creating COM modules/ActiveX controls in X# for use from other languages

Posted: Tue Sep 10, 2024 1:22 pm
by Chris
Thanks a lot Irwin!
As for the XIDE plugins, we will create a section in the downloads area for that. But it has to wait till the next official X# release, which will contain the new IDE as well (which includes plugin system additions that your plugin probably depends on).