48

I using Visual Studio 2010 (.NET 4). I need to create a COM object (in C#) and have no idea how to get started (what type of project to use,etc.)

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Eyal
  • 1,191
  • 1
  • 9
  • 7
  • Doesn't have to be an ActiveX if anyone know how to create a COM in C# that should get me started – Eyal Jul 29 '10 at 07:12
  • @Bujutsu however, the ActiveX part will be the more difficult to find information on: simple WinForms is widely demonstrated. – Richard Nov 24 '15 at 13:34
  • @Richard the peculiar thing about my Answer is that I have given explicit and comprehensive full solution for a windows form secure activex. Another not so easy difference is the different IDE of the linked answer, wich is for Visual Studio 2010. Thank you for your comment. – Giulio Caccin Nov 24 '15 at 16:13
  • Well, if anyone need a help yet, this link is a very good start: https://blogs.msdn.microsoft.com/asiatech/2011/12/05/how-to-develop-and-deploy-activex-control-in-c/ – Andersson Melo Jun 29 '18 at 00:57

3 Answers3

70

OK I found the solution and I'll write it here for the common good.

  1. Start VS2010 as administrator.
  2. Open a class library project (exmaple - MyProject).
  3. Add a new interface to the project (see example below).
  4. Add a using System.Runtime.InteropServices; to the file
  5. Add the attributes InterfaceType, Guid to the interface.
  6. You can generate a Guid using Tools->Generate GUID (option 4).
  7. Add a class that implement the interface.
  8. Add the attributes ClassInterface, Guid, ProgId to the interface.
    ProgId convention is {namespace}.{class}
  9. Under the Properties folder in the project in the AssemblyInfo file set ComVisible to true.
  10. In the project properties menu, in the build tab mark "Register for COM interop"
  11. Build the project

now you can use your COM object by using it's ProgID.

example: the C# code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Runtime.InteropServices;

namespace Launcher
{

    [InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
    public interface ILauncher
    {
        void launch();
    }

    [ClassInterface(ClassInterfaceType.None), Guid("YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYY"), ProgId("Launcher.Launcher")]
    public class Launcher : ILauncher
    {
        private string path = null;

        public void launch()
        {
            Console.WriteLine("I launch scripts for a living.");

        }

    }
}

and VBScript using the COM:

set obj = createObject("PSLauncher.PSLauncher") obj.launch()

and the output will be:

I launch scripts for a living

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Eyal
  • 1,191
  • 1
  • 9
  • 7
  • 2
    Additionally, if you built the assembly and are having trouble using it: ("Could not create object" error) http://stackoverflow.com/questions/1208180/classic-asp-using-com-on-x64-windows-server-2008-and-iis7 http://stackoverflow.com/questions/1281052/classic-asp-using-com-net-interop-64-bit-windows-server-2008-iis-7-server-crea/1343271#1343271 – Zachary Yates Dec 05 '12 at 22:47
  • i followed the above steps and my macro is as follows Sub test() Dim ob As Object Set ob = CreateObject("ClassCom.Name") Dim name As String name = ob.launc() End Sub It said "Error 429" activex cant create object what might have gone wrong? – Vignesh Subramanian May 05 '15 at 05:07
  • I followed the same but no luck. getting same error Activex cant created when i launch the exe in remote machine using VB.Net – Arul Sidthan Jul 16 '18 at 07:13
  • Sample VB.Net code: Protected Sub Unnamed1_Click(sender As Object, e As EventArgs) Dim strRemoteServer = "xxxxx" Dim strUser = "xxx" Dim strPassword = "yyy" Dim strCommand strCommand = "" Dim objWMIService = GetObject("winmgmts:" & "{impersonationlevel=impersonate}!\\" & strRemoteServer & "\root\cimv2") Dim objProcess = objWMIService.Get("Win32_Process") Dim objQWCreate = objProcess.Create(strCommand) End Sub Error : 'Cannot create ActiveX component.' – Arul Sidthan Jul 16 '18 at 07:36
16

Creation Steps

  1. Start Visual Studio 2013 as administrator
  2. Install Visual Studio extension Microsoft Visual Studio Installer Projects
  3. Create a class library project (WinFormActivex)
  4. Create your example window form (MainWindow)
  5. Create a new component interface(ILauncher)
  6. Create a new security interface (IObjectSafety)
  7. Create the component control (Launcher) that implement interfaces and launch the window.
  8. Check that all GUIDs are generated by you
  9. Check that the project is marked for COM
  10. Create the setup project (LauncherInstaller) with the primary output of WinFormActivex with the property Register = vsdrpCOM
  11. Install LauncherInstaller
  12. Run your test page in explorer (test.html)

MainWindow You can create a normal Form, here is pre-generated.

public partial class MainWindow : Form
{
    public MainWindow()
    {
        InitializeComponent();
    }

    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.textBox1 = new System.Windows.Forms.TextBox();
        this.textBox2 = new System.Windows.Forms.TextBox();
        this.SuspendLayout();
        //
        // textBox1
        //
        this.textBox1.Location = new System.Drawing.Point(42, 23);
        this.textBox1.Name = "textBox1";
        this.textBox1.Size = new System.Drawing.Size(100, 20);
        this.textBox1.TabIndex = 0;
        //
        // textBox2
        //
        this.textBox2.Location = new System.Drawing.Point(42, 65);
        this.textBox2.Name = "textBox2";
        this.textBox2.Size = new System.Drawing.Size(100, 20);
        this.textBox2.TabIndex = 0;
        //
        // MainWindow
        //
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(284, 261);
        this.Controls.Add(this.textBox2);
        this.Controls.Add(this.textBox1);
        this.Name = "MainWindow";
        this.Text = "MainWindow";
        this.ResumeLayout(false);
        this.PerformLayout();

    }

    #endregion

    private System.Windows.Forms.TextBox textBox1;
    private System.Windows.Forms.TextBox textBox2;
}

ILauncher

using System.Runtime.InteropServices;
namespace WinFormActivex
{
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [Guid("94D26775-05E0-4B9C-BC73-C06FE915CF89")]
    public interface ILauncher
    {
        void ShowWindow();
    }
}

IObjectSafety

[ComImport()]
[Guid("51105418-2E5C-4667-BFD6-50C71C5FD15C")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IObjectSafety
{
    [PreserveSig()]
    int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions);
    [PreserveSig()]
    int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions);
    }

