Universal_CCW.vb

Author: four.zero.one.unauthorized@gmail.com
Initial Build: 7/29/2010
Rev: 10/16/2010
Status: Beta

EXPERIMENTAL UNTITLED Universal COM Callable Wrapper for most .Net classes.

This is a language-independent COM class library (small DLL file with companion TypeLib file dropped in your system32 folder) allowing virtual porting of most Microsoft .Net framework classes and types into any 32-bit COM-aware scripting or programming language. This library acts as a language-independent API for the entire .Net framework. On Windows OS's, 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. Yes, that's right: you can now port .Net into any of several programming or scripting languages (check the documentation on your language of choice to see if it can create COM objects). Basically, this COM class library eliminates the need for making any .Net projects or default framework classes COM callable yourself in all but special cases.

This library allows you to make Windows forms/GUIs among many other .Net features in many programming and scripting languages, including implementing control events. Using javascript, you can run any .Net code and create controls directly on a website visitor's machine when they also have this library installed. For example: For IE users on a laptop - if you currently have .Net and this library installed, and if you allow "potentially dangerous, unsigned" ActiveX scripting for this page, and if you said 'yes' to the extra security warning, you should see an intrusive and rather bland true Windows form giving you your current battery status.

Before installing, please read the Warnings section. By installing this library, two new COM classes are made available in a new Universal_CCW namespace:


Background

The purpose of this code is purely educational - to show simple dynamic runtime VB methods and to demonstrate one way how to link different platforms, programming and scripting languages. In the programming world, there's usually always an easy solution.

Ultimately for this case, I wanted a WIN API for all languages that I usually work with running on a Windows OS platform. After checking out .Net features, it seemed like close enough. I'd settle for porting .Net classes into any language I use, but I could never get native language features supposedly supporting it to work right.

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 because of this. One might be forced to develop a custom COM Class Wrapper for every single .Net class imaginable. Event handling was right out. 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, or if you don't want to bother with setting up all your .Net projects to be COM callable, then this library is for you.


[top]

About

Conceptually, you create any .Net object or static class indirectly using the new Universal_CCW.Universal_CCW_Factory COM class. Using New_x methods, you spawn a new Universal_CCW_Container object wrapping the .Net object/class. Thus, objects and classes exist within their normal comfort zone of a VB.Net environment while you direct their behavior from some other language using methods of the new Universal_CCW_Container object. Complex objects and types (controls, collections, etc) retain their structure and functionality while interacting indirectly with your language, and you can easily send scalar values (numbers, strings, booleans, etc) back and forth as you'd normally expect.

Under the hood, this is all made possible using dynamic runtime methods:

What this library does not do is allow you to write subs and functions directly in VB.Net code from within your language. This means wrapping the Application class is useless, as its Run() method puts the .Net environment in a loop outside your control or interaction. You will of course have access to the Microsoft.VisualBasic namespace by calling Universal_CCW_Factory.New_Static() method. But if you really want to start writing code in VB.Net, consider switching your preferred language to VB.Net.

Note: Regarding events, handling is currently limited to the following handler types (or any that can comfortably convert down to these):

This is because I've not yet found a way to force the handler type to anything an external call demands at runtime. Also, 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. A loop triggered by any method of the underlying object or class will freeze your project. There is no "main" sub.

Note: The utility of this library is dependant on Microsoft's COM platform, inherently allowing any application, programming or scripting language to use it... Including website scripts through the ActiveXObject() javascript object. This means a website can gain full control over the visitor's system if IE security zone settings are set too low. Because of the power of this library, an extra custom security feature has been added to help protect the user. When using the Universal_CCW_COM_Register.reg reg file to install this library, a new key will be added to the host registry:

The string array value "Disallow" within this key is used as a blacklist for applications that are prohibited from freely using this library. If the calling application is among those listed in this Reg value, a message box will pop up asking the user's confirmation to allow it. By default when using the included Reg file to install this library, only Internet Explorer is blacklisted. This is by no means a foolproof security measure, so use extreme caution when browsing the internet with this library installed.


[top]

Copyright and Support

Copyright terms and support for using this Universal_CCW library: go at it. The code and any implementation of it uses the MIT license. It was written in VB using a regular text editor. Though fully functional, this code is meant for educational purposes only due to licensing restrictions on any underlying language or implementation (if any) and inherent security vulnerabilities. But, I personally place no restrictions on its use other than continuing the MIT license with your implementation. See 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.

