Jump to content

WindowFromAccessibleObject


trancexx
 Share

Recommended Posts

So, here's the thing. You create ActiveX control. To have full functionality handle to that control is required.

I'm not very good with this stuff, so I don't know if it's been done before. If it is... aahh well.

I find it to be very useful and actually kind of fundamental. Function WindowFromAccessibleObject from oleacc.dll is used to do the job.

What's really interesting is that I'm building the code on undocumented AutoIt's feature. On "idispatch" type. Very interesting thing and I must say it offers some new (to me) possibilities.

I have made some other successful calls past few days with it and looks kind of cool now.

Anyway, this is the function:

;#FUNCTION# ;===============================================================================
;
; Name...........: _GetObjectWinHandle
; Description ...: Retrieves the window handle of/for the specified object
; Syntax.........: _GetObjectWinHandle($oObject)
; Parameters ....: $oObject - object
; Return values .: Success - Returns window handle of the object
;                  Failure - Returns 0 sets @error:
;                  |1 - WindowFromAccessibleObject function or call to it failed
; Remarks........: Function uses undocumented AutoIt's type "idispatch".
; Author ........: trancexx
;
;==========================================================================================

Func _GetObjectWinHandle($oObject)

    Local $aCall = DllCall("oleacc.dll", "int", "WindowFromAccessibleObject", _
            "idispatch", $oObject, _
            "hwnd*", 0)

    If @error Or $aCall[0] Then
        Return SetError(1, 0, 0)
    EndIf

    Return $aCall[2]

EndFunc   ;==>_GetObjectWinHandle 
;

And this could be an example of usage:

#include <WinAPI.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

Opt("WinWaitDelay", 0)
Opt("GUIOnEventMode", 1)

; Make a GUI
Global $hGui = GUICreate("ActiveX", 800, 500)

; Create object
Global $oIEObject = ObjCreate("Shell.Explorer.2")

; Check if it's created
If Not IsObj($oIEObject) Then
    MsgBox(262144 + 48, "Error!", "Could not create object. That's bad!")
    Exit
EndIf

; Create ActiveY control
Global $hActiveX = GUICtrlCreateObj($oIEObject, 0, 0, 800, 400)
; Hide it for now
GUICtrlSetState($hActiveX, $GUI_HIDE)

; Some page
Global $sURL = "www.yahoo.com"

; Navigate to that page
$oIEObject.navigate($sURL)

; Exit function
GUISetOnEvent($GUI_EVENT_CLOSE, "_Quit")

; Wait cursor
GUISetCursor(15)

; Show GUI
GUISetState()

; Get that handle
Global $hObjWinHandle = _GetObjectWinHandle($oIEObject)
; Write it
ConsoleWrite("! $oIEObject win handle: " & $hObjWinHandle & @CRLF)

; Change style (got to do something with the handle now that I have it)
Global Const $GWL_STYLE = -16
_WinAPI_SetWindowLong($hObjWinHandle, $GWL_STYLE, BitOR($WS_OVERLAPPEDWINDOW, $WS_CHILD))
; Change title too
WinSetTitle($hObjWinHandle, 0, "My embedded browser")

; Show it
GUICtrlSetState($hActiveX, $GUI_SHOW)

; Needed to check state
Global $iNotLoaded = 1

; Will get another handle, declare variable for it
Global $hObjDocWinHandle ; for other handle

; Loop till end
While 1

    If $iNotLoaded Then ; some strange way of checking if loaded
        If IsObj($oIEObject.document) Then
            $hObjDocWinHandle = _GetObjectWinHandle($oIEObject.document)
            ConsoleWrite("! $oIEObject.document win handle: " & $hObjDocWinHandle & @CRLF) ; this is what you normally get with AutoIt Window Info tool
            GUISetCursor(-1)
            $iNotLoaded = 0
        EndIf
    EndIf

    Sleep(100)

WEnd


; FUNCTIONS:

Func _GetObjectWinHandle($oObject)

    Local $aCall = DllCall("oleacc.dll", "int", "WindowFromAccessibleObject", _
            "idispatch", $oObject, _
            "hwnd*", 0)

    If @error Or $aCall[0] Then
        Return SetError(1, 0, 0)
    EndIf

    Return $aCall[2]

