Jump to content

Creating COM objects without a need of DLL's


ptrex
 Share

Recommended Posts

Anyhow the script was based on a request from someone in the Help and Support, who was wondering if you can create custom COM objects.

And this in order to organize UDF's in a better way, and get the benifit of using METHODS, PROPERTIES and EVENTS.

Well actualy there is an ancient technique that supports this.

For those who are not familiar with the technique it is called :

Windows Script Components

Windows® Script Components provide you with an easy way to create powerful, reusable COM components in script. You create script components using any scripting language that supports the Microsoft® ActiveX® Scripting interfaces. Script languages that support these interfaces include JScript, Microsoft® Visual Basic® Scripting Edition (VBScript), PERLScript, PScript, and Python.

COM Support

This new script component technology supports common types of COM components, such as Automation, and is extensible with add-ons such as DHTML behaviors.

Script Components:

Are small and efficient.

Are easy to create, maintain, and deploy.

Provide the ability to create COM components.

Provide access to a broad range of system services.

Using script components, you can create COM components for a variety of tasks, such as performing middle-tier business logic, accessing and manipulating database data, adding transaction processing to applications, and adding interactive effects to a Web page using DHTML Behaviors.

More information can be found here :

Writing COM Objects with Scripting Languages

Windows Script Components: They Get Around

OK enough talking :

The files *.WSC and *.SCT are VBScript that use XML to create Fully compliant COM objects.

(Search on your PC and propably you will find some .WSC files.)

RightClick the Files and you can select REGISTER / UNREGISTER and GENERATE TYPE LIB is used for the Windows InTelliSence popups.

These Example scripts explain EXPOSING COM Objects TO AU3 :

Save this as a file called DEMO.SCT

<scriptlet>
 
 <Registration
     Description="DemoScriptlet"
     ProgID="Demo.Scriptlet"
     Version="1.00"
     ClassID="{3ac6e5c0-9f18-11d1-83d1-f49604c10000}"
 >
 </Registration>
 
 <implements id=Automation type=Automation>
     <method name=Method1>
        <PARAMETER name=param1/>
     </method>
     <property name="Property1" internalname="mProp1">    
     </property>
 </implements>
 
 &lt;script language=JScript>
 
 var mProp1 = "";
 
 function Method1(param1) {
    return "Method1(param1)";
 }
 
 </script>
 </scriptlet>

Right click the Demo.sct file and select REGISTER. If you don't have a context menu to register it. you will need to do it manually,

using the REGSVR32 command.

; Initialize COM error handler 
$oMyError = ObjEvent("AutoIt.Error","MyErrFunc")


$objWSC = ObjCreate("Demo.Scriptlet")

$MethodWSC_0 = $objWSC.Method1 = 0
$MethodWSC_1 = $objWSC.Method1 = 1

Msgbox(0,"Your Own COM object TEST", $MethodWSC_0)
Msgbox(0,"Your Own COM object TEST", $MethodWSC_1)

Func MyErrFunc()
  $HexNumber=hex($oMyError.number,8)
  Msgbox(0,"COM Test","We intercepted a COM Error !"       & @CRLF  & @CRLF & _
             "err.description is: "    & @TAB & $oMyError.description    & @CRLF & _
             "err.windescription:"     & @TAB & $oMyError.windescription & @CRLF & _
             "err.number is: "         & @TAB & $HexNumber              & @CRLF & _
             "err.lastdllerror is: "   & @TAB & $oMyError.lastdllerror   & @CRLF & _
             "err.scriptline is: "     & @TAB & $oMyError.scriptline     & @CRLF & _
             "err.source is: "         & @TAB & $oMyError.source         & @CRLF & _
             "err.helpfile is: "       & @TAB & $oMyError.helpfile       & @CRLF & _
             "err.helpcontext is: "    & @TAB & $oMyError.helpcontext _
            )
  SetError(1)  ; to check for after this function returns
