Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Universal COM Callable Wrapper

0.00/5 (No votes)
15 Oct 2010 2  
Allows working with most .NET classes in any COM-aware programming or scripting language

Introduction

This is a generic COM Callable Wrapper for any .NET object, class, or type. It is a language-independent COM class library allowing virtual porting of most Microsoft .NET Framework classes and types into any COM-aware language. On Windows OSs, if your language of choice can at least work with COM, theoretically with this library, you can now work with any Microsoft .NET class, type and object. It was written in VB using a regular text editor, and compilable using Microsoft's VB compiler.

The compiled DLL is meant to be registered as a COM class, making it available in any COM-capable language. Under the hood, the universal functionality is made possible using standard runtime calling and declaration methods. Reflection is used to load assemblies and create wrapped objects and static classes of types declared by you at runtime within the Universal_CCW_Factory class. The standard CallByName() function is used inside the Universal_CCW_Container class to work with a wrapped object's properties and methods. More Reflection methods are used inside the Universal_CCW_Container class to work with a wrapped static type's fields, properties, and to call its methods. For events: an anonymous sub within the Universal_CCW_Container wrapper class acts as a universal Delegate/event handler method. Event handling is nothing new: the anon sub simply copies information about the triggered event to a Queue-type property of the main Universal_CCW_Factory master object.

Regarding events, handling is currently limited to EventHandler and ComponentModel.CancelEventHandler handler types (or any that can comfortably convert down to). This is because I've not yet found a way to force the handler type at runtime. Regarding certain traits of .NET classes that may initiate internal loops, such as the Run method of the Application class: objects and classes exist within a VB.NET trapped environment, meaning you will not be privy to any internal processing going on. An internal loop triggered by any method of the underlying object or class will freeze your project. There is no "main" sub. Therefore, if your project calls for such specialized handling, you should consider making a specialized wrapper.

The form screen-shot below is an example of what can be done using this library under PHP command line scripting. A Windows form is created with textbox and button controls, button click event monitoring, and incorporating a color-picker dialog box to print the decimal value of the chosen color back to the textbox.

form_example.jpg

Note: The code and any implementation of it uses the MIT license. Though fully functional, this code is meant for educational purposes only due to licensing restrictions on any underlying language or implementation and inherent security vulnerabilities. But, I place no restrictions on its use other than continuing the MIT license. See the source code for full copyright notice. Because this library allows unrestricted use of the .NET Framework, it is potentially dangerous, especially when using Internet Explorer while this library is installed. I make no warranties or promises when you use this code. Contact me by email at four.zero.one.unauthorized@gmail.com to report any bugs or learn more about it. Microsoft is not in any way involved in this project, so please do not contact them for support with using this Universal_CCW library. For more details and advanced examples, including handling events in PHP, see the Sourceforge project website.

Background

The majority of .NET classes don't appear to be COM-enabled or COM-registered by default. One might have difficulty working with these classes in any other language. One might be forced to develop a custom COM Class Wrapper for every single .NET class imaginable. Event handling was right out. The common advice on the Internet, including from Microsoft, was "make a wrapper". To me, it seemed a ludicrous idea: make a specialized wrapper for every conceivable case, a daunting task considering the hundreds of .NET classes. Thus, a universal wrapper was needed. For languages not yet privy to the full .NET Framework (such as PHP's inability to convert certain values), or if most .NET classes and assemblies happen to be locked out of a normally COM or .NET compatible language for some odd reason, then this library is for you.

Using this Code

Two new COM-registered classes are made available under a new Universal_CCW namespace:

  • Universal_CCW_Factory: COM accessible factory holding the event queue and used assembly registry, and spawning new container objects
  • Universal_CCW_Container: COM accessible Container object spawned by Universal_CCW_Factory. This acts as the go-between/wrapper object for your language and the .NET object or class, offering get, set, call, and event subscription functions

Start

  1. Register the compiled DLL and resulting TLB file in COM. You can COM-register the new classes using Regasm.exe. If your language is restricted to 32bit platforms, you'll have to compile and register in 32bit.
  2. Use the CreateObject function specific to your language to create a new instance of Universal_CCW.Universal_CCW_Factory.
  3. Knowing the full .NET assembly name and the full class name of the class you'll be working with, wrap it using Universal_CCW_Factory.New_Object("any unique ID/name", "assembly name", "full class name") for objects or Universal_CCW_Factory.New_Static("assembly name", "full class name") for static class. This returns a new Universal_CCW_Container object which acts as the .NET object/class wrapper.

Properties

