سؤال

I have created an ActiveX dll assembly that launches an application. I have called this dll via JavaScript in browser. The app is launched on click of URL.

For this script to work, I need to go to the IE settings and enable "Initialize and script activex controls not marked as safe". Is there any way to code the ActiveX object to mark it as safe?

I have used the SN tool to generate a key (.snk file), compiled the DLL with this key. So the dll is signed but yet it doesnot run unless I enable "Initialize and script activex controls not marked as safe" in IE settings.

This is the article which I have implemented and would like to mark it as safe:- Writing an ActiveX control in C#.

I have got a reference to the safe controls concept (IObjectSafety) on this microsoft link:-

I am not able to understand as to where this code needs to be placed. Any help would make it more clear..

هل كانت مفيدة؟

المحلول 2

The following link has a perfect example which is answer to creating as well as marking an activeX control as Safe:-Creating activex objects with c#

Creating activex objects with c#

The purpose of this guide is to provide simple steps for creating a simple ActiveX Control that can be loaded and used within an IE hosted web page.

Background

ActiveX is a Microsoft technology used for creating re-usable components. This technology, also called Object Linking and Embedding (OLE), along with COM is heavily used within the Microsoft environment and application development. For the duration of this article, I will use the term COM to encompass all similar Microsoft related technologies: ActiveX, OLE, Automation, etc… as it is the framework by which components are created. Anyone interested in creating reusable components should have some familiarity with COM. A very good resource is “Essential COM” by Don Box.

The idea behind COM is to allow the creation of components that can be consumed by a variety of clients. This means that I can create a component using C++ which can then be used by an application written in VB, or even a web page which we will be performing shortly. Traditionally, ActiveX objects were usually written in C++ and VB but can actually be written in any language as long as they are COM-aware.

This means that we can create COM objects with the .NET platform. A survey of resources/tutorials on COM and .NET on the internet by and large only tell you how to consume unmanaged COM objects via Interop. So my aim with this guide is to show how to use this powerful feature. Also, FYI, there is a .NET technology very similar to what will be describe below which allows hosting Windows Forms Controls inside of IE (don’t know the official term/name for this).

That’s enough babbling lets get going with the example.

Requirements

  • Familiarity with C#.
  • Development Environment. I will be using Visual Studio

    1. (2003 should also work. Don’t know about the express editions though.)
  • HTML, Javascript

  • IE. ActiveX objects cannot be hosted in any other browser.

Creating the ActiveX Control

The first step is to create a C# DLL Project. This can be done with the following steps:

  1. Launch Visual Studio
  2. Select File -> NewProject
  3. Select “Other Language” -> “Visual C#” for the project type
  4. Select “Class Library” template

When the project is created, go ahead and rename Class1.cs to whatever you want. I renamed it to HelloControl.cs

The ActiveX control we will be creating is the ubiquitous hello world! The code and explanation follow.

HelloControl.cs

    using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;

namespace csharp.activex.sample
{
    /// <summary>
    /// A very simple interface to test ActiveX with.
    /// </summary>
    [
        Guid( "E86A9038-368D-4e8f-B389-FDEF38935B2F"),
        InterfaceType( ComInterfaceType.InterfaceIsDual),
        ComVisible( true)
    ]
    public interface IHello
    {
        [DispId(1)]
        string Hello();

        [DispId(2)]
        int ShowDialog(string msg);
    };

    [
        Guid("873355E1-2D0D-476f-9BEF-C7E645024C32"),

        // This is basically the programmer friendly name
        // for the guid above. We define this because it will
        // be used to instantiate this class. I think this can be
        // whatever you want. Generally it is
        // [assemblyname].[classname]
        ProgId("csharpAx.CHello"),

        // No class interface is generated for this class and
        // no interface is marked as the default.
        // Users are expected to expose functionality through
        // interfaces that will be explicitly exposed by the object
        // This means the object can only expose interfaces we define
        ClassInterface(ClassInterfaceType.None),

        // Set the default COM interface that will be used for
        // Automation. Languages like: C#, C++ and VB
        // allow to query for interface's we're interested in
        // but Automation only aware languages like javascript do
        // not allow to query interface(s) and create only the
        // default one
        ComDefaultInterface(typeof(IHello)),
        ComVisible(true)
    ]
    public class CHello : IHello
    {

        #region [IHello implementation]
        public string Hello()
        {
            return "Hello from CHello object";
        }


