PDA

View Full Version : Loop through tasks to GetObject()



astranberg
11-04-2013, 09:50 AM
Hello everybody (first post!)!!

Problem:
I am trying to access multiple runnings instances as objects, and loop through them to find the right one. In particular, I am trying to access GetObject(, "Reflection2.Session"), but as you likely know, GetObject only gets the first running instance.

Background:
I stumbled onto this forum from google. It partially answered my question, but I have another question. Instead of resurrecting that thread, I am creating a new one with my specific problem.

vbaexpress.com/forum/showthread.php?36318-Loop-through-active-window-applications
(This forum won't let me post an URL link because I don't have enough posts, how dumb is that?)


Partial Solution:
That forum gave this code, which loops through all running tasks on your computer:


Function getCache(num_port)
Dim wd As Object
Dim t, i As Long
Set wd = CreateObject("word.application")
For Each t In wd.Tasks
i = i + 1
Cells(i, 1) = t
If t = "Reflection" Then
getCache = t
Exit Function
End If
Next
wd.Quit
Set wd = Nothing
End Function


My issue is that this function doesn't return an actual object. Does anybody know how I can do that?

Thank you!
Adam

snb
11-04-2013, 10:32 AM
Sub M_snb()
with CreateObject("word.application")
For Each tsk In .Tasks
c00=c00 & vblf & tsk.name
if instr(tsk.name,"Reflection") then c01=tsk.name
Next

.tasks(c01).activate
end with
msgbox c00
End Sub

But I can hardly imagine when you would need such a code.

Kenneth Hobs
11-04-2013, 10:34 AM
This is your 6th post I believe and the same question as before. The forum limits url links until you have 5 posts to stop some spam robots and commercial advertisers that only post once.

So Reflection2.Session is a class object for Word?

See if this gives you any ideas. I alluded to this sort of method in the thread that you referenced.

Option Explicit
' http://eileenslounge.com/viewtopic.php?f=27&t=11385
Declare Function GetCurrentProcessId Lib "kernel32" () As Long


' http://www.vbaexpress.com/forum/showthread.php?48103-Loop-through-tasks-to-GetObject%28%29
Sub StopOthers()
' Add a Reference to Microsoft WMI Scripting Library
Dim hProcMe As Long
hProcMe = GetCurrentProcessId
Dim oWMI As Object
Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Dim oList As Object
Set oList = oWMI.ExecQuery("select * from win32_process where name like 'EXCEL%'")
'http://msdn.microsoft.com/en-us/library/aa393741%28v=vs.85%29.aspx
'Dim oProcess As SWbemObject
Dim oProcess As Object
For Each oProcess In oList
Dim iErr As Integer, hProc As Long
hProc = oProcess.ProcessID
Debug.Print hProc, oProcess.Name
'If hProc <> hProcMe Then
' iErr = oProcess.Terminate
'End If
Next oProcess
End Sub

'http://www.mrexcel.com/forum/excel-questions/599317-visual-basic-applications-code-work-reflection-unix.html

'Reflections 2011 VBA Guide
' http://docs.attachmate.com/reflection/2011/r2/help/en/vba_guide/

astranberg
11-04-2013, 10:56 AM
Snb,

That works fantastic!! Thank you snb! The only thing I'm missing is setting it as an object. Right now I'm using this:

Function M_snb() As Object
With CreateObject("word.application")
For Each tsk In .Tasks
c00 = c00 & vbLf & tsk.Name
If InStr(tsk.Name, "Scripts") Then
c01 = tsk.Name
End If
Next
M_snb = .Tasks(c01)
End With
End Function
Sub test2()
Dim Session As Object
Set Session = M_snb
Session.Transmit "A" ' Tests to see if it got it as an object.
End Sub



But I can hardly imagine when you would need such a code.

I can GetObject(, "Reflection2.Session") to get the first instance of this program, but I want to grab different instances. The people I roll my scripts out to often have multiple Reflections open, and I want to be able to grab the Reflection that is open to a certain spot, instead of making them close all other instances.

Kenneth,
Ah! I understand preventing bots spamming links. :) It actually wouldn't let me create a thread at all until I had 5 posts.

Reflection2.Session is not a class object of Word. It's WRQ Reflection for UNIX and Digital (it's own program) made by AttachMate. I have no idea why the function I found references Word, but all I know is that it loops through all of your computers tasks, including this standalone program. I am hoping to modify it to take the result and reference it as an object.

The code you gave me finds the processes, but I still need to set the function equal to the processes object once it finds it.