EndFunc   ;==>_GetObjectWinHandle


Func _Quit()
    Exit
EndFunc   ;==>_Quit
; End

I lack Obj() knowledge so the example is rather plain, maybe even pointless. Nevertheless I see great potential of this function. That's why I fail to see why isn't there something like it already.

But I guess there is always a possibility that I overlooked it.

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

..and the opposite:

; http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/6c926f83-3eca-47ef-882f-c7f8816b9000/
; Most of this is from there so I'd like to thank Asmdev for his/her straightforward code snippet.

#include <IE.au3>
#include <StructureConstants.au3>

; Very important. If a function is called without a dll reference it'll get freed
; so the objects methods will not be accessible. At least in the case of oleacc.dll.
Global $hdll = DllOpen('oleacc.dll')

Global Const $tagVARIANT = 'int[4];'

Global Const $OBJID_CLIENT = 0xFFFFFFFC
Global Const $ROLE_SYSTEM_PAGETABLIST = 60
Global Const $CHILDID_SELF = 0
Global Const $VT_DISPATCH = 9
Global Const $VT_I4 = 3
Global Const $navOpenInNewTab = 2048

Global $tVAR = DllStructCreate($tagVARIANT)
DllStructSetData($tVAR, 1, $VT_I4, 1)
DllStructSetData($tVAR, 1, $CHILDID_SELF, 2)

Global $tIID_IAccessible = DllStructCreate($tagGUID)
    DllStructSetData($tIID_IAccessible, 1, 0x618736e0)
    DllStructSetData($tIID_IAccessible, 2, 0x3c3d)
    DllStructSetData($tIID_IAccessible, 3, 0x11cf)
    DllStructSetData($tIID_IAccessible, 4, '0x810c00aa00389b71')
    

Global $oIE = _IECreate('http://www.google.com')
Global $hIE = _IEPropertyGet($oIE, 'hwnd')

; This is the control handle we will *interface* with.
Global $hTab = ControlGetHandle($hIE, '', '[CLASS:DirectUIHWND]')

; Open in new tabs.
$oIE.Navigate('http://www.yahoo.com', $navOpenInNewTab)
$oIE.Navigate('http://www.tenouk.com', $navOpenInNewTab)

; Give it a little time to load
Sleep(5000)


; Get accessible object interface from a window handle
Global $pResult = _AccessibleObjectFromWindow($hTab, $OBJID_CLIENT, $tIID_IAccessible, $hdll)

; Get number of contained children.
Global $iChildren = _get_accChildCount($pResult)
; Retrieve IDispatch interface address or child ID of children.
Global $tTabs = _AccessibleChildren($pResult, 0, $iChildren, $hdll)

; Enumerating the child objects and proceeding only if the current child is of PageControl object.
For $i = 1 To $iChildren
    
    ; If VARIANT.pdispVal has information then proceed.
    ; if (vTabs[i].vt == VT_DISPATCH)
    If BitAND(DllStructGetData($tTabs, $i, 1), 0xFFFF) = $VT_DISPATCH Then
        
        ; Get an object interface.
        Local $pObjTabs = _QueryInterface(DllStructGetData($tTabs, $i, 3), $tIID_IAccessible)
        ; Retrieve information that describes the role of the object.
        Local $tResTab = _get_accRole($pObjTabs, $tVAR)
        
        ; If it's role is to tab-list then proceed
        ; if (vTab.lVal == $ROLE_SYSTEM_PAGETABLIST)
        If DllStructGetData($tResTab, 1, 3) = $ROLE_SYSTEM_PAGETABLIST Then
            ; So it's a tab-list control which in part is a container.
            ; Get child count.
            $iChildren = _get_accChildCount($pObjTabs)
            
            ; Retrieve IDispatch interface address or child ID of children.
            $tTab = _AccessibleChildren($pObjTabs, 0, $iChildren, $hdll)
            
            For $j = 1 To $iChildren
                ; Get an object interface.
                Local $pAccTab = _QueryInterface(DllStructGetData($tTab, $j, 3), $tIID_IAccessible)
                ; Get an object name.
                ; Much like getting an internet explorer object's current title property.
                ConsoleWrite(_get_accName($pAccTab, $tVAR) & @LF)
            Next
        EndIf
    EndIf