.Net framework, Visual Basic, Visual Studio, Windows, are all products of Microsoft. For copyright terms and support for using Microsoft products and services: contact Microsoft directly. I am not responsible for or affiliated in any way with Microsoft or their products and services. You are responsible for getting all the information regarding Microsoft products and services on your own, including licensing of using the .Net framework or other products where required.


[top]

Installation

  1. Download and install .Net ~4 framework directly from Microsoft. For support with this application, please contact Microsoft directly.
  2. Download this project's Universal_CCW installation package.
  3. Extract and copy the following files to your C:\Windows\System32 directory:
    • Universal_CCW.DLL
    • Universal_CCW.TLB
  4. Run the Universal_CCW_COM_Register.reg file to register the new COM classes on your system.

[top]

Compile and Install From Source

Below instructions compiles the Universal_CCW_source.vb source code file directly to a new dll in your system32 folder, and from there creates a TypeLib file and registers the COM classes in your WIN Registry. Universal_CCW_source.vb is included in the install package. Remember, though you may be compiling this on a 64-bit system, instructions and directories below only reference a 32-bit compilation. You should have no trouble using the registered COM class on a 64-bit system though.

  1. Set up your machine with .Net: Download and install .Net ~4 framework directly from Microsoft. For support with this application, please contact Microsoft directly.
  2. Compile Universal_CCW_source.vb file included in the install package:
    1. Extract Universal_CCW_source.vb from the install package zip.
    2. If you wish, make any source code revisions you wish to improve functionality or to make it conform to your project needs.
    3. Open a Command Prompt (Click Start-> Run-> cmd).
    4. Compile the Universal_CCW_source.vb source to a dll file directly in your System32 folder by entering this at the command prompt (Be sure to edit C:\path\to\Universal_CCW_source.vb to be the correct path to the Universal_CCW_source.vb file. Also be sure that the path to vbc.exe points to the 32-bit .Net framework directory, not the 64-bit):
      C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc.exe /vbruntime+ /warnaserror+ /target:library C:\path\to\Universal_CCW_source.vb /out:C:\Windows\System32\Universal_CCW.dll
    5. You now have a new DLL file on your hands located at C:\Windows\System32\Universal_CCW.dll (or in C:\Windows\SysWOW64 depending on how your system behaves at the moment).
  3. Add COM classes to the Windows Registry via the command line:
    1. At the Command Prompt, enter this (Note the path to regasm.exe may differ on your system, but it must point to the .Net 4 32-bit Framework directory, not the 64-bit):
      C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /nologo /verbose /tlb /codebase C:\Windows\System32\Universal_CCW.dll
    2. As well as the two Universal_CCW classes now being registered for COM interaction, you now have a new TLB file on your hands located at C:\Windows\System32\Universal_CCW.tlb (or in C:\Windows\SysWOW64 depending on how your system behaves at the moment).
  4. Run the Universal_CCW_COM_Register.reg reg file to complete COM class registration and set up the application blacklist.
  5. Code Something in your preferred language:
    1. Check your language's documentation on how to create COM objects. For example: in PHP, see the manual page on COM class. For Javascript (IE), review any ActiveXObject() function documentation.
    2. Open a web browser to MSDN .Net Reference site. Congratulations: these are the classes you now have access to in your language of choice.

[top]

Uninstall

Because the library is now registered in Windows Registry and files have been created in the Sys32 folder, use extreme caution when uninstalling as serious system damage may occur on any wrong move. To uninstall this library...

  1. Enter this at the command prompt (Note the path to regasm.exe may differ on your system, but it must point to the .Net 4 32-bit Framework directory, not the 64-bit):
    C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /verbose /unregister "C:\Windows\System32\Universal_CCW.dll"
    This unregisters the typelib.
  2. Delete the following files from your System32 folder (or in C:\Windows\SysWOW64 depending on how your system handled installation):
    1. C:\Windows\System32\Universal_CCW.dll
    2. C:\Windows\System32\Universal_CCW.tlb.
  3. Run the Universal_CCW_COM_Unregister.reg reg file included in the install package to completely unregister the library. Alternatively, using Regedit (Start > Run > Regedit), find and delete all keys located under only HKEY_CLASSES_ROOT containing the string Universal_CCW. Likely any Registry cleaning application should also catch these unused entries at this point as well.