Launcher Please generate your GUID here.

 [ComVisible(true)]
 [ClassInterface(ClassInterfaceType.None)]
 [Guid("D100C392-030A-411C-92B6-4DBE9AC7AA5A")]
 [ProgId("WinFormActivex.Launcher")]
 [ComDefaultInterface(typeof(ILauncher))]
 public class Launcher : UserControl, ILauncher, IObjectSafety
 {
     #region [ ILauncher ]

     public void ShowWindow()
     {
         var f = new MainWindow();
         f.StartPosition = FormStartPosition.Manual;
         f.Location = Screen.AllScreens[0].Bounds.Location;
         f.WindowState = FormWindowState.Normal;
         f.WindowState = FormWindowState.Maximized;
         f.ShowInTaskbar = false;
         f.Show();
     }

     #endregion

     #region [ IObjectSafety ]

     public enum ObjectSafetyOptions
     {
         INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001,
         INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002,
         INTERFACE_USES_DISPEX = 0x00000004,
         INTERFACE_USES_SECURITY_MANAGER = 0x00000008
     };

     public int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions)
     {
         ObjectSafetyOptions m_options = ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_CALLER | ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_DATA;
         pdwSupportedOptions = (int)m_options;
         pdwEnabledOptions = (int)m_options;
         return 0;
     }

     public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
     {
         return 0;
     }

     #endregion
 }

test.html Please check that your CLSID match (Launcher) GUID.

<html>
    <head>
        <objectname="activexLauncher" style='display:none' id='activexLauncher' classid='CLSID:D100C392-030A-411C-92B6-4DBE9AC7AA5A' codebase='WinFormActivex'></object>
      <script language="javascript">
        <!-- Load the ActiveX object  -->
        var x = new ActiveXObject("WinFormActivex.Launcher");
        alert(x.GetText());
      </script>
    </head>
    <body>
    </body>
</html>

References

Community
  • 1
  • 1
Giulio Caccin
  • 2,962
  • 6
  • 36
  • 57
13

You could use a class library project. Declare a type with methods that will be exposed as a COM object.

Make sure that the assembly has been made COM-visible:

alt text

And finally register it using regasm.exe:

regasm.exe /codebase mylib.dll

Now the assembly is exposed as a COM object and the type you declared can be consumed by any client that supports COM.

Community
  • 1
  • 1
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    "Declare a type with methods that will be exposed as a COM object" ? Do i have to explicity implement an interface and give it a GUID or does the "Make Assem. COM Visible" option all that? – CloudyMarble Jun 08 '12 at 06:14
  • I also do not understand this. What did you mean by "Declare a type with methods that will be exposed as a COM object" ? – PeakGen May 14 '13 at 10:20