Jump to content

Vista Compatibility


Recommended Posts

Hi,

I couldn't find a thread documenting Vista issue compatibility considerations with AutoIt. If I missed the topic, I would appreciate someone directing me to it. ;)

My most recent issue is with ProcessExists and ProcessList. As you may know, if you are logged on as a standard user in Vista, you need administrative privileges to see all processes under Task Manager. Otherwise, you cannot see processes owned by users logged on under other user accounts.

AutoIt's ProcessExists and ProcessList functions DO SEE processes owned by other users when running under a standard user account, and with no intervention by UAC.

I'm not suggesting those functions shouldn't see processes owned by other users, but it presents a challenge because WinGetHandle does not "see" the windows owned by processes of other users (unless that is a "bug"), and of course all the window api's monitored by User Interface Privilege Isolation (UIPI) under Vista fail if directed to a process with a higher integrity level, which apparently includes all processes started by a user running under an administrative account.

What a headache!

For me, a solution is to not "see" processes owned by other users. There are plenty of examples of how to accomplish this using WMI. However, both on my Vista machine and XP Pro machine, WMI is SLOW -- 750 to 1,500 milliseconds to answer the question: Does a process with this name and owned by this user exist?

Is this on par with your experience with WMI?

Do you know of another way to determine the owner of a process other than using WMI?

Do you know of a way to determine the integrity level of another process without using WMI?

Your comments are appreciated -- and thank you in advance.

Sincerely,

Paul

Edit: While it IS true that UIPI fails certain functions directed to a process with a higher integrity level, the strikeout above is because what seemed "apparent" was really an isuue of the process running under a different user account; not an issue of integrity level (read rest of thread :D )

Edited by pdaughe
Link to comment
Share on other sites

from the helpfile: AutoIt on Windows Vista

To force a script to attempt to run itself with administrative rights add the #requireadmin directive at the top of your script.

That doesn't seem to be what this person is asking. They seem to be asking how to find out the owner/creator of a process without using WMI. As far as I'm aware there isn't a native autoit solution to this e.g. there is no functions such as ProcessOwner().

Link to comment
Share on other sites

> AutoIt's ProcessExists and ProcessList functions DO SEE processes owned by other users ...

> ... because WinGetHandle does not "see" the windows owned by processes of other users ...

> For me, a solution is to not "see" processes owned by other users.

But if you ran the whole script as admin via "the #requireadmin directive" would WinGetHandle "see" all windows? Then you would not need your work around. I cannot test this theory on Vista right now - so I'll leave it to you/others.

> ... and of course all the window api's monitored by User Interface Privilege Isolation (UIPI) under Vista fail if directed to a process with a higher integrity level, which apparently includes all processes started by a user running under an administrative account.

But would they fail if the script was running as admin?

Sorry, I'm going to guess that your reply to this is going to teach me more about Vista and what you want to do than it is going to be helping you.

[size="1"][font="Arial"].[u].[/u][/font][/size]

Link to comment
Share on other sites

But would they fail if the script was running as admin?

Good question. I tested it and no, WinGetHandle does NOT see the windows of a process owned by another user. I actually opened a problem ticket on this issue.

As for Vista compatibility, I have run into a number of issues. For background information, take a look at this thread:

http://www.autoitscript.com/forum/index.ph...9717&st=585

That thread discusses a related, but different problem with AutoIt and Vista. The other compatibility issue I recall is one regarding extended file properties; Vista introduces many more extended file properties; the problem is Microsoft changed the number of some existing properties (e.g. duration)! Then of course there's the change is the way wave volume is handled.

These are just issues I've run across, which is way I thought others may be interested in starting a thread on Vista compatibility issues. It takes time to document these things and it's easier, at least for me, to do at the time you're struggling with a given issue.

Link to comment
Share on other sites

Thanks for the info and the link. I had seen your post of 09 August in that thread, but had not been back there until today to see the subsequent info on Vista.

[Yep - good/proper communication is hard, no matter how long one has been practicing :-]

Luckly, I don't have to script for Vista. The company that I work for downgrades every new system to XP Pro.

Edited by herewasplato

[size="1"][font="Arial"].[u].[/u][/font][/size]

Link to comment
Share on other sites

If you put this at the first line of your code then some of the problems there are fixed...

#requireadmin

But that is in the windows vista help file,

so i dont really think i'm helping anyone much ;)

Thanks for replying, but you really have to look at this closely and not confuse UAC and UIPI and the attendent implications. The full impact of UIPI is difficult to ascertain, at least in my experience. It has me asking more often than I would like: "Is this a problem, or just the way it is implemented in Vista for reasons beyond my present level of understanding?"

