COM Tutorial

From Scriptwiki
Revision as of 23:58, 19 July 2015 by Aca20031 (talk | contribs) (Broke code formatting there...)

Jump to: navigation, search

mIRC supports interaction with COM through dynamic binding. COM in mIRC is an advanced topic and usually requires knowledge in strongly typed language. Knowledge of C++ is a big plus.

For this tutorial to make sense, you should at a minimum know

  • How to use Identifiers in mIRC
  • About identifier properties (e.g. $identifier().prop)
  • That the noop command will execute but not print or take an action
  • What objects are in object oriented programming
  • Basics of strongly typed languages (e.g. int vs short vs string)

If you aren't sure you know each of these things, turn around and save yourself the time.

What is COM?

COM stands for Component Object Model and the details of this are quite complicated. We will be skipping over too many specifics, but MSDN has more.

To speak generally, COM is a Windows technology for creating and accessing Objects no matter what language. For example, you could create an object with methods and properties in C++ and use it in mIRC.

Specific Object-Oriented Concepts

mIRC allows us to create an instance of a class using comopen and to call methods, get the value of properties, or set the value of properties using $com and $comcall.

$com and $comcall are basically identical except that $comcall is asynchronous and will call an alias (like a callback function) when the call completes.

Where a language such as Java might an infinite number of types (String, Socket, Integer, InputStream, etc) COM has only a handful - albeit a large handful. In COM, every variable is represented as VARIANT. This is a C structure and it can be used to represent many things. The way this works in C that the "vt" member is set to the type of variable the Variant is to represent, and then the corresponding field is set to the value.

For example, a Variant can represent a byte like this:

VARIANT v; v.vt = VT_BYTE; v.bVal = 4;

Or, it can be changed to a float like this:

v.vt = VT_FLOAT; v.fltVal = 4.0f;

If you are familiar with the common types, you will probably recognize most of them. What you might not recognize is dispatch and unknown and these are very important.

Unknown (IUnknown) is a COM specific construct that is beyond the scope of this tutorial. If you need it, you probably know what it is (and if you do, know that you can call QueryService/Release/AddRef through $com and $comcall - this is only useful though if you eventually get a dispatch back (see below))

Methods

There are 4 things you can do to a COM object via Dispatches

  • Call a method on them
  • Get the value of a property they have
  • Set the value of a property they have
  • Set the value of a property they have to a reference

The third parameter of $com allows you to pick from one of these 4 things by passing it in either 1 (call a method), 2 (get a property), 4 (put a property) or 8 (put a property reference). We'll see an example of this soon.

Dispatch

In COM, a dispatch is how we support complex types. It represents an IDispatch object in C, which is important because it has the Invoke method which lets you call members.

Ok let's back up. You know how Java and C# represent all complex types as "Object" - sort of like a super-class? A String is an Object, an InputStream is an Object, etc? In COM, IDispatch serves a similar function. It represents a COM object what you can call methods or get and set properties on. If you have one, you can pass it as an argument to $com and $comopen.

Still lost on what a Dispatch is? It's OK - move on and use the examples to help gain an understanding.

Programmatic and Class IDs

In order to get started in COM, you need a [ProgID]. A ProgID essentially represents the name of a class that supports COM instantiation via dynamic binding (which is what we are doing here).

In C#, you might do this

var nameOfStack = new System.Collections.Stack()

If Stack was exported as a com object with a prog ID (and it is!) you would do this in mIRC

comopen nameOfStack System.Collections.Stack


Details

You can skip this part - it's just some extra info

Internally, program IDs are mapped to GUIDs in the Registry. For example, check out HKEY_CLASSES_ROOT\System.Collections.Stack\CLSID - this is the Class ID for the Stack class.

If you want to do this same thing in C++, you would use code like the following:

CLSID id;
CLSIDFromProgID(L"System.Collections.Stack", &id);
IDispatch *disp;
CoCreateInstance(id, nullptr, CLSCX_INPROC_SERVER,IID_IDispatch, (LPVOID*)&disp);

Example 1, titleof

Enough theory, let's jump in. Here are some things you need to know

  • Internet Explorer exports it's IWebBrowser2 interface as the progid InternetExplorer.Application
  • It has a method called Navigate which takes a string and goes there
  • Navigate is asynchronous. It will return as soon as the loading has started.
  • It has a property called ReadyState. This is an enum which is set to 4 when it is done.
  • It has a property called LocationName This is a String set to the title of the page.

We're going to make an identifier that takes a URL and returns the title of the page. For example:

$titleof(google.com) == Google
$titleof(facebook.com) == Welcome to Facebook - Log In, Sign Up or Learn More

Disclaimer: Titles may have changed since I wrote this

We're going to do this by instantiating WebBrowser2, navigating to the URL we are passed, waiting until the page is loaded, then returning the location.

alias titleof  {
 if (!$isid) { return }

 ; Instantiate the class
 .comopen titleof InternetExplorer.Application

 ; Call Navigate. Pass it one parameter of type bstr (string) - the URL we were passed. 1 = DISPATCH_METHOD because we are calling a method
 noop $com(titleof, Navigate, 1, bstr, $1-)

 ; $com(<dispatch>).result returns the value the last call returned. Loop until it is 4 (READYSTATE_COMPLETE)
 while ($com(titleof).result != 4) {
   ; Get (2 = DISPATCH_PROPERTYGET) the value of the ReadyState property
   noop $com(titleof, ReadyState, 2)
   ; Now we'll loop again and check the value to see if it was 4 or not.
 }

 ; Get the value of the LocationName property
 noop $com(titleof, LocationName, 2)

; Save it so we can cleanup
 var %value = $com(titleof).result

 ; Quit closes InternetExplorer's background tasks
 noop $com(titleof, Quit, 1)

 ; Close the COM object to tidy up
 .comclose titleof

; Return what we saved
 return %value
}

Example 2: Using the managed stack

Now let's use System.Collections.Stack

This page not yet completed...Sorry!