Next

DllClose($hdll)
Exit
; ########################################################################################################

Func _AccessibleObjectFromWindow($hWnd, $iObjId, $tRefIID, $hOleacc = 'oleacc.dll')
    Local $aResult
    
    $aResult = DllCall($hOleacc, 'int', 'AccessibleObjectFromWindow', 'hwnd', $hWnd, 'uint', $iObjId, 'ptr', DllStructGetPtr($tRefIID), 'int*', 0)
    
    If @error Then Return SetError(@error, @extended, 0)
    Return SetError($aResult[0], 0, $aResult[4])
EndFunc
; ########################################################################################################

Func _AccessibleChildren($paccContainer, $iChildStart, $iChildren, $hOleacc = 'oleacc.dll')
    Local $tVARIANT, $pVARIANT
    Local $sStruct = ''
    Local $aResult

    For $i = 1 To $iChildren
        $sStruct &= $tagVARIANT
    Next

    $tVARIANT = DllStructCreate($sStruct)
    $pVARIANT = DllStructGetPtr($tVARIANT)
    
    $aResult = DllCall($hOleacc, 'int', 'AccessibleChildren', 'ptr', $paccContainer, 'int', $iChildStart, 'int', $iChildren, 'ptr', $pVARIANT, 'int*', 0)
    If @error Then Return SetError(@error, @extended, 0)
    Return SetError(0, $aResult[5], $tVARIANT)
EndFunc
; ########################################################################################################

Func _get_accChildCount($pObj)
    Local $tCall, $pCall, $xCall
    Local $tBuff
    
    $tBuff = DllStructCreate('int')
    
    $xCall  = '0x8D05' & SwapEndian(DllStructGetPtr($tBuff)) & _                ; lea eax, dword ptr [tBuff]
              '50' & _                                                          ; push eax
              'B8' & SwapEndian($pObj) & _                                      ; mov eax, pObj
              '50' & _                                                          ; push eax
              'A1' & SwapEndian($pObj) & _                                      ; mov eax, dword ptr [pObj]
              'FF5020' & _                                                      ; call dword ptr [eax + 20]
              'C3'                                                              ; ret
              
    $tCall = DllStructCreate('byte[' & BinaryLen($xCall) & ']')
    $pCall = DllStructGetPtr($tCall)
    DllStructSetData($tCall, 1, $xCall)

    DllCall("user32.dll", "none", "CallWindowProc", "ptr", $pCall, "int", 0, "int", 0, "int", 0, "int", 0)

    Return DllStructGetData($tBuff, 1)
EndFunc
; ########################################################################################################

Func _get_accRole($pObj, $tQueryVAR)
    Local $tCall, $pCall, $xCall
    Local $tVarRole
    
    $tVarRole = DllStructCreate('int[4]')
    
    $xCall  = '0x8D05' & SwapEndian(DllStructGetPtr($tVarRole)) & _             ; lea eax, dword ptr [tVarRole]
              '50' & _                                                          ; push eax
              'A1' & SwapEndian(DllStructGetPtr($tQueryVAR, 1)+12) & _          ; mov eax, dword ptr [tQueryVAR + C]
              '50' & _                                                          ; push eax
              'A1' & SwapEndian(DllStructGetPtr($tQueryVAR, 1)+8) & _           ; mov eax, dword ptr [tQueryVAR + 8]
              '50' & _                                                          ; push eax
              'A1' & SwapEndian(DllStructGetPtr($tQueryVAR, 1)+4) & _           ; mov eax, dword ptr [tQueryVAR + 4]
              '50' & _                                                          ; push eax
              'A1' & SwapEndian(DllStructGetPtr($tQueryVAR, 1)) & _             ; mov eax, dword ptr [tQueryVAR]
              '50' & _                                                          ; push eax
              'B8' & SwapEndian($pObj) & _                                      ; mov eax, pObj
              '50' & _                                                          ; push eax
              'A1' & SwapEndian($pObj) & _                                      ; mov eax, dword ptr [pObj]
              'FF5034' & _                                                      ; call dword ptr [eax + 34]
              'C3'                                                              ; ret
              
    $tCall = DllStructCreate('byte[' & BinaryLen($xCall) & ']')
    $pCall = DllStructGetPtr($tCall)
    DllStructSetData($tCall, 1, $xCall)

    DllCall("user32.dll", "none", "CallWindowProc", "ptr", $pCall, "int", 0, "int", 0, "int", 0, "int", 0)

    Return $tVarRole