Fundamentally, here's the question: Would you expect ProcessExist or ProcessList to "see" processes owned by users running under other standard user accounts, and if so, would you expect the window functions to "see" the windows belonging to those processes?

Is the way it works at the present due to probems in Vista, AutoIt's support of Vista, or a general lack of understanding on my part regarding multiple user accounts and the ramifications thereof? I do not know the answer to that question. I did open a problem ticket regarding the inability of WinGetHandle to retrieve the handle of a window belonging to a process owned by another user (only because the documentation does not state otherwise), but I don't know if that's the way it's "suppose to work". The documentation does not speak to this issue, which is why I started this thread.

The bottom line is that it appears there are restrictions on communicating with processes owned by another user, and IN GENERAL, an application should not have this requirement (MSDN recommends that such functionality, if needed, should be implemented as a service).

Therefore, with my understanding at this point, it seems the ability to ask if a process exists which belongs to the currently logged on user is, in some cases, required.

I am trying determine how others perceive the dichotomy between the process functions and window functions with respect to multiple user accounts and to learn from their comments. This seems a reasonable and wise course to follow before requesting the developers to provide an optional parameter on the process functions to filter by user. The way I see it, the developers work very, very hard to provide us with the functionality we need, and they do it asking nothing in return. So I think it is incumbent upon us to do our homework first, including reading threads carefully, before making feature requests.:D

Edited by pdaughe
Link to comment
Share on other sites

I just checked the AutoIt Trac system and have received a response for the problem ticket I opened regarding WinGetHandle and multiple user accounts.

It is NOT a bug: WinGetHandle/WinGetTitle do not "see" windows belonging a process owned by a user running under a different user account because the user is running under a different desktop. That makes sense.

What this means is that you have to be "careful" in designing your application if it is multi-process or has multi-user considerations. ProcessExist and ProcessList DO see processes belonging to the other users, but you cannot communicate with them, nor can you interact with them in any way. For example, consider this script from the help documentation for ProcessClose:

$PID = ProcessExists ("notepad.exe")
If $PID Then ProcessClose ($PID)

As written, the ProcessExists will "see" a notepad.exe process running under a different user account; but the ProcessClose will NOT see it; that is, the ProcessClose will fail.

This is NOT related to UIPI or Vista, as I first expected. If you read the thread referred to post #6 above, the situation discussed there was indeed a direct result of UIPI. In that case, ProcessExists did NOT see the process launched with the Run command because the launched process required adminstrative privleges and therefore ran at a higher integrity level than the launching process.

For me, it's back to the original question: Does anyone know of a more efficient way than WMI to determine the owner of a process?

Link to comment
Share on other sites

You're confusing things here. The reason you can't see windows belonging to other users is because those windows are attached to a different Desktop. The reason you can't access processes started with higher privileges is because you do not have high enough privileges to interact with it. In the first case it is a situation where you can't "see" the windows via code but in the latter case the process can be seen it just can't be tinkered with.

Now, as for solutions to problems. There are functions for enumerating the Desktops on a system as well as the windows on a specific Desktop. See the Windows API functions EnumDesktops() and EnumDesktopWindows(). As for finding the the user-name of a process, that's relatively simple from the looks of it. I did a search for "username of process" on Google and unsurprisingly the first result show example C code which looks pretty trivial to port to AutoIt as a UDF.

Link to comment
Share on other sites

Valik,

You're right, it has been very confusing. As it turns out, the very title to this topic is misleading! The "confusing part" for me involved this point you made:

The reason you can't access processes started with higher privileges is because you do not have high enough privileges to interact with it

That is true (which was the first situation I encountered), but it is also true that one can't access a process started with standard user privileges, but running under a different user account. It's not the integrity level of the process that appears to be the issue (unless you consider the process owner as part of the integrity level, but nowhere in my research on MSDN is integrity level defined that way), it's the process owner. Just start notepad on a standard user account, no elevated privileges. Then log on to a different account. It doesn't matter whether the different account you log on to has administrative (i.e. higher) privileges or not. You cannot "tinker" with the process. You can "see it", but ProcessClose can't close it, and we know the Win functions cannot even see the process's windows. This is the issue that initially confused me. With multiple user accounts running, the ProcessClose in the simple two-line script I posted above may or may succeed. It is unpredictable, without knowing the owner of the notepad.exe process.