[top]

Using This Code

One new COM-registered class is made available: Universal_CCW_Factory. This is the main COM object holding the event queue and used to spawn new Universal_CCW_Container container objects. Factory creates Universal_CCW_Container objects which acts as wrappers for any .Net object or class you wish. Through methods of the Universal_CCW_Container object, you manipulate the .Net object and set up listening for events triggered by the .Net object. Any errors originating from this library will throw an exception.

Calling Application Blacklist

This library references an application blacklist stored in the Registry. This is useful for preventing applications like Internet Explorer from freely using this library, potentially exposing the user to dangerous javascript. By using the Universal_CCW_COM_Register.reg reg file to register this library, a Registry key is created to store an application blacklist:

This key holds the Registry string array value, Disallow. By default, iexplore.exe is the only listed application. Remove iexplore.exe if you wish your system to potentially be hijacked by any website that is aware of this library. Add additional applications to this value list to add them to the blacklist. While running a blacklisted application, a pop-up warning will alert the user if the application tries to call this library. The user must click yes to permit this action.

Start

  1. Use the CreateObject() function specific to your language to create a new instance of Universal_CCW.Universal_CCW_Factory.
  2. Knowing the full .Net assembly name and the full classname 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/types. This returns a new Universal_CCW_Container object which acts as the .Net object/class wrapper. Note: Wrapping an object will always give you full access to methods and properties of the underlying object's type as well. On the other hand, wrapping a static class and then attempting to treat it like a wrapped object will trigger an exception.

For example calling on the Microsoft.VisualBasic.Interaction .Net class in PHP:

$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);

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:

  1. Universal_CCW_Container.Get_Property_Value("property name" [,optional index]): Returns the value of the wrapped object's named property. Use optional index parameter if the property is a collection of some sort and you just want a single member. 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.
  2. Universal_CCW_Container.Get_Property_TypeName("property name" [,optional index]): Returns the type name of the wrapped object's named property. Use optional index parameter if the property is a collection of some sort and you just want information on a single member.
  3. Universal_CCW_Container.Set_Property_Value("property name", "new value" [,optional "using_method"]): 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. Use optional using_method parameter to specify another "add", "delete", or other method name which the property requires. This defaults to "set".

For example setting the width and height of a WIN form in PHP:

$asmb_full_name = "System.Windows.Forms, Version=2.0.0.0, Culture=neutral, " .
  "PublicKeyToken=b77a5c561934e089";
$form1 = $COM->New_Object($asmb_full_name, "System.Windows.Forms.Form");
$form1->Set_Property_Value("Width", 550);
$form1->Set_Property_Value("Height", 550);

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:

  1. Universal_CCW_Container.Get_Static_Property_Value("property name" [,optional index]): Returns the value of the wrapped static class's named property. Use optional index parameter if the property is a collection of some sort and you just want information on a single member. 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.
  2. Universal_CCW_Container.Get_Static_Property_TypeName("property name" [,optional index]): Returns the type name of the wrapped static class's named property. Use optional index parameter if the property is a collection of some sort and you just want information on a single member.
  3. Universal_CCW_Container.Get_Static_Field_Value("property name"): Returns the value of the wrapped static class's named field (enum, etc). 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.
  4. Universal_CCW_Container.Get_Static_Field_TypeName("property name"): Returns the type name of the wrapped static class's named field (enum, etc).

For example getting the enum value for "High" in BatteryChargeStatus in PHP:

$class_name = "System.Windows.Forms.BatteryChargeStatus";
$battery_state_enum = $COM->New_Static($asmb_full_name, $class_name);
echo $battery_state_enum->Get_Static_Field_Value("High"); // 1