EndfuncoÝ÷ Ù8b²+d{¦¦W¡×ºÑÊ&¦ÞÞ¶ëJÞ+-­«b¢zk¢oÝ÷ ٻ릦W¬r¸©¶Ç±¦V¢EÏ9"
#9¸ÞrÛ¢Z+ QtÓM,¥©ì·)^oÝ÷ ÚäáDZjjezÈhÂËazÇ+_WjÇ{¦¦W¿¹&¯zØb±ø¥y«*º@St®*mëh«b¢t®÷«ÂÇ*ºZ×hm~àzÛh­è"²×«Ý÷ßz®¢×¥µÊ&¦ÞØ-ÚÞ+-­«b¢zk¢oÝ÷ Ù*"Ýz»hrZ,zØ^r*Üë¬zØb²Ç+m¡Æ¥*.­Ë¬¶8Êç-~º&T®*mI«ÞÖ¬ªê-MºËhæãyËSzËonÊ®¢Ö§v»§×(uåÊ&{8Ô[§rبg¥rãN5)²¡zYCg&u'­¡¸ÖHoÝ÷ Ù:ò¶!jx¶§¶)íz·¬¶éz¸¨u©Ý±ç°«aj^Æ®¶­sc²æFƦR4ôÒW'&÷"æFÆW"¢b33c¶ô×W'&÷"Òö&¤WfVçBgV÷C´WFôBäW'&÷"gV÷C²ÂgV÷C´×W'$gVæ2gV÷C²¢b33c¶ö&¥u42Òö&¤7&VFRgV÷C´S$4ôÕôfææ6Âåu42gV÷C² ¤b4ö&¢b33c¶ö&¥u42FVà¢b33c´ÆöâÒb33c¶ö&¥u42äÆöäÖ÷'F¦FöâÃ2Ã#B²&æ6ÆRÖ÷VçBÂçFW&W7BÂW&ö@ ¤×6t&÷ÂgV÷C´6ÆÆ&6²S$4ôÒgV÷C²ÂgV÷Cµ÷W"Ö÷'F¦FöâW"ÖöçF2¢gV÷C²fײ5"fײb33c¶ö&¥u42äÖ÷'F¦FöâfײgV÷C²b33c²ògV÷C²²&W7VÇB&WGW&æVBg&öÒS$4ôÐ ¤VÇ6R ×6t&÷ÂgV÷C´W'&÷"gV÷C²ÂgV÷C´æòvööBö&¦V7BFV6Æ&VBgV÷C²¤VæD` ¤gVæ2×W'$gVæ2¢b33c´WçVÖ&W#ÖWb33c¶ô×W'&÷"æçVÖ&W"â×6v&÷ÂgV÷C´4ôÒFW7BgV÷C²ÂgV÷CµvRçFW&6WFVB4ôÒW'&÷"b333²gV÷C²fײ5$Äbfײ5$Äbfײð gV÷C¶W'"æFW67&Föâ3¢gV÷C²fײD"fײb33c¶ô×W'&÷"æFW67&Föâfײ5$Äbfײð gV÷C¶W'"çvæFW67&Föã¢gV÷C²fײD"fײb33c¶ô×W'&÷"çvæFW67&Föâfײ5$Äbfײð gV÷C¶W'"æçVÖ&W"3¢gV÷C²fײD"fײb33c´WçVÖ&W"fײ5$Äbfײð gV÷C¶W'"æÆ7FFÆÆW'&÷"3¢gV÷C²fײD"fײb33c¶ô×W'&÷"æÆ7FFÆÆW'&÷"fײ5$Äbfײð gV÷C¶W'"ç67&FÆæR3¢gV÷C²fײD"fײb33c¶ô×W'&÷"ç67&FÆæRfײ5$Äbfײð gV÷C¶W'"ç6÷W&6R3¢gV÷C²fײD"fײb33c¶ô×W'&÷"ç6÷W&6Rfײ5$Äbfײð gV÷C¶W'"æVÇfÆR3¢gV÷C²fײD"fײb33c¶ô×W'&÷"æVÇfÆRfײ5$Äbfײð gV÷C¶W'"æVÇ6öçFWB3¢gV÷C²fײD"fײb33c¶ô×W'&÷"æVÇ6öçFWBð ¢6WDW'&÷"²Fò6V6²f÷"gFW"F2gVæ7Föâ&WGW&ç0¤VæFgVæ

These are the "Swiss Army knife" tools which help you get started.

Windows Script Component Wizard

WSH & WSC Scripts for SciTE

Generating a type library for the specified WSC file

' Generating a type library for the specified WSC file.
 ' The script assumes to be invoked through a context menu 
 ' and to receive the WSC name on the command line.
 ' ---------------------------------------------------------------
 
 ' Get the WSC file name to work with
 If WScript.Arguments.Count = 0 Then
     wscFile = InputBox ("Enter the WSC file name:", "WSC")
 Else
     wscFile = WScript.Arguments.Item(0)
 End If
 if wscFile = "" Then WScript.Quit
 
 
 ' Instantiate the object to create the typelib
 Set oTL = CreateObject("Scriptlet.TypeLib")
 
 
 ' Set source and target file names 
 oTL.AddURL wscFile             
 tlbFile = Replace(wscFile, ".wsc", ".tlb", 1, -1, 1)   
 oTL.Path = tlbFile               
 
 
 ' Set the name to appear in the Object Browser
 posSlash = InStrRev(wscFile, "\")
 posDot = InStrRev(wscFile, ".")
 wscFileOnly = Mid(wscFile, posSlash+1, posDot-posSlash-1)
 oTL.Name = wscFileOnly & "TLB"
 
 ' Get the description
 defDesc = wscFileOnly & " Type Library"
 desc = InputBox ("Enter the TypeLib description", "WSC Description", defDesc)
 If desc <> "" Then
     oTL.Doc = desc 
 Else
     oTL.Doc = defDesc 
 End If
 
 ' Write the TypeLib
 oTL.Write
 oTL.Reset

This give everyone a possibility to create there own COM objects :P

and benifit from all these advantages :

- Create you own custom COM objects.

- Reuse your AU3 UDF's in other COM compliant applications

- No need to translate AU3 UDF's to other script languages

- FULL exposure of all AU3 commands and functions :

- supports methode, properties and events

- supports parameter functions and variables (Tested)

- supports standard UDF's with return values (Tested)

- supports AU3 GUI controls (Tested)

- supports DLLcall UDF's (Tested)

- supports UDF's including other COM objects, cascading COM calls (Tested)

- supports multidirectional COM communication (Tested)

- supports reuse of the custom COM opjects in other COM compliant application (Tested)

- supports write back functionality (Not Tested)

- Easy and on the fly debugging of the custom COM objects, using notepad

- Easy and on the fly extendible custom COM objects, using notepad

- Easy distribution of the COM objects, everything it is text based.

..No need to compile DLL's and distribute.

- No need to have an AutoIT full install, on the destination system.

..Only need to have the "AutoIt3.exe" distributed.

- Windows platform independend (as of Windows 98 and later)

..No need to register an &lt;script language="AutoIT"> namespace on the system, to run it.

- Which makes it more transparant and portable to other systems.

- Ease reuse of NON AU3, scripts as COM objects in AutoIT

..Supporting COM interfaces from JScript, Microsoft® Visual Basic® Scripting Edition (VBScript), PERLScript, PScript, and Python to AU3.

- Easy to generate a TypeLibrary files, from the custom COM objects, for use in COM compliant apps.

..supporting the MS Intellisence technology

- Support for VB CreateObject LATE and EARLY Bindings.

..Provided that you have a type library for the scriptlet, you can employ early binding as well.

- Small footprint perfect marriage for AutoIT

- ...

Enjoy your first custom COM object !!

Happy scripting in 2007

ptrex

Test_Financial.au3

Test_Excel_Object.au3

Test_SQLite_ColumnCount.au3

Test_GUICtrlCreateListView.au3

Edited by ptrex
Link to comment
Share on other sites

Link to comment
Share on other sites

Add an second example of how to create a .WSC file COM object.

I think it would even be possible to capture AutoIT functions as COM objects this way,

The only thing that is needed is that we need to tell the system the there is <script language="AutoIT"> instead of <script language="VBScript">

Can someone tell us where this is defined, so I can run some tests.

when googling I have seen that this technique can capture more types of languages like Jscript, Perl, REXX, Ruby, etc. So why should it not be possible to have .WSC functions as a AU3 container.

Maybe this is a challange for next year. :P

regards

ptrex

Edited by ptrex
Link to comment
Share on other sites

Link to comment
Share on other sites

Yeah looks awesome ptrex!

I think it would even be possible to capture AutoIT functions as COM objects this way,

The only thing that is needed is that we need to tell the system the there is &lt;script language="AutoIT"> instead of &lt;script language="VBScript">

Maybe this is a challange for next year. :P

Someone get on it!!!

Link to comment
Share on other sites

@All

Thanks you all !!

Someone get on it!!!

I know it is possible, I only need to go through some posts to find it.

The guys who made AutoIT WebEnabled managed to do it. I only need to find it.

ADDED COMMENT :

Damn it doesn' seem to that easy to implement an AutoIT Active scripting Engine

this is article explains how to With Active Scripting, your application can easily implement a full macro language like VBScript in the comfort of your own code.

But since it involves C programming I am not in the game anymore. Someone Else maybe.

regards

ptrex

Edited by ptrex
Link to comment
Share on other sites

Hi!

An exemple of usage of active-scripting ?

http://www.autoitscript.com/forum/index.ph...&hl=JScript

I use (often, & from AutoIt) : VBscript, Jscript, Perlscript, Rubyscript, PHPscript, Python. But, I had try, but I cannot use LuaScript (no found recent release), HaskellScript (only old release), TCLscript (no release since ...)

:P

Good new year!

Link to comment
Share on other sites

Add an second example of how to create a .WSC file COM object.

I think it would even be possible to capture AutoIT functions as COM objects this way,

The only thing that is needed is that we need to tell the system the there is &lt;script language="AutoIT"> instead of &lt;script language="VBScript">

I dont think so. Unless the AutoItX dll also serves serves for activescripting

I have a script I wrote a year or so ago that will take any php script and build a wsc file out of it - if there is any interest I will try to figure out exactly what dependencies it has and put it up where it can be used.

... though - I also have something a bit better that I will have useable outside of my stuff in the next week or so.

Its a com dll that gives me two things I really wanted.

1) I can use any activescript language as if its a regular class - no wsc files - functions can be called directly - returns are direct - everything is direct. The source of the script that is compiled in is completely unreadable...(though depending on what interface one allows to be public - obviously one can get the source just from the interface if one wants .. but if its all kept internal - nobody would ever be able to read it)

2) Any program written in any language can communicate with any other program in any language directly - as if they are same program. You can call functions from one program to the other - pass data back and forth - etc - all as if its the same program ... obviously any programs would actually have this compiled into it and any other program trying to access it would have to be allowed.