EndFunc
; ########################################################################################################

Func _get_accName($pObj, $tQueryVAR)
    Local $tCall, $pCall, $xCall
    Local $tBSTR, $tBuffer
    
    $tBSTR = DllStructCreate('int')
    
    $xCall  = '0x8D05' & SwapEndian(DllStructGetPtr($tBSTR)) & _                ; lea eax, dword ptr [tBSTR]
              '50' & _                                                          ; push eax
              'A1' & SwapEndian(DllStructGetPtr($tQueryVAR, 1)+12) & _          ; mov eax, dword ptr [tQueryVAR + C]
              '50' & _                                                          ; push eax
              'A1' & SwapEndian(DllStructGetPtr($tQueryVAR, 1)+8) & _           ; mov eax, dword ptr [tQueryVAR + 8]
              '50' & _                                                          ; push eax
              'A1' & SwapEndian(DllStructGetPtr($tQueryVAR, 1)+4) & _           ; mov eax, dword ptr [tQueryVAR + 4]
              '50' & _                                                          ; push eax
              'A1' & SwapEndian(DllStructGetPtr($tQueryVAR, 1)) & _             ; mov eax, dword ptr [tQueryVar]
              '50' & _                                                          ; push eax
              'B8' & SwapEndian($pObj) & _                                      ; mov eax, pObj
              '50' & _                                                          ; push eax
              'A1' & SwapEndian($pObj) & _                                      ; mov eax, dword ptr [pObj]
              'FF5028' & _                                                      ; call dword ptr [eax + 28]
              'C3'                                                              ; ret
              
    $tCall = DllStructCreate('byte[' & BinaryLen($xCall) & ']')
    $pCall = DllStructGetPtr($tCall)
    DllStructSetData($tCall, 1, $xCall)

    DllCall("user32.dll", "none", "CallWindowProc", "ptr", $pCall, "int", 0, "int", 0, "int", 0, "int", 0)

    $tBuffer = DllStructCreate('wchar[256]', DllStructGetData($tBSTR, 1))
    Return DllStructGetData($tBuffer, 1)
EndFunc
; ########################################################################################################

Func _QueryInterface($pObj, $tRefIID)
    Local $tCall, $pCall, $xCall
    Local $tBuff
    
    $tBuff = DllStructCreate('int')
    
    $xCall  = '0x8D05' & SwapEndian(DllStructGetPtr($tBuff)) & _                ; lea eax, dword ptr [tBuff]
              '50' & _                                                          ; push eax
              '68' & SwapEndian(DllStructGetPtr($tRefIID)) & _                  ; push [tRefIID]
              'B8' & SwapEndian($pObj) & _                                      ; mov eax, pObj
              '50' & _                                                          ; push eax
              'A1' & SwapEndian($pObj) & _                                      ; mov eax, dword ptr [pObj]
              'FF10' & _                                                        ; call dword ptr [eax]
              'C3'                                                              ; ret
              
    $tCall = DllStructCreate('byte[' & BinaryLen($xCall) & ']')
    $pCall = DllStructGetPtr($tCall)
    DllStructSetData($tCall, 1, $xCall)

    DllCall("user32.dll", "none", "CallWindowProc", "ptr", $pCall, "int", 0, "int", 0, "int", 0, "int", 0)

    Return DllStructGetData($tBuff, 1)
EndFunc
; ########################################################################################################

Func SwapEndian($hex)
    Return StringFormat('%.8s', Hex(Binary($hex)))
    ;Return Hex(Binary($hex))
EndFunc   ;==>SwapEndian
; ########################################################################################################
Link to comment
Share on other sites

That's massive!

... I said that I did some other calls with "idispatch" , check this:

#include <WinAPI.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>


Opt("WinWaitDelay", 0)
Opt("GUIOnEventMode", 1)
Opt("MustDeclareVars", 1)

Global $oMyError = ObjEvent("AutoIt.Error", "_ErrFunc")


