Component Object Model (COM)
Indice
- Oggetti COM
- Apertura e chiusura di una connessione COM
- Interazione con l’oggetto
- Members
- Metodo di invocazione
- Tipi di variabile
- Invocare i members
- Esempio di invocazione
- Valore di una proprietà / collezione enumerata
- Recupero dei valori restituiti
- Object dispatching
- Esempio di dispatching - CPU info
- Altre proprietà utili
- Limitazioni del COM
- Esempi
- Trovare i ProgID
Component Object Model (COM) è una specifica che consente alle applicazioni come file exe e dll di rendere la propria funzionalità disponibile a qualunque applicazione conforme a COM. Le applicazioni interagiscono tra loro attraverso una raccolta di funzioni chiamate interfacce. I vantaggi degli oggetti COM sono che sono orientati agli oggetti, personalizzabili, modulari, aggiornabili e indipendenti dal linguaggio. Indipendentemente dal linguaggio con cui sono stati programmati, qualunque altro linguaggio o script che supporti la specifica COM sarà in grado di usarli. mSL offre una varietà di comandi e identificatori per gestire gli oggetti COM.
Oggetti COM
Un oggetto COM è un componente che espone proprietà, metodi ed eventi dell’applicazione. Gli oggetti COM sono identificati da una stringa univoca chiamata Programmatic IDentifier o ProgID.
Alcuni esempi di oggetti COM con i rispettivi ProgID:
- iTunes.Application - un’interfaccia che consente di interagire con l’applicazione iTunes
- WScript.Shell - un’interfaccia che fornisce accesso alla shell nativa di Windows
- Shell.Application - un’interfaccia che fornisce metodi per controllare la shell ed eseguire comandi al suo interno, oltre a metodi per ottenere altri oggetti correlati alla shell
- MSScriptControl.ScriptControl - un’interfaccia che fornisce metodi per valutare ed eseguire script, con supporto a VBScript e JavaScript
- Word.Application - un’interfaccia che fornisce metodi e proprietà associati all’applicazione Word
- WMPlayer.ocx - un’interfaccia che fornisce metodi e proprietà associati al programma Windows Media Player
Nota: un singolo computer può avere migliaia di oggetti COM disponibili.
Apertura e chiusura di una connessione COM
Per aprire una connessione COM verso un oggetto specifico, si usa la seguente sintassi:
/comopen <hName> <progid>
Per chiudere la connessione COM, bisogna usare lo stesso handle usato per crearla.
/comclose <hName>
Prima di continuare a usare l’oggetto COM, dobbiamo confermare che la connessione sia stata
stabilita correttamente. $comerr restituirà 1 se la connessione è fallita, 0 altrimenti.
Esempio di connessione/disconnessione
Qui sotto c’è un semplice esempio di connessione al Windows Script Host e successiva disconnessione.
alias
example {
.comopen shellCOM WScript.Shell
if ($comerr) {
echo $color(info) -aet * Error connection to the Windows Script Host
}
else {
; no errors, continue here
.comclose shellCOM
}
}
Interazione con l’oggetto
Prima di parlare di come interagire con l’oggetto COM a cui ci siamo connessi, dobbiamo chiarire alcuni altri concetti.
Members
I Members di un oggetto sono tutti i metodi e le proprietà forniti da quell’oggetto. In seguito potremo invocare questi members. Per esempio, l’oggetto WScript.Shell possiede un metodo Exec che consente di eseguire un’applicazione in una command shell figlia.
Metodo di invocazione
Il metodo di invocazione è il modo in cui l’invocazione deve essere applicata. Alcuni members dell’oggetto richiedono semplicemente di essere chiamati, altri possono restituire un valore. A volte può essere necessario impostare una proprietà per valore oppure per riferimento.
| Valore | Metodo |
|---|---|
| 1 | Chiama un member di un oggetto |
| 2 | Restituisce un valore |
| 3 | (1+2) Chiama un member e restituisce un valore * |
| 4 | Imposta una proprietà |
| 5 | (1+4) Imposta una proprietà con chiamata a member * |
| 6 | (2+4) Imposta una proprietà e restituisce un valore * |
| 7 | (1+2+4) Imposta una proprietà e restituisce un valore con chiamata a member * |
| 8 | Imposta una proprietà per riferimento |
| 9 | (1+8) Imposta una proprietà per riferimento con chiamata a member * |
| 10 | (2+8) Imposta una proprietà per riferimento e restituisce un valore * |
Questi metodi derivano dalla combinazione dei quattro metodi di base 1, 2, 4 e 8. Per esempio, 5 = 1 + 4, cioè imposta una proprietà con chiamata a member. Possono esistere anche altre combinazioni non incluse nella tabella sopra.
Tipi di variabile
A differenza di mSL, gli oggetti COM richiedono un tipo di dato per ogni valore.
Per esempio, se vogliamo invocare un metodo che riceve un numero intero, dobbiamo usare
il tipo int.
| Tipo | Descrizione | Valori |
|---|---|---|
| i1 | intero signed a un byte | 0 a 225 |
| ui1 | intero unsigned a un byte | 0 a 255 |
| i2 | intero signed a due byte | -32768 a 32767 |
| ui2 | intero unsigned a due byte | 0 a 65535 |
| i4 | intero signed a quattro byte | +/- 2147483647 |
| ui4 | intero unsigned a 32 bit (4 byte) | 0 a 4,294,967,295 |
| int | intero | -2147483648 a 2147483647 |
| uint | intero unsigned | 0 a 4294967295 |
| r4 | numero reale in virgola mobile a 4 byte | 1.17549435E-38 a 3.40282347E+38 |
| r8 | numero reale doppia precisione a 8 byte | 2.2250738585072014E-308 a 1.7976931348623157E+308 |
| cy | numero valuta a otto byte | -922337203685477.5625 a 922337203685477.5625 |
| date | data e ora memorizzate come floating point a 8 byte | +/-79,228,162,514,264,337,593,543,950,335 |
| decimal | valori signed a 128 bit che rappresentano interi a 96 bit | +/-79,228,162,514,264,337,593,543,950,335 |
| bool | può contenere una rappresentazione stringa o numerica | |
| bstr | stringa unicode terminata da null | da 0 a circa 2 miliardi di caratteri Unicode |
| variant | può contenere stringa, data, ora, booleano o numerico; quando usato va seguito da un tipo, ad esempio: variant int 5 | |
| dispatch | puntatore a un’interfaccia IDispatch su un oggetto OLE | |
| unknown | puntatore a un’interfaccia IUnknown su un oggetto OLE | |
| error | codice di errore a 32 bit |
Per creare una variabile per riferimento, aggiungi un asterisco al tipo di dato e un nome variabile,
per esempio: int* pIntOfBeer.
Invocare i members
Per invocare un member di un oggetto COM, usiamo l’identificatore $com con la seguente sintassi:
; member senza argomenti
$com(<hName>, <member>, <Invoke Method>)
; member con un argomento
$com(<hName>, <member>, <Invoke Method>, <type>, <value>)
; member con N argomenti
$com(<hName>, <member>, <Invoke Method>, <type1>, <value1>, ..., <typeN>, <valueN>)
Esempio di invocazione
Per esempio, torniamo all’oggetto WScript.Shell. Supponiamo di voler aprire calc usando il metodo Exec. La sintassi documentata è:
object.Exec(strCommand)
dove object è l’oggetto WshShell e strCommand è la stringa contenente la command line da eseguire.
Da queste informazioni sappiamo che:
- Questa è una chiamata a un member, quindi il metodo di invocazione è 1
- Il metodo Exec accetta 1 argomento
- L’argomento è una stringa, quindi il tipo di dato è
bstr
Possiamo quindi scrivere così l’identificatore $com:
; Exec è il member dell'oggetto che stiamo invocando
; 1 = Call a member of an object
; bstr è il tipo stringa di "calc"
; calc è ciò che stiamo cercando di eseguire
$com(shellCOM, Exec, 1, bstr, calc)
$com restituirà 1 se l’invocazione del member ha avuto successo, 0 altrimenti.
È buona pratica controllare sempre il risultato.
alias
openCalc {
.comopen shellCOM WScript.Shell
if ($comerr) {
echo $color(info) -aet * Error connection to the Windows Script Host
}
else {
var %result = $com(shellCOM, Exec, 1, bstr, calc)
if (!%result) {
echo $color(info) -aet * Error executing the Exec method.
}
.comclose shellCOM
}
}
Valore di una proprietà / collezione enumerata
Recuperare un valore da una collezione enumerata o da una proprietà, come una variabile,
può essere fatto usando l’identificatore $comval con la seguente sintassi:
$comval(<hName>, <N>, <member>)
Se il member restituisce un’enumerazione di valori, puoi attraversarli. Quando n = 0, verrà restituito il numero totale di valori nell’enumerazione.
Recupero dei valori restituiti
Se usiamo il metodo di invocazione 2, stiamo indicando che vogliamo un valore di ritorno.
Per recuperarlo, possiamo usare $com in questo modo:
var %value = $com(<hName>).result
Object dispatching
In molti casi può essere necessario invocare un member su un oggetto annidato dentro un altro oggetto, e talvolta anche dentro un terzo. Spesso l’oggetto padre ha un metodo che, quando chiamato, restituisce un’istanza dell’oggetto figlio che possiamo usare. Questa operazione si chiama dispatching.
Durante il processo, si esegue il dispatch dell’oggetto figlio su una propria connessione COM con un proprio handle. Questo consente di accedere comodamente ai suoi members e, se necessario, ad altri oggetti figli.
Nota: quando si effettua il dispatch di un oggetto, l’oggetto padre rimane comunque aperto. Se non serve più, è consigliabile chiudere la connessione subito dopo il dispatch.
La sintassi generale del dispatching è la seguente:
; apri l'oggetto padre
comopen mainObj some.object
; dispatch dell'oggetto figlio su una propria connessione COM, chiamata childObj
var %result = $com(mainObj, memberName, 3, dispatch* childObj)
; chiudi l'oggetto padre
comclose mainObj
; fai qualcosa con l'oggetto figlio
var %result = $com(childObj, memberName, 1, ...)
; chiudi l'oggetto figlio
comclose childObj
Esempio di dispatching - CPU info
Un buon esempio d’uso del dispatch è il recupero di informazioni sul processore del computer.
Le informazioni del processore si trovano in un’istanza della classe Win32_Processor.
La gerarchia descritta è la seguente:
- Win32_Processor Class
- recuperata usando il metodo
InstancesOf - da SWbemServices Class
- recuperata usando il metodo
ConnectServer - da SWbemLocator Class
- recuperata usando il ProgID
WbemScripting.SWbemLocator
- recuperata usando il metodo
Per arrivare all’oggetto finale dobbiamo quindi effettuare due dispatch.
alias
getProcInfo {
.comopen SWbemLocator WbemScripting.SWbemLocator
if ($comerr) echo $color(info) -aet * Error connection to WbemScripting.SWbemLocator
else {
var %result = $com(SWbemLocator, ConnectServer, 3, dispatch* SWbemServices)
.comclose SWbemLocator
if (!%result) echo $color(info) -aet * Error instantiating SWbemServices object.
else {
var %result = $com(SWbemServices, InstancesOf, 3, string, Win32_Processor, dispatch* Win32_Processor)
.comclose SWbemServices
if (!%result) echo $color(info) -aet * Error retrieving an instance of the Win32_Processor class
else {
echo -a * CPU Info: $comval(Win32_Processor, 1, Name)
.comclose Win32_Processor
}
}
}
}
Questo può essere invocato semplicemente digitando:
/getProcInfo
E restituirà qualcosa del tipo:
* CPU Info: Intel(R) Core(TM) i7 CPU 980x @ 3.33GHz
Altre proprietà utili
L’identificatore $com ha alcune proprietà aggiuntive utili:
| Proprietà | Descrizione |
|---|---|
| argerr | l’argomento che ha causato l’errore |
| error | valore d’errore, quando applicabile |
| errortext | descrizione dell’errore |
| progid | nome dell’oggetto |
| dispatch/unknown | restituisce $true o $null se esiste un puntatore a questo oggetto |
Limitazioni del COM
Attualmente l’implementazione COM di mSL presenta alcune limitazioni che altri linguaggi potrebbero non avere.
COM Passing
Non esiste un modo per passare il risultato grezzo o un oggetto da un’istanza COM a un’altra. Per i risultati che mIRC riesce a interpretare, come stringhe o interi, questo in genere non è un problema, a meno che il risultato non sia eccessivamente lungo, ma per un oggetto restituito attualmente non esiste un modo per passarlo a un’altra connessione COM.
COM Events
mSL non supporta gli eventi che un’istanza COM può emettere. L’implementazione funziona con un modello ask-receive: mIRC richiede dati o un’azione all’istanza COM e l’istanza deve restituire subito un risultato.
Binary Variables
mSL non supporta il passaggio di variabili binarie, bvars, a un’istanza COM né il recupero dei risultati in una bvar. Questo può essere un limite quando i dati da passare o recuperare superano il limite attuale di lunghezza riga di mIRC.
Esempi
$file_get_contents()
ProgID: Msxml2.ServerXMLHTTP.3.0
Members dell’oggetto: XMLHTTPRequest Members
/*
############################################################
# Get source of a file/website
#
# Syntax:
# var %source = $file_get_contents(<website>)
#
# Example:
# //echo -a $file_get_contents(http://www.example.com/)
############################################################
*/
alias
file_get_contents {
if (!$1) {
echo $color(info) -est * Too few parameters: $!file_get_contents
halt
}
; make sure it wasn't left open from last time
if ($com(tcpCom)) .comclose $v1
; connect to the COM object
.comopen tcpCom Msxml2.ServerXMLHTTP.3.0
if ($comerr) echo $color(info) -est $!file_get_contents failed opening com
else {
; initializes the object
; oXMLHttpRequest.open(bstrMethod, bstrUrl, varAsync, bstrUser, bstrPassword);
noop $com(tcpCom, open, 2, bstr, GET, bstr, $1)
; send to the server, get response
noop $com(tcpCom, send, 1)
$com(tcpCom, responseText, 2)
if ($comerr) echo $color(info) -est $!file_get_contents failed opening connection
else {
; get the result of the response, and return it
var %r = $com(tcpCom).result
.comclose tcpCom
return %r
}
}
}
Queue example
ProgID: System.Collections.Queue
Members dell’oggetto: Queue Class Members
/*
#########################################################################
# Queue
#
# Represents a first-in, first-out collection of elements
#
# Syntax:
# /enqueue <queue_name> <value>
# /freeQueue <queue_name>
# var %value = $queue(<queue_name>).dequeue
# var %value = $queue(<queue_name>).peek
# var %count = $queue(<queue_name>).count
#
# Example:
# /enqueue example a
# /enqueue example b
# /enqueue example c
# //while ($queue(example).count) { echo -a $queue(example).dequeue }
# /freequeue example
#########################################################################
*/
alias
enqueue {
if ($0 < 2) {
echo $color(info) -aet /enqueue: insufficient parameters
halt
}
; if there is no connection, open one
if (!$com($1)) .comopen $1 System.Collections.Queue
; enqueue
noop $com($1, enqueue, 1, bstr, $2-)
if ($comerr) {
echo $color(info) -aet /enqueue: queue error
halt
}
}
alias
queue {
; check if its an ident, com exists, and the proprty is either dequeue/peek/count
if (($isid) && ($istok(dequeue peek count, $prop, 32)) && ($com($1))) {
; call the peek() or dequeue() method, get the return value
noop $com($1, $prop, 2)
; get the return value
return $com($1).result
}
}
alias
freeQueue {
; close the com connection, if exists
if ($com($1)) .comclose $1
}
Sendkeys example
ProgID: WScript.Shell
Members dell’oggetto: WshShellObject Properties and Methods
; sends "Hello there!" to the editbox and presses enter
alias
sendkeys {
; open a com connection
.comopen x WScript.Shell
; execute the sendkeys() method and close the com connection
.comclose x
$com(x, sendkeys, 1, bstr, Hello there!{ENTER})
}
RAM information
ProgID: WbemScripting.SWbemLocator
Members dell’oggetto: Win32_PhysicalMemory Class
/*
####################################
# RAM Info
#
# Gets some interesting RAM info
#
# Syntax:
# /getRamSpecs
####################################
*/
alias
getRamSpecs {
; open COM connection
.comopen SWbemLocator WbemScripting.SWbemLocator
if ($comerr) echo $color(info) -aet * Error connection to WbemScripting.SWbemLocator
else {
; dispatch and get an instance of the SWbemServices class
var %result = $com(SWbemLocator, ConnectServer, 3, dispatch* SWbemServices)
.comclose SWbemLocator
if (!%result) echo $color(info) -aet * Error instantiating SWbemServices object.
else {
; dispatch and get an instance of the Win32_PhysicalMemory class
var %result = $com(SWbemServices, InstancesOf, 3, string, Win32_PhysicalMemory, dispatch* Win32_PhysicalMemory)
.comclose SWbemServices
if (!%result) echo $color(info) -aet * Error retrieving an instance of the Win32_PhysicalMemory class
else {
; 0 returns the total number of elements
var %memSticks = $comval(Win32_PhysicalMemory, 0, Caption)
echo -a * RAM: %memSticks Sticks
while (%memSticks) {
var %size = $bytes($comval(Win32_PhysicalMemory, $v1, Capacity)).suf
var %location = $comval(Win32_PhysicalMemory, $v1, DeviceLocator)
var %speed = $comval(Win32_PhysicalMemory, $v1, Speed)
var %type = $getType($comval(Win32_PhysicalMemory, $v1, MemoryType))
echo -a * Stick $v1 $+ : Size: %size Location: %location Speed: %speed Type: %type
dec %memSticks
}
.comclose Win32_PhysicalMemory
}
}
}
}
alias -l
getType {
var %types = Unknown,Other,DRAM,Synchronous DRAM,Cache DRAM,EDO,EDRAM, $&
VRAM,SRAM,RAM,ROM,Flash,EEPROM,FEPROM,EPROM,CDRAM,3DRAM,SDRAM,SGRAM, $&
RDRAM,DDR,DDR-2
return $gettok(%types, $calc($1 + 1), 44)
}
Trovare i ProgID
La domanda finale più comune è: come faccio a trovare altri ProgID. La risposta è che serve un po’ di ricerca. Sono disponibili in molti punti del sistema e della documentazione. Due strumenti utili indicati nel testo sono:
- OLE-COM Object Viewer - strumento Microsoft per localizzare oggetti COM
- ActiveXHelper - piccola utility che consente di visualizzare informazioni sui componenti ActiveX
Punti di interesse
Le classi Win32 fanno parte delle Windows Management Instrumentation Classes, WMI Classes, e consentono di accedere al monitoraggio e alla gestione dell’hardware e delle funzionalità di sistema.
- Application Objects
- Word.Application
- Excel.Application
- PowerPoint.Application
- Access.Application
- InternetExplorer.Application
- FrontPage.Application*
- Outlook.Application
- Photoshop.Application
- Photoshop.Application.7
- ShockwaveFlash.ShockwaveFlash
- iTunes.Application
- Scripting Objects
- XML Document Objects
- MSXML.DOMDocument
- Msxml2.DOMDocument
- Msxml2.DOMDocument.3.0
- Msxml2.DOMDocument.4.0
- Msxml2.DOMDocument.6.0
- Microsoft.XMLDOM
- MSXML2.XMLHTTP
- MSXML2.XMLHTTP.3.0
- Microsoft.XMLHTTP
- MSXML2.DSOControl
- MSXML2.DSOControl.3.0
- MSXML.DOMDocument
- ADO Database Objects
Questo elenco non è affatto esaustivo, ma solo una raccolta di alcuni degli oggetti più comuni. * Prodotto non più supportato.