I have been playing around with AutoIt last day or so getting it to work with the 2nd option - the first option it already would with no adjustments.

So far - and as far as I am going to worry about for now - from an AutoIt script, compiled or not - you can control any other program that you want it to.

By "control" - I do not mean move it around and change its borders - I mean - internal full access - be it a vb program - it will have accesss to every single function in the vb program that is in the same scope (and of course are public)

Edited by Zach
Link to comment
Share on other sites

What a great first programming experiment for 2007! :P

I went looking for a way to interact with each component (property, method, event) AND a surefire way to hide the scripting component itself during distribution.

Here's what I came up with. The year is off to a very good start!

1) Accept a string containing VBScript (or JScript, or Javascript...) and output it to a temporary Windows Scripting Component file. (Works)

1a) The string contains special characters to represent line breaks and tabs, in order to support calls from a context where these would be sorely misinterpreted. (Works)

2) Register that file for use as a COM object. (Works)

3) Interact with the methods of that COM object. (Works)

4) Interact with the properties of that COM object. (Works)

5) Capture events fired by that COM object. (Works! I can hardly believe it actually works!)

#include <array.au3>
   Global $oAU3Error = ObjEvent("AutoIt.Error","OnAutoItError")    ; Initialize COM error handler
   
   Local $pblc = _
       '<method name="Welcome"/>¶' & _
       '<method name="Factorial"/>¶' & _
       '<property name="CallbackData"/>¶' & _
       '<event name="CallbackEvt" dispid="10"/>'
   
   Local $funcs = _
       "Function Welcome()¶" & _     ;"Msgbox ""Hello World""¶" & _
       "CallbackData = ""Hello World""¶" & _
       "fireEvent(""CallbackEvt"")¶" & _
       "End Function" & _
       "¶" & _
       "Function Factorial(n)¶" & _
       "If isNumeric(n) Then¶" & _
       "If n <= 1 Then¶" & _
       "Factorial = 1¶" & _
       "Else¶" & _
       "Factorial = n * Factorial(n-1)¶" & _
       "End If¶" & _
       "Else¶" & _
       "Factorial = -2   ' Error code.¶" & _
       "End If¶" & _
       "End Function"
   
   CreateWSC2("HelloWorld", $pblc, $funcs, "VBScript")
   
   $objWSC=CreateWSCObject("HelloWorld","objWSC")
   ObjEvent($objWSC, "wsc_") ; Interact with event
   $MethodWSC_1 = $objWSC.Welcome ; Interact with method, which fires event
   $MethodWSC_2 = $objWSC.factorial(5) ;
   
   MsgBox(0,"factorial",$MethodWSC_2)   
   
   ;~ =========================================================================================
   ;~ CreateWSC2
   ;~ =========================================================================================
   ;~ The vbscript parameter can use the following metacharacter encoding, for access from non-linebreakable secondary platforms via Execute():
   ;~     ¶ = Paragraph = 0182 --> CRLF
   ;~      = Ellipses = 0133 --> TAB
   ;~     Example:
   ;~         Function Welcome()¶msgbox "Hello World"¶End Function
   ;~     Becomes:
   ;~         Function Welcome()
   ;~             msgbox "Hello World"
   ;~         End Function
   Func CreateWSC2($name, $PublicScript, $InnerScript, $scriptlang="VBScript")
       Local $fn=@TempDir & '\' & $name & '.wsc'
       ConsoleWrite($fn & @CRLF)
   ;~     If FileExists($fn) Then Return
       $PublicScript = StringReplace(StringReplace($PublicScript,"¶",@CRLF),"",@TAB)
       $InnerScript = StringReplace(StringReplace($InnerScript,"¶",@CRLF),"",@TAB)
       Local $filedata='<component>' _
           & @CRLF & '<registration progid="' & $name & '.WSC"/>' _
           & @CRLF & '<public>' _
           & @CRLF & $PublicScript _
           & @CRLF & '</public>' _
           & @CRLF _
           & @CRLF & '<script language="' & $scriptlang & '">' _
           & @CRLF & $InnerScript _
           & @CRLF & '</script>' _
           & @CRLF & '</component>'
       $fn=FileOpen($fn,2)
       FileWrite($fn, $filedata)
       FileClose($fn)
       ShellExecuteWait("regsvr32.exe", '"' & $fn & '" -s')
   EndFunc
   
   ; Creates a WSC-described object
   Func CreateWSCObject($type, $varName="tmp")
       Local $objWSC = ObjCreate($type & ".WSC")
       Assign($varName, $objWSC) ; assign the variable
       Return $objWSC
   EndFunc
   
   Func CleanupAllWSCObjects()
       $oWSCObjectRepository=_ArrayCreate("")
   EndFunc
   
   Func CleanupWSCObject($varName)
       Assign($varName,0)
   EndFunc
   
   Func wsc_CallbackEvt()
       MsgBox(0,"event", $objWSC.CallbackData)
   EndFunc

   Func OnAutoItError()
     ConsoleWrite("We intercepted a COM Error !"       & @CRLF  & @CRLF & _
                "err.description is: "    & @TAB & $oAU3Error.description    & @CRLF & _
                "err.windescription:"     & @TAB & $oAU3Error.windescription & @CRLF & _
                "err.number is: "         & @TAB & Hex($oAU3Error.Number, 8) & @CRLF & _
                "err.lastdllerror is: "   & @TAB & $oAU3Error.lastdllerror   & @CRLF & _
                "err.scriptline is: "     & @TAB & $oAU3Error.scriptline     & @CRLF & _
                "err.source is: "         & @TAB & $oAU3Error.source         & @CRLF & _
                "err.helpfile is: "       & @TAB & $oAU3Error.helpfile       & @CRLF & _
                "err.helpcontext is: "    & @TAB & $oAU3Error.helpcontext _
               )
     Return SetError(1)  ; to check for after this function returns
   EndFunc