Global $hOLEACC = DllOpen("oleacc.dll")

; Make a GUI
Global $hGui = GUICreate("ActiveX", 800, 500)

; Create object
Global $oIEObject = ObjCreate("Shell.Explorer.2")

; Check if it's created
If Not IsObj($oIEObject) Then
    MsgBox(262144 + 48, "Error!", "Could not create object. That's bad!")
    Exit
EndIf

; Create ActiveY control
Global $hActiveX = GUICtrlCreateObj($oIEObject, 0, 0, 800, 400)
; Hide it for now
GUICtrlSetState($hActiveX, $GUI_HIDE)

; Some page
Global $sURL = "www.yahoo.com"

; Navigate to that page
$oIEObject.navigate($sURL)

; Exit function
GUISetOnEvent($GUI_EVENT_CLOSE, "_Quit")

; Wait cursor
GUISetCursor(15)

; Show GUI
GUISetState()

; Get that handle
Global $hObjWinHandle = _GetObjectWinHandle($oIEObject)
; Write it
ConsoleWrite("! $oIEObject win handle: " & $hObjWinHandle & @CRLF)

; New function
Global $oAccessibleObject = _AccessibleObjectFromWindow($hObjWinHandle)

; Gained:
If IsObj($oAccessibleObject) Then

    ; ConsoleWrite("accChild: " & $oAccessibleObject.accChild($Param) & @CRLF) ;<-
    ConsoleWrite("accChildCount: " & $oAccessibleObject.accChildCount & @CRLF)
    ConsoleWrite("accDefaultAction: " & $oAccessibleObject.accDefaultAction & @CRLF)
    ConsoleWrite("accDescription: " & $oAccessibleObject.accDescription & @CRLF)
    ConsoleWrite("accFocus: " & $oAccessibleObject.accFocus & @CRLF)
    ConsoleWrite("accHelp: " & $oAccessibleObject.accHelp & @CRLF)
    ; ConsoleWrite("accHelpTopic: " & $oAccessibleObject.accHelpTopic($Param) & @CRLF) ;<-
    ConsoleWrite("accKeyboardShortcut: " & $oAccessibleObject.accKeyboardShortcut & @CRLF)
    ConsoleWrite("accName: " & $oAccessibleObject.accName & @CRLF)
    ConsoleWrite("accParent: " & $oAccessibleObject.accParent & @CRLF)
    ConsoleWrite("accRole: " & $oAccessibleObject.accRole & @CRLF)
    ConsoleWrite("accSelection: " & $oAccessibleObject.accSelection & @CRLF)
    ConsoleWrite("accState: " & $oAccessibleObject.accState & @CRLF)
    ConsoleWrite("accValue: " & $oAccessibleObject.accValue & @CRLF)

EndIf

ConsoleWrite(@CRLF)

#cs
; Change style (got to do something with the handle now that I have it)
Global Const $GWL_STYLE = -16
_WinAPI_SetWindowLong($hObjWinHandle, $GWL_STYLE, BitOR($WS_OVERLAPPEDWINDOW, $WS_CHILD))
#ce

; Change title
WinSetTitle($hObjWinHandle, 0, "My embedded browser")

; Show it
GUICtrlSetState($hActiveX, $GUI_SHOW)

; Needed to check state
Global $iNotLoaded = 1

; Will get another handle, declare variable for it
Global $hObjDocWinHandle ; for other handle

; New objects
Global $oIEObjectDocument
Global $oAccessibleObjectDoc

; For moving (nothing smart)
Global $iX
Global $iUp

