I'm using Visual Studio 2008, but other versions should work as well.
[ComVisible(true)] makes the interface visible to COM.
[InterfaceType(ComInterfaceType.InterfaceIsDual)] sets the COM interface type to Dual, see InterfaceTypeAttribute Class on MSDN.
[Guid("41E85D5D-C57A-4386-B722-4031D0B1E1B7")] let's us manually assign a GUID to the interface. Use guidgen.exe to generate your own.
We now have a COM visible control that implements the IHelloWorld interface.
[ComVisible(true)] makes the control visible to COM, see ComVisibleAttribute Class on MSDN.
[ClassInterface(ClassInterfaceType.None)] indicates that no class interface is generated for this class, see ClassInterfaceType Enumeration on MSDN.
[Guid("1FC0D50A-4803-4f97-94FB-2F41717F558D")] let's us manually assign a GUID to the control, see GuidAttribute Class on MSDN. Use guidgen.exe to generate your own.
[ProgId("AxControls.HelloWorld")] is a "user friendly" ID that we'll use later from JavaScript when initiating the control, see ProgIdAttribute Class on MSDN.
[ComDefaultInterface(typeof(IHelloWorld))] sets IHelloWorld as the default interface that will be exposed to COM, see ComDefaultInterfaceAttribute Class on MSDN.
[Guid("DCE0B4D4-FA5C-43e4-AE3E-0C881A6DD293")] This is the GUID of the original IObjectSafety interface. Do not change it.
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] sets the COM interface type to Unknown, see InterfaceTypeAttribute Class on MSDN.
This is just a simple implemetation of the IObjectSafety interface that will mark the control as safe. In "real life" there would probably be some sort of logic to determine if the control is safe or not.
- After starting Visual Studio click File -> New -> Project and select Class Library under C#.
- Call the project 'AxControls' and click OK.
2. Create a new class that inherits from UserControl
- Rename 'Class1.cs' to 'HelloWorld.cs', making sure to rename the class name as well.
- Add a project reference to System.Windows.Forms.
- Make the HelloWorld class inherit UserControl.
3. Create a new interface that exposes the controls methods and properties to COM interop
- Right click the project in Visual Studio and click Add -> New Item.
- Select 'Interface' from the list of components, name it 'IHelloWorld.cs' and click Add.
- Edit the 'IHelloWorld.cs' file so it looks like this:
01
using
System;
02
using
System.Collections.Generic;
03
using
System.Text;
04
using
System.Runtime.InteropServices;
05
06
namespace
AxControls
07
{
08
[ComVisible(
true
)]
09
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
10
[Guid(
"41E85D5D-C57A-4386-B722-4031D0B1E1B7"
)]
11
public
interface
IHelloWorld
12
{
13
string
GetText();
14
}
15
}
[ComVisible(true)] makes the interface visible to COM.
[InterfaceType(ComInterfaceType.InterfaceIsDual)] sets the COM interface type to Dual, see InterfaceTypeAttribute Class on MSDN.
[Guid("41E85D5D-C57A-4386-B722-4031D0B1E1B7")] let's us manually assign a GUID to the interface. Use guidgen.exe to generate your own.
4. Make the control class implement the new interface
Make the HelloWorld class implement the IHelloWorld interface and have the GetText() method return a string of your choice. This is what the file might look like:01 | using System; |
02 | using System.Collections.Generic; |
03 | using System.Text; |
04 | using System.Windows.Forms; |
05 | using System.Runtime.InteropServices; |
06 |
07 | namespace AxControls |
08 | { |
09 | [ComVisible( true )] |
10 | [ClassInterface(ClassInterfaceType.None)] |
11 | [Guid( "1FC0D50A-4803-4f97-94FB-2F41717F558D" )] |
12 | [ProgId( "AxControls.HelloWorld" )] |
13 | [ComDefaultInterface( typeof (IHelloWorld))] |
14 | public class HelloWorld : UserControl, IHelloWorld |
15 | { |
16 | #region IHelloWorld Members |
17 |
18 | public string GetText() |
19 | { |
20 | return "Hello ActiveX World!" ; |
21 | } |
22 |
23 | #endregion |
24 | } |
25 | } |
[ComVisible(true)] makes the control visible to COM, see ComVisibleAttribute Class on MSDN.
[ClassInterface(ClassInterfaceType.None)] indicates that no class interface is generated for this class, see ClassInterfaceType Enumeration on MSDN.
[Guid("1FC0D50A-4803-4f97-94FB-2F41717F558D")] let's us manually assign a GUID to the control, see GuidAttribute Class on MSDN. Use guidgen.exe to generate your own.
[ProgId("AxControls.HelloWorld")] is a "user friendly" ID that we'll use later from JavaScript when initiating the control, see ProgIdAttribute Class on MSDN.
[ComDefaultInterface(typeof(IHelloWorld))] sets IHelloWorld as the default interface that will be exposed to COM, see ComDefaultInterfaceAttribute Class on MSDN.
5. Mark the control as safe for scripting and initialization
By default IE will not allow initializing and scripting an ActiveX control unless it is marked as safe. This means that we won't be able to create instances of our ActiveX class with JavaScript by default. We can get around this by modifying the browser security settings, but a more elegant way would be to mark the control as safe. Before you do this to a "real" control, be sure to understand the consequences. I found an ancient (1996) MSDN article that explains this here. We will mark the control as safe by implementing the IObjectSafety interface.- Right click the project in Visual Studio and click Add -> New Item.
- Select 'Interface' from the list of components, name it 'IObjectSafety.cs' and click Add.
- Edit the 'IObjectSafety.cs' file so it looks like this:
01
using
System;
02
using
System.Collections.Generic;
03
using
System.Text;
04
using
System.Runtime.InteropServices;
05
06
namespace
AxControls
07
{
08
[ComImport()]
09
[Guid(
"CB5BDC81-93C1-11CF-8F20-00805F2CD064"
)]
10
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
11
interface
IObjectSafety
12
{
13
[PreserveSig()]
14
int
GetInterfaceSafetyOptions(
ref
Guid riid,
out
int
pdwSupportedOptions,
out
int
pdwEnabledOptions);
15
16
[PreserveSig()]
17
int
SetInterfaceSafetyOptions(
ref
Guid riid,
int
dwOptionSetMask,
int
dwEnabledOptions);
18
}
19
}
- Make the HelloWorld class implement the IObjectSafety interface. The end result should look something like this:
01
using
System;
02
using
System.Collections.Generic;
03
using
System.Text;
04
using
System.Windows.Forms;
05
using
System.Runtime.InteropServices;
06
07
namespace
AxControls
08
{
09
[ComVisible(
true
)]
10
[ClassInterface(ClassInterfaceType.None)]
11
[Guid(
"1FC0D50A-4803-4f97-94FB-2F41717F558D"
)]
12
[ProgId(
"AxControls.HelloWorld"
)]
13
[ComDefaultInterface(
typeof
(IHelloWorld))]
14
public
class
HelloWorld : UserControl, IHelloWorld, IObjectSafety
15
{
16
#region IHelloWorld Members
17
18
public
string
GetText()
19
{
20
return
"Hello ActiveX World!"
;
21
}
22
23
#endregion
24
25
#region IObjectSafety Members
26
27
public
enum
ObjectSafetyOptions
28
{
29
INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001,
30
INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002,
31
INTERFACE_USES_DISPEX = 0x00000004,
32
INTERFACE_USES_SECURITY_MANAGER = 0x00000008
33
};
34
35
public
int
GetInterfaceSafetyOptions(
ref
Guid riid,
out
int
pdwSupportedOptions,
out
int
pdwEnabledOptions)
36
{
37
ObjectSafetyOptions m_options = ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_CALLER | ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_DATA;
38
pdwSupportedOptions = (
int
) m_options;
39
pdwEnabledOptions = (
int
) m_options;
40
return
0;
41
}
42
43
public
int
SetInterfaceSafetyOptions(
ref
Guid riid,
int
dwOptionSetMask,
int
dwEnabledOptions)
44
{
45
return
0;
46
}
47
48
#endregion
49
}
50
}
[Guid("DCE0B4D4-FA5C-43e4-AE3E-0C881A6DD293")] This is the GUID of the original IObjectSafety interface. Do not change it.
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] sets the COM interface type to Unknown, see InterfaceTypeAttribute Class on MSDN.
This is just a simple implemetation of the IObjectSafety interface that will mark the control as safe. In "real life" there would probably be some sort of logic to determine if the control is safe or not.
6. Create a .msi installer for the control
Before an ActiveX control can be used it must be installed and registered on the client. This can be done in a number of ways, from manually editing the registry to using regasm.exe, but we're going to create a Vistual Studio setup project to handle the installation for us.- Right click the Visual Studio solution, select Add -> New Project and select Setup Project under Other Project Types.
- Call the project 'AxControlsInstaller' and click OK.
- Right click the 'AxControlsInstaller' project, select Add -> Project Output, select 'Primary output' from the 'AxControls' project and click OK.
- Right click 'Primary output from AxControls (Active)' and select Properties.
- Change the Register property from 'vsdrpDoNotRegister' to 'vsdrpCOM'.
- Right click the 'AxControlsInstaller' project and select Build.
7. Package the installer in a .cab file for web deployment
For public web sites we obviously can't deploy our ActiveX control to the client with a Group Policy. In this case we're gonna have to use Internet Explores built-in ability to download and install controls that are packaged in .cab files.- Download the Microsoft Cabinet Software Development Kit.
- Unpack the kit to a local folder and copy Cabarc.exe to the 'AxControlsInstaller' folder.
- Create a new file named 'AxControls.inf' in the 'AxControlsInstaller' folder and add the following content:
01
[version]
02
signature="$CHICAGO$"
03
AdvancedINF=2.0
04
05
[Add.Code]
06
AxControlsInstaller.msi=AxControlsInstaller.msi
07
08
[AxControlsInstaller.msi]
09
file-win32-x86=thiscab
10
clsid={1FC0D50A-4803-4f97-94FB-2F41717F558D}
11
FileVersion=1,0,0,0
12
13
[Setup Hooks]
14
RunSetup=RunSetup
15
16
[RunSetup]
17
run="""msiexec.exe""" /i """%EXTRACT_DIR%\AxControlsInstaller.msi""" /qn
- Click the AxControlsInstaller project and then click the Properties window (View -> Properties Window if it's not visible).
- Click the '...' button next to the PostBuildEvent property and add the following content:
1
"$(ProjectDir)\CABARC.EXE" N "$(ProjectDir)AxControls.cab" "$(ProjectDir)AxControls.inf" "$(ProjectDir)$(Configuration)\AxControlsInstaller.msi"
- Right click the 'AxControlsInstaller' project and select Build.
- There should now be a 'AxControls.cab' file in the 'AxControlsInstaller' folder.
8. Ininitalize and test the control with JavaScript
- Right click the AxControls solution, select Add -> New Project and select 'ASP.Net Web Application' under 'Web'.
- Call the project 'WebAppTest' and click OK.
- Right click the 'WebAppTest' project, select Add -> New Item and select 'HTML Page'.
- Call it 'index.html' and click OK.
- Add the following content to index.html:
01
<
html
>
02
<
head
>
03
04
<
object
name
=
"axHello"
style
=
'display:none'
id
=
'axHello'
classid
=
'CLSID:1FC0D50A-4803-4f97-94FB-2F41717F558D'
codebase
=
'AxControls.cab#version=1,0,0,0'
></
object
>
05
06
<
script
language
=
"javascript"
>
07
08
<!-- Load the ActiveX object -->
09
var x = new ActiveXObject("AxControls.HelloWorld");
10
11
<!-- Display the String in a messagebox -->
12
alert(x.GetText());
13
14
</
script
>
15
</
head
>
16
<
body
>
17
</
body
>
18
</
html
>
- Right click 'index.html' and select 'Set as start page'.
- Right click the 'WebAppTest' project and select 'Set as startup project'.
- Copy 'AxControls.cab' from the 'AxControlsInstaller' folder to the same folder as index.html.
- Uninstall the control from the client by going to Control Panel -> Programs and Features, selecting 'AxControlsInstaller' on the list and clicking Uninstall. This forces Internet Explorer to download and install the .cab file and is an important step in case you've already installed the control.
- Run the application (F5). This will open 'index.html' in Internet Explorer.
- Internet Explorer will display a security warning, asking if you want to install 'AxControls.cab'. Click Install.
- When the page loads it should display a message box with the string you defined in HelloWorld's GetText() method.
No comments:
Post a Comment