Edited by sohfeyr
Link to comment
Share on other sites

@sohfeyr

Nice example !!

Glad to see it all works.

@Zach

Never say Never. Who would have thought that a COM object could be created without compiling an Exe or DLL.

So I am waiting for a creative solution to make AutoIT Scritp Namespace in Windows, without having to create a DLL or EXE.

Maybe some registry hack ?

@Michel Claveau

Thanks as well for your contribution.

As well this is using VBscript as a language. I am waiting for an example that uses AU3 script.

Any hints are welcome.

regards

ptrex

Edited by ptrex
Link to comment
Share on other sites

Never say Never. Who would have thought that a COM object could be created without compiling an Exe or DLL.

So I am waiting for a creative solution to make AutoIT Scritp Namespace in Windows, without having to create a DLL or EXE.

Maybe some registry hack ?

...

I am waiting for an example that uses AU3 script.

Just out of curiousity, what makes you believe this is possible without modifying the Windows Script Host itself? You might be right, I'm just wondering how you would know to think of it?

Any hints are welcome.

The first place I would look at is anything that ties VBScript and Javascript to any of these files in the windows\system32 folder:

scrobj.dll

scrrun.dll

wscript.exe

wshcon.dll

wshom.ocx

Doubtless, there would be a lot of GUIDs involved.