; Loop till end
While 1

    If $iNotLoaded Then

        $oIEObjectDocument = $oIEObject.document

        If IsObj($oIEObjectDocument) Then

            ;Wait till fully loaded
            While 1
                If $oIEObjectdocument.readyState = "complete" Or $oIEObjectdocument.readyState = 4 Then ExitLoop
                Sleep(100)
            WEnd

            $hObjDocWinHandle = _GetObjectWinHandle($oIEObjectDocument)

            ConsoleWrite("! $oIEObject.document win handle: " & $hObjDocWinHandle & @CRLF) ; this is what you normally get with AutoIt Window Info tool

            $oAccessibleObjectDoc = _AccessibleObjectFromWindow($hObjDocWinHandle)

            If IsObj($oAccessibleObjectDoc) Then

                ; ConsoleWrite("accChild: " & $oAccessibleObject.accChild($Param) & @CRLF) ;<- limitation
                ConsoleWrite("accChildCount: " & $oAccessibleObjectDoc.accChildCount & @CRLF)
                ConsoleWrite("accDefaultAction: " & $oAccessibleObjectDoc.accDefaultAction & @CRLF)
                ConsoleWrite("accDescription: " & $oAccessibleObjectDoc.accDescription & @CRLF)
                ConsoleWrite("accFocus: " & $oAccessibleObjectDoc.accFocus & @CRLF)
                ConsoleWrite("accHelp: " & $oAccessibleObjectDoc.accHelp & @CRLF)
                ; ConsoleWrite("accHelpTopic: " & $oAccessibleObjectDoc.accHelpTopic($Param) & @CRLF) ;<- limitation
                ConsoleWrite("accKeyboardShortcut: " & $oAccessibleObjectDoc.accKeyboardShortcut & @CRLF)
                ConsoleWrite("accName: " & $oAccessibleObjectDoc.accName & @CRLF)
                ConsoleWrite("accParent: " & $oAccessibleObjectDoc.accParent & @CRLF)
                ConsoleWrite("accRole: " & $oAccessibleObjectDoc.accRole & @CRLF)
                ConsoleWrite("accSelection: " & $oAccessibleObjectDoc.accSelection & @CRLF)
                ConsoleWrite("accState: " & $oAccessibleObjectDoc.accState & @CRLF)
                ConsoleWrite("accValue: " & $oAccessibleObjectDoc.accValue & @CRLF)

            EndIf

            ; Restore cursot
            GUISetCursor(-1)
            $iNotLoaded = 0

        EndIf

    Else

        WinMove($hObjWinHandle, 0, $iX, 0)

        If $iX > 100 Then
            $iUp = -1
        ElseIf $iX <= 0 Then
            $iUp = 1
        EndIf

        $iX += $iUp

    EndIf

    Sleep(30)

WEnd


; FUNCTIONS:

Func _GetObjectWinHandle($oObject)

    Local $aCall = DllCall("oleacc.dll", "int", "WindowFromAccessibleObject", _
            "idispatch", $oObject, _
            "hwnd*", 0)

    If @error Or $aCall[0] Then
        Return SetError(1, 0, 0)
    EndIf

    Return $aCall[2]

EndFunc   ;==>_GetObjectWinHandle


Func _AccessibleObjectFromWindow($hWnd)

    Local $sIID_IAccessible = "{618736E0-3C3D-11CF-810C-00AA00389B71}"
    Local $tGUID = _WinAPI_GUIDFromString($sIID_IAccessible)
    Local $pGUID = DllStructGetPtr($tGUID)

    Local $aCall = DllCall($hOLEACC, "int", "AccessibleObjectFromWindow", _
            "hwnd", $hWnd, _
            "dword", 0, _ ; OBJID_WINDOW
            "ptr", $pGUID, _
            "idispatch*", 0)

    If @error Or $aCall[0] Then
        Return SetError(1, 0, 0)
    EndIf

    Return $aCall[4]

EndFunc   ;==>_AccessibleObjectFromWindow


Func _ErrFunc()

    ConsoleWrite("--- COM Error, number = " & Ptr($oMyError.number) & ", description: " & $oMyError.windescription)

EndFunc   ;==>_ErrFunc


Func _Quit()

    Exit

EndFunc   ;==>_Quit
;

We had the same findings about loading oleacc.dll. That's great!

I run first post example on Vista, and was not pleased with what I find. That is to say that I made a mistake with not waiting for page to fully load. That must be done before processing that object.

It's fixed here.

edit:

Call to CoInitialize would be smart to add at the beginning of the script, or maybe:

DllCall("ole32.dll", "int", "CoInitializeEx", "ptr", 0, "dword", 2) ;COINIT_APARTMENTTHREADED

I added error checking for AccessibleObjectFromWindow function

Edited by trancexx

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

I am working on a project to control WPF in Vista, using AutoIT Window Info has no help to me. Is there any help of getting object winhandle using oleacc.dll on WPF application through this? Posted Image

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