Function getCache2() As Object
' Add a Reference to Microsoft WMI Scripting Library
Dim hProcMe As Long
hProcMe = GetCurrentProcessId
Dim oWMI As Object
Set oWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Dim oList As Object
Set oList = oWMI.ExecQuery("select * from win32_process where name like 'r2w%'")
'Dim oProcess As SWbemObject
Dim oProcess As Object
For Each oProcess In oList
Dim iErr As Integer, hProc As Long
hProc = oProcess.ProcessID
StopOthers = oProcess
Next oProcess
End Function

snb
11-04-2013, 12:46 PM
Sub M_snb()
For Each tsk In Tasks
If InStr(tsk.Name, "Scripts") Then c01 = c01 & vblf & tsk.Name
Next

for each it in split(mid(c01,2),vblf)
Set c02 = Tasks(it)
With c02
.Application.Close 0
End With
next
End Sub

I didn't find any property that discriminates between several instances of the same program; maybe your application produces those unique properties, reflected in the property 'name'.

astranberg
11-04-2013, 01:06 PM
Well there is a reference to my program with some very unique attributes and commands and such (I don't know the technical terms). For example, you can GetText(StartRow, StartCol, EndRow, EndCol), .Transmit "Text", etc. I am going to be using GetText to discriminate between multiple instances, but I need to be able to access each instance first.

The code just above works until


c02.Application.Close 0

That is what keeps hanging me up. I have set something to that task, but I can't seem to access it the same as if I had done:

Dim Session as Object
Set Session = GetObject(, "Reflection2.Session")
If Instr(1, Session.GetText(0, 0, 24, 78), "Script Start Position") > 0 then
' Proceed
End If

snb
11-04-2013, 02:35 PM
or ?


For Each it In split(mid(c01,2),vblf)
Set c02 = Tasks(it)
msgbox c02.GetText(0, 0, 24, 78), "Script Start Position")
Next

astranberg
11-04-2013, 03:06 PM
c02.Activate works, but none of the custom functions that works with GetObject(, "Reflection2.Session") work.

Kenneth Hobs
11-04-2013, 03:10 PM
While not foolproof, many find that knowing the classname of the application and the window's caption is sufficient to find the right instance of an application.

astranberg
11-04-2013, 03:46 PM
While not foolproof, many find that knowing the classname of the application and the window's caption is sufficient to find the right instance of an application.

I don't need a solution for figuring out which window to get. What I need is to access each window as an object. I can GetObject(, "Reflection2.Session"), but that only grabs the first instance. snb's solution successfully cycles through the windows perfectly, but I can't use the object as I normally do with that GetObject.

I need



obj_FoundWindow.Transmit "A" ' This .Transmit only works with a "Reflection2.Session," object.


to work.

snb
11-05-2013, 03:40 AM
Did you activate the reference to "Reflection.Session" ?

Aflatoon
11-05-2013, 05:25 AM
I don't need a solution for figuring out which window to get. What I need is to access each window as an object.

I disagree. If you can get the correct window handle you may be able to use the AccessibleObjectFromWindow API function to return the required automation object. Using Word's Tasks collection will not give you that object as far as I am aware.

You might also find a solution in an Attachmate/Reflection user group since this doesn't really have anything to do with Excel (even if that's where you happen to be running the code).

Kenneth Hobs
11-05-2013, 08:46 AM
To use the method that Aflatoon mentioned, the IDespatch ID is needed. Since I don't use Unix, I would have no way of finding that. I have seen some examples using it for Excel and some for MSWord to a small degree.
http://www.mrexcel.com/forum/excel-questions/499935-getobject-hwnd.html

One simple method that I used in the past was to close each instance of Excel until I got the one that I needed using GetObject().
http://excel.tips.net/T009451_Finding_Other_Instances_of_Excel_in_a_Macro.html

astranberg
11-05-2013, 09:44 AM
Did you activate the reference to "Reflection.Session" ?

Yes.


You might also find a solution in an Attachmate/Reflection user group since this doesn't really have anything to do with Excel (even if that's where you happen to be running the code).

Unfortunately, there is no such thing as a Reflection user group. Their customer service is awful.


One simple method that I used in the past was to close each instance of Excel until I got the one that I needed using GetObject().
http://excel.tips.net/T009451_Finding_Other_Instances_of_Excel_in_a_Macro.html

I wish I could use this, but they really need to have their spot saved in each instance.


I disagree. If you can get the correct window handle you may be able to use the AccessibleObjectFromWindow API function to return the required automation object. Using Word's Tasks collection will not give you that object as far as I am aware.


To use the method that Aflatoon mentioned, the IDespatch ID is needed. Since I don't use Unix, I would have no way of finding that. I have seen some examples using it for Excel and some for MSWord to a small degree.
http://www.mrexcel.com/forum/excel-questions/499935-getobject-hwnd.html

