vanowm Posted September 18, 2008 Share Posted September 18, 2008 (edited) Hello! I'm trying create a launcher that would launch an application only once from the same folder (lets call it app.exe). It will allow launch another instance of "app.exe" only if already launched "app.exe" located in different folder. I'm not sure if I can use WinActivate() function because all windows "app.exe" are the same (titles, class, etc), the only difference between them is the path of the executable and PID. So I'm using different method to get list of full paths of all processes with name "app.exe" and their PID This is what I got so far:$path = "app.exe" If $CmdLine[0] > 0 Then;filename submitted via command line If (StringInStr($CmdLine[1], "\") OR StringInStr($CmdLine[1], "/")) Then;assuming if submitted string conteins / or \ its a full path otherwise its only a filename $path = $CmdLine[1] Else $path = @WorkingDir & '\' & $CmdLine[1] EndIf Else $path = @WorkingDir & '\' & $path EndIf If (NOT FileExists($path)) Then Exit EndIf $name = StringReplace($path, "/", "\");lets work only on one type of slashes $name = StringRight($name, StringLen($name) - StringInStr($name, "\", 0, -1)) $objWMIService = ObjGet("winmgmts:{authenticationLevel=pktPrivacy, (Debug)}\\.\root\CIMV2") $colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_Process WHERE Caption = '" & $name & "'", "WQL", 0x10 + 0x20) For $objItem In $colItems If $objItem.ExecutablePath == $path Then $PID = $objItem.ProcessId ; app.exe from this folder already started, we need restore it's window and activate it. ; WinActivate($PID); obviosly it doesn't work MsgBox(64, "PID", $PID) Exit; EndIf Next ; app.exe not started, so lets launch it Run($path) Now if someone could help me out by point out to the right direction how to activate already started "app.exe" window by PID (or other methods) my task would be complete. Thank you. Edited September 18, 2008 by vanowm Link to comment Share on other sites More sharing options...
Moderators SmOke_N Posted September 18, 2008 Moderators Share Posted September 18, 2008 http://www.autoitscript.com/forum/index.ph...st&p=313837 Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer. Link to comment Share on other sites More sharing options...
FreeFry Posted September 18, 2008 Share Posted September 18, 2008 While Smoke's function sure will do the job, I'd rather use my version, as not only does it allow you to specify a classname, but you can also specify title, text, and as well a flag which you can set to true to only return the first matching window. Func _ProcessGetWinEx($ivPid, $svClass = "", $svTitle = "", $svText = "", $ivReturnOnlyFirstMatch = False) $ivPid = ProcessExists($ivPid) If Not $ivPid Then Return(SetError(1, 0, 0)) Local $avwArray = WinList() Local $avRet[1] = [0] For $i = 1 To $avwArray[0][0] $avClass = DllCall("User32.dll", "int", "GetClassName", "hwnd", $avwArray[$i][1], "str", "", "int", 4096) If WinGetProcess($avwArray[$i][1]) = $ivPid Then If $svClass = "" Or (IsArray($avClass) And $avClass[2] = $svClass) Then If ($svTitle = "" Or StringInStr($avwArray[$i][0], $svTitle)) And ($svText = "" Or StringInStr(WinGetText($avwArray[$i][1]), $svText)) Then $avRet[0] += 1 ReDim $avRet[$avRet[0]+1] $avRet[$avRet[0]] = $avwArray[$i][1] If $ivReturnOnlyFirstMatch Then $avRet = $avret[1] ExitLoop EndIf EndIf EndIf EndIf Next Return $avRet EndFunc Link to comment Share on other sites More sharing options...
Moderators SmOke_N Posted September 18, 2008 Moderators Share Posted September 18, 2008 I noticed I was doing a call tonight to GetClassName API and I was doing it with returning [2] as my value. Although this works... I'm not entirely sure it's the correct method. This seemed to me to be correct:Local $t_class = DllStructCreate("char[260]") DllCall("User32.dll", "int", "GetClassName", "hwnd", $h_wnd, "ptr", DllStructGetPtr($t_class), "int", 260) Local $s_class = DllStructGetData($t_class, 1) Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer. Link to comment Share on other sites More sharing options...
vanowm Posted September 18, 2008 Author Share Posted September 18, 2008 Thank you for you replies, guys. But I think you misunderstand my question (if I correctly understand your function's purpose). I've already got list of running processes, I got their PID's, all I need now is activate one specific process' window using PID Correct me if I'm wrong, but if you run the same program twice that doesn't change it's title text, you can't search and do actions by title nor by CLASS because CLASS would be the same for both instances? So PID is the only way identify the exact process? WinList same as ProcessList don't return process' location full path, therefore these two functions are useless without additional "tricks"... P.S. _WinGetByPID() worked only for one process instance and only if process has a window (aka not minimized to tray, which I didn't mention earlier )... There are so many functions in AU3 that return PID, but so few where you can use it to...weird. Link to comment Share on other sites More sharing options...
Moderators SmOke_N Posted September 18, 2008 Moderators Share Posted September 18, 2008 Thank you for you replies, guys.But I think you misunderstand my question (if I correctly understand your function's purpose).I've already got list of running processes, I got their PID's, all I need now is activate one specific process' window using PIDCorrect me if I'm wrong, but if you run the same program twice that doesn't change it's title text, you can't search and do actions by title nor by CLASS because CLASS would be the same for both instances? So PID is the only way identify the exact process?WinList same as ProcessList don't return process' location full path, therefore these two functions are useless without additional "tricks"...P.S._WinGetByPID() worked only for one process instance and only if process has a window (aka not minimized to tray, which I didn't mention earlier )...There are so many functions in AU3 that return PID, but so few where you can use it to...weird.A window isn't "minimized to tray". It's either destroyed or hidden. You kind of need to know what you really need/want and what windows do/don't do in order to ask a proper question I suppose.You have a foundation.Winlist()ProcessList()A structured way to give you an idea of how to incorporate the idea.Keep in mind that WinGetState() will also come in hand it seems. Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer. Link to comment Share on other sites More sharing options...
PsaltyDS Posted September 18, 2008 Share Posted September 18, 2008 (edited) Thank you for you replies, guys.But I think you misunderstand my question (if I correctly understand your function's purpose).I've already got list of running processes, I got their PID's, all I need now is activate one specific process' window using PIDCorrect me if I'm wrong, but if you run the same program twice that doesn't change it's title text, you can't search and do actions by title nor by CLASS because CLASS would be the same for both instances? So PID is the only way identify the exact process?Correcting you because you're wrong: Every window has a unique handle. The most common method used to uniquely address a window is by handle. Also, there is no one-to-one relationship between PID and window, because a PID may have no window at all, or may have many windows.WinList same as ProcessList don't return process' location full path, therefore these two functions are useless without additional "tricks"...P.S._WinGetByPID() worked only for one process instance and only if process has a window (aka not minimized to tray, which I didn't mention earlier >_< )...There are so many functions in AU3 that return PID, but so few where you can use it to...weird.Use WinList() to get an array of all matching windows. Loop through the list with WinGetProcess(), using the handle to ID the window, to find which window(s) match(es) your PID (there may be more than one). As for execution path of the PID, which is completely irrelevant to finding its window(s), look at _ProcessListProperties(). Edited September 18, 2008 by PsaltyDS Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law Link to comment Share on other sites More sharing options...
FreeFry Posted September 20, 2008 Share Posted September 20, 2008 (edited) Thank you for you replies, guys. But I think you misunderstand my question (if I correctly understand your function's purpose). I've already got list of running processes, I got their PID's, all I need now is activate one specific process' window using PID Correct me if I'm wrong, but if you run the same program twice that doesn't change it's title text, you can't search and do actions by title nor by CLASS because CLASS would be the same for both instances? So PID is the only way identify the exact process? WinList same as ProcessList don't return process' location full path, therefore these two functions are useless without additional "tricks"... P.S. _WinGetByPID() worked only for one process instance and only if process has a window (aka not minimized to tray, which I didn't mention earlier )... There are so many functions in AU3 that return PID, but so few where you can use it to...weird. Try doing something like this(using my function): ; If you run the processes yourself(ie. through the script, skip this part) Dim $aProcesses = ProcessList("explorer.exe"); This will get all the pids of all processes that has a matching processname to explorer.exe ; Now, as an example, lets loop through all the matching processes, and display their windows. ^^ For $i = 1 To $aProcesses[0][0] $aWindows = _ProcessGetWinEx($aProcesses[$i][1]); Retrieve all the windows by each matching processes, note that the _ProcessGetWinEx function returns the handle of the windows. ConsoleWrite("Process: " & $aProcesses[$i][0] & " with pid: " & $aProcesses[$i][1] & " has #" & $aWindows[0] & " windows:" & @LF) For $i = 1 To UBound($aWindows)-1; Now let's loop through the windows, outputting their window title ConsoleWrite(@TAB & '"' & WinGetTitle($aWindows[$i]) & '"' & @LF & @TAB & @TAB & 'hWnd: ' & $aWindows[$i] & @LF) Next Next Func _ProcessGetWinEx($ivPid, $svClass = "", $svTitle = "", $svText = "", $ivReturnOnlyFirstMatch = False) $ivPid = ProcessExists($ivPid) If Not $ivPid Then Return(SetError(1, 0, 0)) Local $avwArray = WinList() Local $avRet[1] = [0] For $i = 1 To $avwArray[0][0] $avClass = DllCall("User32.dll", "int", "GetClassName", "hwnd", $avwArray[$i][1], "str", "", "int", 4096) If WinGetProcess($avwArray[$i][1]) = $ivPid Then If $svClass = "" Or (IsArray($avClass) And $avClass[2] = $svClass) Then If ($svTitle = "" Or StringInStr($avwArray[$i][0], $svTitle)) And ($svText = "" Or StringInStr(WinGetText($avwArray[$i][1]), $svText)) Then $avRet[0] += 1 ReDim $avRet[$avRet[0]+1] $avRet[$avRet[0]] = $avwArray[$i][1] If $ivReturnOnlyFirstMatch Then $avRet = $avret[1] ExitLoop EndIf EndIf EndIf EndIf Next Return $avRet EndFunc Does what PsaltyDS explained. >_< Edited September 20, 2008 by FreeFry Link to comment Share on other sites More sharing options...
vanowm Posted September 20, 2008 Author Share Posted September 20, 2008 @FreeFry: If WinGetProcess($avwArray[$i][1]) = $ivPid Then Is exactly what I've been missing. Thank you all for the help. P.S. Just in case someone looking for the same solution here is my final code that works perfectly in my tests: expandcollapse popup#NoTrayIcon $path = 'app.exe' If $CmdLine[0] > 0 Then;filename submitted via command line If (StringInStr($CmdLine[1], "\") OR StringInStr($CmdLine[1], "/")) Then;assuming if submitted string conteins / or \ its a full path otherwise its only a filename $path = $CmdLine[1] Else $path = @WorkingDir & '\' & $CmdLine[1] EndIf Else $path = @WorkingDir & '\' & $path EndIf If (NOT FileExists($path)) Then Exit EndIf $name = StringReplace($path, "/", "\");lets work only on one type of slashes $name = StringRight($name, StringLen($name) - StringInStr($name, "\", 0, -1)) $objWMIService = ObjGet("winmgmts:{authenticationLevel=pktPrivacy, (Debug)}\\.\root\CIMV2") $colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_Process WHERE Caption = '" & $name & "'", "WQL", 0x10 + 0x20) For $objItem In $colItems If $objItem.ExecutablePath = $path Then ; app.exe from this folder already started, we need restore it's window and activate it. $PID = $objItem.ProcessId Local $list = WinList() For $i = 1 To $list[0][0] If WinGetProcess($list[$i][1]) = $PID AND $list[$i][0] Then;we need activate only window that matches our PID and has a title WinSetState($list[$i][1], "", @SW_SHOW) WinActivate($list[$i][1]) ExitLoop EndIf Next Exit EndIf Next ; app.exe not started, so lets launch it Run($path) Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now