Link to comment
Share on other sites

@sohfeyr

Thanks for the info.

I looked at the Wscript.exe where there is a parameter called /E.

Stands for specifiying the Engine. Maybe we could find a way that the Wscript.exe looks for AutoIT as an engine ?

Just wondering.

This is all for know.

ptrex

Link to comment
Share on other sites

The only thing that is needed is that we need to tell the system the there is &lt;script language="AutoIT"> instead of &lt;script language="VBScript">

You should ask to AutoIT Devs :P

IMHO this may be realy hard to do, as it would imply a patch from Microsoft.

But, if done, think you would be able to use something like &lt;script Language="AutoIT"> in a web page !!

Apzo

(and a happy new year !)

Link to comment
Share on other sites

@Apzo

Thanks for the feedback.

But I don't think a MS Patch is the solution while running other languages like PHP are not supported by MS.

And windows does recognize the <script language="PHP"> notation.

It's only a matter to tell to the Windows system that <script language="AutoIT"> scritping language exists.

Of cource this is easier said than done. :P

But if we all put our heads together, there might be someone around who can tell how or what to look for.

Regards,

ptrex

Link to comment
Share on other sites

I looked at the Wscript.exe where there is a parameter called /E.

Stands for specifiying the Engine. Maybe we could find a way that the Wscript.exe looks for AutoIT as an engine ?