        public int ShowDialog(string msg)
        {
            System.Windows.Forms.MessageBox.Show(msg, "");
            return 0;
        }
        #endregion
    };
}

The HelloControl.cs file consists of one class and one interface. So let’s start with the interface definition and follow up with the CHello class.

IHello Interface

An interface is generally described as a contract. Once an interface is designed and published for use it should NEVER change. Let me repeat this, it should NEVER change.

Several attributes have been defined for this interface.

1) Guid – This is a unique identifier. Use guidgen.exe to generate your own 2) InterfaceType – This determines which base class to expose to COM.

The InterfaceType attribute demands a little further explaination. Here is how it is defined.

public enum ComInterfaceType
{
    InterfaceIsDual = 0,
    InterfaceIsIUnknown = 1,
    InterfaceIsIDispatch = 2,
}

All COM interfaces and objects must at a minimum inherit the IUnknown interface (see Appendix A.). This interface allows clients to create the COM objects via CoCreateInstance() and search for other interfaces along with handling object lifetime management.

The IDispatch interface allows objects to be used in Automation-aware only languages like javascript (see Appendix B.). This interface, allows for client applications to query for properties and methods that the object supports at run- time and execute them. This is very much like Reflection in .Net.

Dual simply means a combination of the two above. The main reason for this is to allow the interface to be created without all of the overhead that IDispatch has in situations where it’s not needed.