Okay, this idea sounds the most promising. This is Reflection for Unix and Open VMS, but I actually run this program on Windows 7. I have no idea what an IDispatch ID is or how to get it.

Kenneth Hobs
11-05-2013, 10:04 AM
Getting the ID might not solve much for you since this is an advanced topic. I guess if you want to try, go into regedit and Find the class Reflection2.Session. Then expand the CLSID to get the value.

Have you tried to open each Application to see if the Caption for each is different? This is done by double clicking the application(s) in the Application tab of the Task Manager. Post a screen print in an Excel file for us to see the application windows.

astranberg
11-05-2013, 10:21 AM
Thank you Kenneth.

Each window has the same caption, (i.e. you can open multiple instances of the same program).

10787

Aflatoon
11-06-2013, 04:54 AM
If the captions are all the same, I don't know how you would plan to identify the one you want.

astranberg
11-06-2013, 08:43 AM
If the captions are all the same, I don't know how you would plan to identify the one you want.

The logic behind how I get the right one really isn't my question, but I'll explain anyways. After I am "in" the window (e.g. with GetObject), I can pull the PORT number, time window has been up, or where the window is in Reflection. Notice how the top left window is different from the other three. With a simple GetText() command, I can check to see if "ENTER ACCOUNT#, MPI#, STATEMENT# OR NAME" is in the window. Perhaps that text signifies the start position of this script, so I would loop through accessing each Reflection window until I find that text in it.

But seriously, I'm just trying to figure out how to loop through these windows and "access" them with the commands I get to use when I access the first window through GetObject(, "Reflection2.Session").

Aflatoon
11-06-2013, 10:10 AM
In that case, assuming you can obtain the correct IDispatch ID, you would have to call AccessibleObjectFromWindow on every window of the relevant class to get an object and then apply your tests to see if it is the one you want.

astranberg
11-06-2013, 10:20 AM
In that case, assuming you can obtain the correct IDispatch ID, you would have to call AccessibleObjectFromWindow on every window of the relevant class to get an object and then apply your tests to see if it is the one you want.

Thank you! I will now attempt to AccessibleObjectFromWindow.......


I guess if you want to try, go into regedit and Find the class Reflection2.Session. Then expand the CLSID to get the value.

Does F29799A0-4B0C-101B-AC7B-04021C007002 seem like a valid IDispatch ID? And will this ID be the same accross all computers?

I am now trying to figure out this function. Can someone help me modify this code to work?



Option Explicit

Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long

Private Declare Function IIDFromString Lib "ole32" _
(ByVal lpsz As Long, ByRef lpiid As GUID) As Long

Private Declare Function AccessibleObjectFromWindow Lib "oleacc" _
(ByVal hWnd As Long, ByVal dwId As Long, ByRef riid As GUID, _
ByRef ppvObject As Object) As Long

Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type

Private Const RETURN_OK As Long = &H0
Private Const IID_IDispatch As String = "{F29799A0-4B0C-101B-AC7B-04021C007002}" ' Found this for Reflection2.Session
Private Const OBJID_NATIVEOM As Long = &HFFFFFFF0 '// This is supposed to go as the second input of AccessibleObjectFromWindow, _
'// but I think I need to change this, as this was originally written for Excel.
Sub ComplexTest()

Dim iCounter As Long
Dim hWndXL As Long
Dim oXLApp As Object
Dim oWB As Object
Dim oWS As Object

'// First the first Reflection Window
hWndXL = FindWindowEx(0&, 0&, "r2Window", vbNullString)

'// Got one, at least...?
Do While hWndXL > 0

'// Increment counter
iCounter = iCounter + 1

'// Print Instance & Handle to Debug window
Debug.Print "Instance #" & iCounter & ": "; "Handle: " & hWndXL

'// Get a reference to it
If GetReferenceToApp(hWndXL, oXLApp) Then
'// Check if this is the window I want
End If

'// Find the next Reflection Window
hWndXL = FindWindowEx(0, hWndXL, "r2Window", vbNullString)

Loop

End Sub

'// Returns a reference to a specific instance of Excel.
'// The Instance is defined by the Handle (hWndXL) passed
'// by the calling procedure
Function GetReferenceToApp(hWndXL As Long, oXLApp As Object) As Boolean

Dim hWinDesk As Long
Dim hWin7 As Long

Dim obj As Object
Dim iID As GUID

Call IIDFromString(StrPtr(IID_IDispatch), iID) ' Outputs iID as the GUID

If AccessibleObjectFromWindow(hWndXL, OBJID_NATIVEOM, iID, Session) = RETURN_OK Then
Set oXLApp = obj.Application
GetReferenceToXLApp = True
Session.Transmit "IT WORKS!!!!!!!!!!!!!!!!!!!!!!!!"
End If
End Function