If you are working with an object, use the following methods of the Universal_CCW_Container wrapper object to interact with the underlying object's properties:

  • Universal_CCW_Container.Get_Property_Value("property name"): Returns the value of the wrapped object's named property. If an object should normally be returned (such as a control), you will instead be given a new Universal_CCW_Container object wrapping this returned object instead.
  • Universal_CCW_Container.Set_Property_Value("property name", "new value"): Sets the wrapped object's property to the given new value. If new value happens to be a Universal_CCW_Container object, its wrapped object will be automatically extracted and assigned as the new value instead.

If you are working with a static class, use the following methods of the Universal_CCW_Container wrapper object to interact with the underlying static class's properties and fields:

  • Universal_CCW_Container.Get_Static_Member_Value("field/property", "property name"): field/property is a string with a value either "field" or "property" to designate what type of member you are looking up (admittedly, this part could use a better method to choose). Returns the value of the wrapped static class's named property or field. If an object should normally be returned (such as a control), you will instead be given a new Universal_CCW_Container object wrapping this returned object instead.

Call Methods

If you are working with an object, use the following methods of the Universal_CCW_Container wrapper object to call its methods:

  • Universal_CCW_Container.Call_Method("method name", "args array"): args array is a 1-dimension array of arguments in order as the underlying method requires. Returns the value of the results of wrapped object's named method. If any of the args array items happens to be a Universal_CCW_Container object, its wrapped object will be automatically extracted and assigned as the new value instead. If an object should normally be returned (such as a control), you will instead be given a new Universal_CCW_Container object wrapping this returned object instead.

If you are working with a static class, use the following methods of the Universal_CCW_Container wrapper object to call its methods:

  • Universal_CCW_Container.Call_Static_Method("method name", "args array"): args array is a 1-dimension array of arguments in order as the underlying method requires. Returns the value of the results of wrapped static class's named method. If any of the args array items happens to be a Universal_CCW_Container object, its wrapped object will be automatically extracted and assigned as the new value instead. If an object should normally be returned (such as a control), you will instead be given a new Universal_CCW_Container object wrapping this returned object instead.

Events

Universal_CCW_Container.Subscribe_To_Event("event name") will cause all events of the type you specify to be queued in the main Universal_CCW_Factory event queue. Under the hood, this method essentially creates a more or less universal delegate and event handler for the event type you specify.

View the Universal_CCW_Factory.Pending_Message_Count property to monitor the queue for the number of pending events. Use the Universal_CCW_Factory.Consume_Message() method to pull the event first in line in the event queue. Note: Consume_Message() returns a Hashtable (source=>"container object ID", event=>event name, args=>EventArgs). Note: EventArgs is an object, so it will be wrapped in a new Universal_CCW_Container object automatically. See above instructions for working with the underlying event object's properties and methods.

Examples

PHP

The following example does nothing more than beep on the host system. For command line usage only. Note: PublicKeyToken and other .NET assembly info may differ from system to system...

<?php

// load the main Universal_CCW_Factory COM class
$COM = new COM("Universal_CCW.Universal_CCW_Factory");

$asmb_full_name = 'Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, ' . 
'PublicKeyToken=b03f5f7f11d50a3a';
$class_name = 'Microsoft.VisualBasic.Interaction';

// create a new static wrapper for the Microsoft.VisualBasic.Interaction class
$vb_static = $COM->New_Static($asmb_full_name, $class_name);

// beep
$vb_static->Call_Static_Method("Beep");

?>

JavaScript 

The following JavaScript example creates a pop-up Windows form on the client's machine when they visit the webpage. Client must have Universal_CCW library and .NET Framework installed. Because it uses the ActiveXObject() function, it will only work in Internet Explorer unless the client has a 3rd party ActiveX plug-in installed (possibly this may work). Note: PublicKeyToken and other .NET assembly info may differ from system to system.

Yes, this gives the website complete control over the client's machine, but let's just brush aside security considerations here for a moment.

<html>
<head>
<script type="text/javascript">

function create_form() {
	// create a new main Universal_CCW_Factory COM object instance
	var main = new ActiveXObject("Universal_CCW.Universal_CCW_Factory");
	
	// create and wrap a new Form object
	var form = main.New_Object("form1", "System.Windows.Forms, Version=2.0.0.0, " +
         "Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Windows.Forms.Form");
	form.Set_Property_Value("Text", "A Blank Form");
	
	// create and wrap a new Textbox object, and set its Parent to the form object
	var textbox = main.New_Object("txt1", "System.Windows.Forms, Version=2.0.0.0, " +
         "Culture=neutral, PublicKeyToken=b77a5c561934e089", 
				"System.Windows.Forms.TextBox");
	textbox.Set_Property_Value("Parent", form);
	textbox.Set_Property_Value("Height", 200);
	textbox.Set_Property_Value("Width", 200);
	textbox.Set_Property_Value("Multiline", true);
	
	// cause the form object to become visible
	form.Call_Method("Show");
    }
        
</script>

</head>

<body onload="create_form();">

test

</body>

Rough results...

javascript_form_example.jpg

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here