Lastly, you will notice the DispId(#) attribute for each method in the IHello interface. This is needed when calling Invoke from the IDispatch interface.

CHello class

The last thing to discuss is the CHello class. This class simple implements the IHello interface and nothing more. The ShowDialog() simply shows that we can execute objects that require some sort of UI, in this case a messagebox. The attributes are described in the comments.

Compiling & Registering

Once the code is written up go ahead an compile the ActiveX assembly. The assembly will be called [projectname].dll inside the bin folder of the project.

The next step is to register. Do this opening up a command prompt and go to the location of the dll. To register the assembly type:

C:\Regasm [projectname].dll /codebase /tlb

Make sure replace “[projectname]” with the correct name of your dll. You should get some output that says the assembly was exported correctly. Something like the following.

Microsoft ® .NET Framework Assembly Registration Utility 2.0.50727.42 Copyright © Microsoft Corporation 1998-2004. All rights reserved. Types registered successfully Assembly exported to '[TLB location] ', and the type library was registered successfully

To unregister the assembly pass the “/unregister” flag to Regasm. You will need to unregister and close IE when changes need to be made to the assembly. If you don’t unregister your assembly prior to making code changes you may end up with bogus registry entries and a pain to clean up. The reason for closing IE is to make sure that all reference to your assembly have been released.

Creating HTML Test Page

We are just about done. All we need to do now is create a test page. This should be really simple, but here it is nonetheless. The Javascript to create the ActiveX object is also provided.

Test.html

<html xmlns="http://www.w3.org/1999/xhtml">
<head> <title>C# ActiveX Test</title> </head>

<body onload="myload();">
<h1>This is Our ActiveX Test Page h1>

The message from the ActiveX Control is [
<div id="axmsg"></div>
]

<script type ="text/javascript">
function myload()
{
    var myAx = new ActiveXObject("csharpAx.CHello");
    if(myAx != null)
    {
        myAx.ShowDialog("hello from asp.net"); 
        var d = document.getElementById("axmsg");

        var s = myAx.Hello();
        d.outerText = s;
    }
    else
        lert("NOOOO... we failed");
}
</script>
</body></html>

To create our ActiveX object in Javascript we use the ActiveXObject(). The parameter for this is generally the PROGID that we specified in its definition. If a PROGID was not specified in its definition then I think you can pass in “AssemblyName.ClassName”. The rest of the javascript simply calls our ActiveX objects methods.

That’s it!!! We should have a fully functional example. This ability to execute ActiveX objects from javascript is very powerful. Debates and arguments about security and safety of ActiveX objects are well known and I will not throw more fodder to the fire. However, imagine creating a Win32 or even a Winform application where the UI is written in HTML via IWebBrowser2 and all the UI interaction handled with your ActiveX object, just food for thought. Though, now that I think of it, WPF is already doing this. Oh Well.

Hopefully you found this guide on how to create an ActiveX object with C# helpful.

-skaoth-

Further Modifications

The example above, though complete (hopefully error free), has one slight problem. When the html page is loaded we get a nasty message box saying

“An ActiveX control on this page might be unsafe…blah blah blah”

So how do we fix this? Well it is quit simple really. We need to Implement an interface called IObjectSafety. To do this, add a new .cs file to the project.

IObjectSafety.cs

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Text;

namespace csharp.activex.sample
{
    [
        Serializable,
        ComVisible(true)
    ]
    public enum ObjectSafetyOptions
    {
        INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001,
        INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002,
        INTERFACE_USES_DISPEX = 0x00000004,
        INTERFACE_USES_SECURITY_MANAGER = 0x00000008
    };

    //
    // MS IObjectSafety Interface definition
    //
    [
        ComImport(),
        Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064"),
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
    ]
    public interface IObjectSafety
    {
        [PreserveSig]
        long GetInterfaceSafetyOptions( ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions);

        [PreserveSig]
        long SetInterfaceSafetyOptions( ref Guid iid, int dwOptionSetMask, int dwEnabledOptions);
    };

    //
    // Provides a default Implementation for
    // safe scripting.
    // This basically means IE won't complain about the
    // ActiveX object not being safe
    //
    public class IObjectSafetyImpl : IObjectSafety
    {
        private ObjectSafetyOptions m_options =
            ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_CALLER | 
            ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_DATA;

        #region [IObjectSafety implementation]
        public long GetInterfaceSafetyOptions( ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions)
        {
            pdwSupportedOptions = (int)m_options;
            pdwEnabledOptions = (int)m_options;
            return 0;
        }

        public long SetInterfaceSafetyOptions(ref Guid iid, int dwOptionSetMask, int dwEnabledOptions)
        {
            return 0;
        }
        #endregion
    };
}

Then modify the CHello object so that it inherits from the IObjectSafetyImpl class so that it looks like

public class CHello : IObjectSafetyImpl, IHello
{
…
};

With that, the dialog about the ActiveX object not being safe should disappear.

References

ATL Internals – Brent E. Rector, Chris Sells. Essential COM – Don Box.

http://msdn2.microso...yw6(VS.80).aspx http://msdn2.microso...y/aa768224.aspx http://www.c-sharpco...tiveXInNet.aspx

Appendix A: IUnknown

interface IUnknown
{
virtual HRESULT QueryInterface(REFIID riid, void **ppvObject) = 0;
virtual ULONG AddRef(void) = 0;
virtual ULONG Release(void) = 0;

};

Appendix B: IDispatch

interface IDispatch : public IUnknown
{
virtual ULONG GetTypeInfoCount(unsigned int FAR* pctinfo) = 0;
virtual HRESULT GetTypeInfo(unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR* ppTInfo ) = 0;
virtual ULONG GetIDsOfNames(
REFIID riid,
OLECHAR FAR* FAR* rgszNames,
unsigned int cNames,
LCID lcid,
DISPID FAR* rgDispId) = 0;
virtual ULONG Invoke(
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS FAR* pDispParams,
VARIANT FAR* pVarResult,
EXCEPINFO FAR* pExcepInfo,
unsigned int FAR* puArgErr) = 0;
}; 

نصائح أخرى

I picked up pieces of code (and knowledge) from different places and here is my working version - a interface:

    [Serializable, ComVisible(true)]
    public enum ObjectSafetyOptions
    {
        INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001,
        INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002,
        INTERFACE_USES_DISPEX = 0x00000004,
        INTERFACE_USES_SECURITY_MANAGER = 0x00000008
    }

    //
    // MS IObjectSafety Interface definition
    //
    [
        ComImport(),
        Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064"),
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
    ]
    public interface IObjectSafety
    {
        [PreserveSig]
        long GetInterfaceSafetyOptions(ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions);

        [PreserveSig]
        long SetInterfaceSafetyOptions(ref Guid iid, int dwOptionSetMask, int dwEnabledOptions);
    }
  • and the implementation:

        #region safe for scripting
        private ObjectSafetyOptions m_options = ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_CALLER | ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_DATA;
        public long GetInterfaceSafetyOptions(ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions)
        {
            pdwSupportedOptions = (int)m_options;
            pdwEnabledOptions = (int)m_options;
            return 0;
        }
    
        public long SetInterfaceSafetyOptions(ref Guid iid, int dwOptionSetMask, int dwEnabledOptions)
        {
            return 0;
        }
        #endregion safe for scripting
    

Now IE does not nag me with the confirmation dialog.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top