Call Methods

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

  1. Universal_CCW_Container.Call_Method("method name" [, optional 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 objects"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), a new Universal_CCW_Container object wrapping this new object will be returned instead.

For example making the form visible in PHP:

$form1->Call_Method("Show");

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

  1. Universal_CCW_Container.Call_Static_Method("method name" [, optional 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 enqueued 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.

For example adding a button to a form, and listening for and acting on the button"s onclick event in PHP:

$my_button = $COM->New_Object(("btn1", $asmb_full_name, "System.Windows.Forms.Button");
$my_button->Set_Property_Value("Parent", $form1);
$my_button->Subscribe_To_Event("Click");

while(true) {
	// Main app loop
	com_message_pump();
	if ($COM->Pending_Message_Count < 0) {
		$event = $COM->Consume_Message();
		if ($event["event"] === "Click" AND $event["source"] === "btn1") {
			echo "Click!";
			}
		}
	}

[top]

Examples

#1 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");

?>

Rough results: *beep*

#2 PHP

Click the button to pull up a color-picker box. Pick a color to get its decimal value back. For command line usage only. The following example uses a PHP class "Container" to itself contain the contained object (I"m sure there"s a "yo dawg" joke in here somewhere). See PHP_gui_example.php source code for the extensive PHP code example. This PHP class allows you to naturally use the COM object as if there were no intermediate container. For example: you can do $form->Controls->Add($btn1);.

Rough Results...

#3 Javascript

The following Javascript example creates a pop-up Windows form on the client"s machine when they visit the webpage. Client must have this Universal_CCW library and MS's .Net framework installed. Because it uses the ActiveXObject() function, it will only work in IE unless the client has a 3-rd party ActiveX plugin 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. When a client visits this webpage in IE, as a security feature of this library, a popup warning will alert them to the calling of this COM class library. The user must click 'yes' to continue.

<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...

First, the website visitor will see this warning message. They must click 'yes' to permit the website to use this library on their machine:


If the visitor permits it, they will then see something like the following Windows Form pop up on their desktop:




[top]

Warnings

  1. This library may pose a high security risk when browsing the internet. If your browser permits ActiveX objects, any website can essentially take complete control over the machine, either by calling on .Net system classes or by creating deceptive windows. This security consideration is created inherently by Microsoft's COM platform, where any application, including Internet Explorer, can call system components on demand. Because of this security consideration in MS's COM platform, though this library allows for powerful features, I recommend not installing it on any machine which uses Internet Explorer primarily to browse the internet.

    As an security feature/precaution, I've added a registry value which this code checks the calling application against. If the application is listed (iexplore.exe is by default), a warning will pop up asking the user to confirm allowing it. Only if the user allows it will the code work. Though proved effective after a few tests, you should not rely on this simple security feature to protect your system.
  2. This is a work-in-progress. Full testing, security, and enhancement plans haven"t been hashed out yet. Untested on WIN or .Net clones.
  3. Potential Memory Leaks! There appears to be some issue with garbage collection and COM, where tracking of object use is not so cut and dry. Universal_CCW_Container.Destroy() method is made available to help avoid memory leaks as it frees up resources on the library side, including by calling the contained object's Dispose() method (if exists). Then, unset the object via the calling language side.
  4. If used on a server back-end, such as with PHP, this library should be used in command line environments only. Because code could pop up prompts expecting user input (such as full applications), the system will halt waiting for input from the host operating system itself. Though if using true shared applications - such as MS Access, or if using in client-side script, such as Javascript, it should be just fine though.
  5. Not recommended for implementing in any life-support or civil infrastructure systems. This library is the accidental result of a hobby. I make no claims that I am a VB, COM or .Net expert. I also make no guarantees nor accept any responsibility if this code should destroy anyone's system or spiritual beliefs, nor am I responsible for the bringing of unimaginable profit or enlightenment to anyone. I don't exist.

[top]

To Do

  1. Improve parameter validation to prevent unexpected results.
  2. Test on more machines, languages, especially Python, Ruby, etc.
  3. Clean up documentation.
  4. For events, currently only those implementing delegates of type System.EventHandler and System.ComponentModel.CancelEventHandler (or types able to be converted to those two types) are supported. Need to support universal or all delegate types.
  5. Constructors with parameters aren't yet supported. You may instead be able to wrap the static class (using Universal_CCW_Factory.New_Static(), then call one of its constructors through Universal_CCW_Container.Call_Static_Method() to return a new wrapped object.
  6. Likely there's code shortcuts I'm missing.

[top]
© 2010 four.zero.one.unauthorized@gmail.com

TOC

Requirements

References/Credits

Links