I don't think that is what you are looking for. This is what MS has to say about it.

Edit: The Script Component Wizard may prove interesting. I can't run it on this computer (stupid government policies) but this is what it provides. I have bolded the interesting part:

  • Creates the .wsc file and adds basic XML elements.
  • Prompts for and creates registration information.
  • Prompts for the type of interface handler you want the script component to use.
  • Prompts for properties you want to expose and creates the necessary elements in the scripting language you specify.
  • Prompts for methods you want to expose and creates the necessary elements in the scripting language you specify.
  • Prompts for events that the script component can fire and creates the skeleton event elements.
Edit 2: I really hope I am helping more then I am hurting.... I found this page and it had this interesting quote:

Windows® Script Components provide you with an easy way to create powerful, reusable COM components in script. You create script components using any scripting language that supports the Microsoft® ActiveX® Scripting interfaces. Script languages that support these interfaces include JScript, Microsoft® Visual Basic® Scripting Edition (VBScript), PERLScript, PScript, and Python

As you can see, the interesting part is bolded. AFAIK AutoIt supports ActiveX, right?

Edit 3 - Last edit: Ok, I also found this.

The scripting host reads and passes the specified script file contents to the registered script engine by the IActiveScriptParse::ParseScriptText method provided by the script engine.

Is AutoIt a 'registered script engine'? If so, did it use the 'IActiveScriptParse::ParseScriptText method'? I honestly don't know what that means but this is probably beyond a scripters ability. Edited by Snarg

A little reading goes a long way. Post count means nothing.

Link to comment
Share on other sites

Link to comment
Share on other sites

Without reading anything beyond what's been posted already, I imagine this will require AutoIt implementing one or more COM interfaces. I also suspect they would need to be in-process (DLLs). The typical way for stuff like this to work is you write a DLL implementing the interfaces you need for the features you want. Then you register the DLL appropriately and Windows will take care of the rest. I wouldn't think this would be any different than writing Office, Shell or Visual Studio add-in's in that regard. Theoretically, AutoItX could probably me made to work with little more than implementing the required interfaces.

However, I wouldn't hold your breath waiting on this idea to be implemented.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...