I also saw the C example you referred to (which doesn't use WMI). I may try to tackle it, but I fear I won't have the level of competence required. I can understand DLLStructs and their use just fine, but it's that ability to translate from the MSDN syntax or C code that is difficult for me.

I did open a feature request to have an additional parameter added to ProcessExists and ProcessList.

With the feature request the above example could be rewritten as follows:

$PID = ProcessExists ("notepad.exe", @UserName)
If $PID Then ProcessClose ($PID)

which removes the unpredictability. Even if it is not approved, I think the implications of UIPI and multiple user accounts could be mentioned in the ProcessExist and ProcessList helpfile documentation to minimize the confusion I've experienced with these issues.

Sincerely,

Paul

Link to comment
Share on other sites

If ProcessClose() can't close the process when it should have the right privileges then that is a bug. I specifically added code to ProcessClose() so that it could close processes running under a different user-account. It works on XP. Maybe it requires more work on Vista. However, if Vista's Task Manager (running elevated) can close a process, then a similarly elevated ProcessClose() statement on the same process should also close it and if it can not then it is a bug in ProcessClose().

Link to comment
Share on other sites

That is true (which was the first situation I encountered), but it is also true that one can't access a process started with standard user privileges, but running under a different user account. It's not the integrity level of the process that appears to be the issue (unless you consider the process owner as part of the integrity level, but nowhere in my research on MSDN is integrity level defined that way), it's the process owner. Just start notepad on a standard user account, no elevated privileges. Then log on to a different account. It doesn't matter whether the different account you log on to has administrative (i.e. higher) privileges or not. You cannot "tinker" with the process. You can "see it", but ProcessClose can't close it, and we know the Win functions cannot even see the process's windows. This is the issue that initially confused me. With multiple user accounts running, the ProcessClose in the simple two-line script I posted above may or may succeed. It is unpredictable, without knowing the owner of the notepad.exe process.

When you log on as that adminstrator account you are still a standard user. Vista does not grant you administrator rights just because the account is a part of the administrator group. That's what all that UAC business is about. So it does not surprise me at all that the situation you describe works how you describe. However, and this is the key, when you elevate your script so that it's an administrator, you *can* close the process started by another user - even on Vista. I just tested this myself and it works as expected.

Closing processes started by other users in Windows requires a privilege only adminstrators have. On Vista, being on an administrator account isn't enough, the process must be elevated to take advantage of those rights. This is all perfectly normal.

Link to comment
Share on other sites

Thanks Valik, apparently I HAVE really confused this! I did not mean to imply that an elevated process could not close a process owned by another user, in fact, the very point I was trying to make is that a non-elevated process won't close it.

Then log on to a different account. It doesn't matter whether the different account you log on to has administrative (i.e. higher) privileges or not. You cannot "tinker" with the process. You can "see it", but ProcessClose can't close it, and we know the Win functions cannot even see the process's windows.

We know that's true from what you pointed out: that's the design intent of UAC -- the fact you log on under an administrative account is irrelavant to the issue.

Look at the example for ProcessClose in the help documentation.

If you run that example AS IS, is the ProcessClose going to succeed?

You, or I, have NO WAY OF KNOWING.

If the notepad.exe is running under a different account, the ProcessExists still sees it, but ProcessClose CANNOT close it (the script is running non-elevated). If the notepad.exe is running under the same account, the ProcessClose can close it. It is, however, unpredictable, and it's easy to see how that will produce unreliable scripts.

Look at my very first post. That's the problem. The Win functions do not "see" the windows of processes belonging to other users, because as you taught us ;), they are running under a different desktop. To make me, that made perfect sense. Even if, for example, WinGetHandle DID return the handle to a window belonging to a process owned by another user, you wouldn't be able to do anything with it, right?

To me, it would also make sense that ProcessList and ProcessExist would not see processes owned by other users. You can't do anything with the processes anyway (as a normal, non-elevated process), and that is the point.

As I mentioned in the very first post, I'm not suggesting that ProcessList and ProcessExist should change, but it does make using AutoIt "unpredictable" in use when there are multiple user accounts. Again, one cannot predict the outcome of code like the example for ProcessClose without knowing the owner of the process.

My bet is that if the Process functions did filter on process owner, many existing scripts today would not break, but have a greater chance of functioning correctly in a multiple account environment. I know I wouldn't, and expect you wouldn't either, make such a change though due to compatibilty concerns. In my experience, though, the addition of an optional parameter on the ProcessExist and ProcessList functions would be beneficial, but not of course if I am the only user using AutoIT in a multiple user account environment. :D

P.S. By the way, I "thought" I opened a feature request, but now I don't see it anywhere?

Edit: Also, it may be best if the title of this topic was changed to "Using AutoIT with Multiple User Accounts" so as not to mislead others. I originally though it was a Vista UIPI issue, but I was wrong; it is a multiple user accounts issue.

Edited by pdaughe
Link to comment
Share on other sites

The feature request for ProcessList() was closed.

Processes are a global thing. Windows are not. You're trying to compare apples and oranges with your "I can't see windows but I can see processes" statement.

You're also taking a very short-sighted view of the problem. Yes, the example script can fail. Who gives a damn? It's an example script. You want to prevent problems, write error checking. You've notice, I'm sure, most of the example scripts don't have error checking. Here are two different ways of verifying if ProcessClose() worked:

Local $pid = ProcessExists("notepad.exe")
ProcessClose($pid)
If ProcessExists($pid) Then MsgBox(4096, "", "Process failed to close.")

Local $pid = ProcessExists("notepad.exe")
ProcessClose($pid)
If Not ProcessWaitClose($pid, 1000) Then MsgBox(4096, "", "Process failed to close.")

The second example is probably better as it should avoid any race conditions the first example may or may not have. But at any rate, your complaint seems to be largely centered around being unable to verify if a process was closed when it's an incredibly trivial thing to check yourself by just writing a little extra code.

Link to comment
Share on other sites

Your complaint seems to be largely centered around being unable to verify if a process was closed when it's an incredibly trivial thing to check yourself by just writing a little extra code.

Golly, sorry Valik if I came across as complaining. Actually, I was simply trying to learn how others are handling this. I only used ProcessClose as an example. It's NOT shortsighted though-- there are many ramifications.

Is Calculator running? If not, display the Calculator.

If Not ProcessExists ("Calc.exe") Then Run ("Calc.exe")

Is the calculator window visible to the user? It all depends on whether calc.exe is running under the same or different account. You do not see the dichotomy here?

Sure, you can follow the code with "error checking" as you described it by checking if a Calculator window exists, but then you introduce dependencies on window titles.

I will not call another shortsighted -- I am also not responsible for how others judge a reply...

Link to comment
Share on other sites

I will not call another shortsighted -- I am also not responsible for how others judge a reply...

The following text from you is an example of you being short-sighted.

Sure, you can follow the code with "error checking" as you described it by checking if a Calculator window exists, but then you introduce dependencies on window titles.

Using window titles in general is a bad idea. The class for Calculator is "SciCalc". It does not matter what the window title is, the class will still be the same.

Yes, I realize, you're coming up with contrived examples which are easy to shoot down. Here's my point: stop looking for the one magical solution. Your insistence on the user-name is obscuring the fact that it's not a requirement to do the things you need to do. It is only one of many potential methods you can use.

Personally, if I were you and this was an issue, I'd see about getting some C code for determining a username of a process ported to a UDF (ask on the forum if you need help). Then with that in place it would be trivial to implement your own custom _ProcessList() or _ProcessExists() functions using your _ProcessGetUser() and the built-in ProcessList(). Here's the implementation for those two functions which is rather straightforward. Add in a function name _ProcessGetUser() which takes a PID and returns the user that owns the process and you'll have what you want.

Func _ProcessExists($vProc, $sUser = "")
    ; If the input is a number, it's a PID and the user parameter doesn't make sense so is ignored.
    ; Also, if the user isn't specified, then just do normal ProcessExists().
    If StringIsDigit($vProc) Or $sUser = "" Then Return ProcessExists($vProc)

    ; We must be looking for a specific process by a specific user, so get all processes with
    ; a matching name.
    Local $a = ProcessList($vProc)

    ; Iterate the processes and check the name, return if we have a match.
    For $i = 1 To UBound($a) - 1
        If _ProcessGetUser($a[$i][1]) = $sUser Then Return $a[$i][1]
    Next

    ; No match found, return 0.
    Return 0
EndFunc    ; ProcessExists()

Func _ProcessList($sName = "", $sUser = "")
    ; If not looking for a specific user then just return ProcessList() results.
    If $sUser = "" Then Return ProcessList($sName)

    ; Get all the processes by the name.
    Local $a = ProcessList($sName)

    ; Assume that all processes match.
    Local $b[UBound($a)][2]

    ; Iterate the array building a list of processes running as the specified user.
    For $i = 1 To UBound($a) - 1
        If _ProcessGetUser($a[$i][1]) = $sUser Then
            ; We have a match, increment the count and copy the array data.
            Local $nIndex = $b[0][0] + 1
            $b[0][0] = $nIndex
            $b[$nIndex][0] = $a[$i][0]
            $b[$nIndex][1] = $a[$i][1]
        EndIf
    Next

    ; Resize the array as necessary.
    If $b[0][0] Then
        ; Trim the array to fit.
        ReDim $b[$b[0][0]][2]
    Else
        ; No results, size the array to the minimum and set @error.
        ReDim $b[1][2]
        SetError(1)
    EndIf

    ; Return the result.
    Return $b
EndFunc    ; _ProcessList()

Please note, while the code does pass Au3Check it is dry-coded.

Link to comment
Share on other sites

Thanks Valik -- another case where one definitely needs to know the owner of a process is with the multi-process UDF's.

Here's what I am currently using for UserProcessExists:

#include-once
If Not IsDeclared ("objWMI") Then Global $objWMI = 0

Func UserProcessExists ($Process_Name)

If StringIsDigit ($Process_Name) Then Return ProcessExists ($Process_Name)

If Not IsObj ($objWMI) Then $objWMI = ObjGet ("winmgmts:" & "{impersonationLevel=impersonate}!\\.\root\cimv2")

Local $Owner_Name, _
      $Domain_Name, _
      $ProcessCollection = $objWMI.ExecQuery ("Select * from Win32_Process where Name = '" & $Process_Name & "'")

For $objProcess In $ProcessCollection
    If $objProcess.GetOwner ($Owner_Name, $Domain_Name) Then ContinueLoop
    If $Owner_Name <> @UserName Or $objProcess.Name <> $Process_Name Then ContinueLoop
    Return $objProcess.ProcessId
Next

Return 0

EndFunc

and for UserProcessList:

#include-once
If Not IsDeclared ("objWMI") Then Global $objWMI = 0

Func UserProcessList ($Process_Name = "")

If Not IsObj ($objWMI) Then $objWMI = ObjGet ("winmgmts:" & "{impersonationLevel=impersonate}!\\.\root\cimv2")

Local $Result_Array[1][2] = [ [0, "[ProcessId]"] ], _
      $Owner_Name, _
      $Domain_Name, _
      $ProcessCollection =  $objWMI.ExecQuery ("Select * from Win32_Process")

For $objProcess In $ProcessCollection
    If $objProcess.GetOwner ($Owner_Name, $Domain_Name) Then ContinueLoop
    If $Owner_Name <> @UserName Then ContinueLoop
    If $Process_Name <> "" And $Process_Name <> $objProcess.Name Then ContinueLoop
    $Result_Array[0][0] += 1
    ReDim $Result_Array[$Result_Array[0][0] + 1][2]
    $Result_Array[$Result_Array[0][0]][0] = $objProcess.Name
    $Result_Array[$Result_Array[0][0]][1] = $objProcess.ProcessId
Next

Return $Result_Array

EndFunc

On both my Vista machine and XP Pro machine these functions are relatively slow. You can see I (desperately) moved the instantiation of the WMI object out to a global, but that makes negligible difference. I have three processes I need to check during the initialization of the main process, and this code adds almost three seconds to the elapsed time from when the user double clicks on the application's desktop icon until the application's main window appears. Using the standard AutoIt process functions, the initialization appears almost instant, but then the application fails in a multiple user account environment.

If my experience with WMI is typical, then like you suggested, this probably needs to be done in compiled code. I may someday tackle that C routine, but for now I've pursued another solution. The real question in my case is not whether a process exists; the question is whether I can communicate with it. The (non-global) mutex functions do not cross user account boundaries and therefore provide a VERY efficient solution. My tests with this solution parallels the efficiency of the native AutoIt functions.

Perhaps this thread, even with the confusion, will be beneficial to others.

Thanks again for your assistance.

Paul

Link to comment
Share on other sites

Geesh, I've been at this for three days -- time to take a break....

This post is for anyone who may be following this thread or find it via search in the future. Don't make the same mistake I did!

Sure, you can use a mutex (with a one-to-one correspondence between the mutex name and process name) to reliably answer the question: Does this process exists under the account which I am currently running?

Great, if it doesn't exist (i.e the mutex was created), launch the process (you have its PID -- life is great).

Otherwise, use the currrently existing process (my application, depending on user settings, may not close the existing process).

How could I be so silly? I need the PID of the existing process! My initial test worked fine, by LUCK.

ProcessExists apparently finds the process with the highest PID, regardless of how recently the process was launched.

Duh?

The UserProcessExists and UserProcessList functions in the previous post provide a reliable solution if you can tolerate the performance penalty of WMI.

Back to the drawing board.....

While I rest, it still intriques me to anwer the TRUE question: Can I communicate with this process?

Does anyone have any thoughts on how to best answer that question?

C'mon, we love this stuff!

Paul

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