Results 1 to 26 of 26

Thread: How to control an external application that my process spawns.

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Mar 2010
    Posts
    661

    How to control an external application that my process spawns.

    I have a program, when the user clicks on the "Process" button, a process goes through and in the end writes the final results into a few text files.
    Then I show those text files to the user like this:
    Code:
       CmdStr = "C:\Windows\System32\Notepad.exe " & TheFilePath
       Call Shell(CmdStr, vbNormalFocus)
    Now, if the user changes the parameters on the screen and clicks on the "Process" button one more time, the new process will overwrite some of those text files and create new text files instead of the other ones and then the process will go on to open the new version of all those text files (overwritten ones as well as the new ones).
    What I want to do is to make sure that when the "Process" button is clicked, it either closes all those previous text files or shows them (by bringing them to the front) and issuing an error message to the user to ask him to close all those text files and try again.
    In other words, if any of those text files from a previous run are already open, the process should not start. Instead it should break with an error message.
    In order to achieve the above goal, I need two things:

    1. When my program opens the text file in Notepad, I have to somehow identify this situation, for example if I open a text file in Notepad, does that instance of Notepad have a handle that my program can programmically read and store in a variable?
    In other words if the process spawns a new (external) application for example Notepad.exe or use Shell command to execute any other application, I guess that application that pops up should have a handle or some kind of identifier or something that the calling process can read and store.
    So, what is that handle or identifier or whatever it is called? and how can I read it?

    2. If I have a variable containing that handle or whatever, how can I programmically determine whether the window associated with that handle (for example Notepad) is still open or not?

    3. And if it is open, how can I programmically close it or bring it to the front?
    Please advise.
    Thanks.

  2. #2
    PowerPoster
    Join Date
    Dec 2004
    Posts
    25,621

    Re: How to control an external application that my process spawns.

    if you can display your saved data in excel or word
    1. the file will be locked and can not be overwritten
    2, office applications can be automated (rather than shelled) from vb6, which should be able to give you full control over the application that has the file open

    if you really want to stick with notepad, you may be able to use:-
    shell and wait,
    or loop all processes to find which process has that file open and force close
    there is an example in codebank to find all open files from a specific drive and close them, which should be easily modified to just work with one or more files
    shell command can get a return value, which is the task id
    Remarks

    If the Shell function successfully executes the named file, it returns the task ID of the started program. The task ID is a unique number that identifies the running program. If the Shell function can't start the named program, an error occurs.

    Note By default, the Shell function runs other programs asynchronously. This means that a program started with Shell might not finish executing before the statements following the Shell function are executed.

    Security Note If you do not enclose the path and file specification in quotes, there is a security risk if the file name or a path node contains spaces. If the path node specification is not inside quotes, for example \Program Files and a program named Program.exe had been installed in C:\, for example by illicit tampering, Windows would execute it instead of MyFile.exe.
    i found a code here to get the window handle from task id
    http://www.vb-helper.com/howto_shell_get_hwnd.html
    i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
    Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next

    dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part

    come back and mark your original post as resolved if your problem is fixed
    pete

  3. #3
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    8,173

    Re: How to control an external application that my process spawns.

    This is one way, but it's a bit brutal. If you use it, you should notify your user somehow that their program will not proceed until they close that copy of Notepad.

    Code:
    
    Option Explicit
    '
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
    Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
    '
    
    
    Public Sub WaitForTermination(lProcessIdFromShell As Long)
        ' This procedure should be used with some caution. 
        ' The program will not be able to refresh its forms and controls 
        '   while waiting.  The best way to use this is to make sure that 
        '   the program has nothing visible before calling this procedure. 
        '
        ' The following command is the best way to use this procedure: 
        '            WaitForTermination Shell(  sTheShellCommandString  ) 
        Const SYNCHRONIZE As Long = &H100000
        Const INFINITE As Long = &HFFFF
        Dim lProcessHwnd As Long
        If lProcessIdFromShell = 0 Then ' Make sure there is a process to wait on. 
            Exit Sub
        End If
        lProcessHwnd = OpenProcess(SYNCHRONIZE, 0, lProcessIdFromShell)
        If lProcessHwnd = 0 Then ' Make sure we can open the process. 
            Exit Sub
        End If
        ' Wait for process to terminate. 
        WaitForSingleObject lProcessHwnd, INFINITE
        CloseHandle lProcessHwnd
    End Sub
    
    
    
    There may be another way by just monitoring the process ID, but I'd also want to check and make sure that the ID corresponded with Notepad and not something else.

    Regards,
    Elroy
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  4. #4
    PowerPoster
    Join Date
    Feb 2012
    Location
    West Virginia
    Posts
    14,095

    Re: How to control an external application that my process spawns.

    Seems like it would be easier to just add a form with a multiline textbox into your program and show the file using that. That is probably the way I would handle it.

  5. #5
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    8,173

    Re: How to control an external application that my process spawns.

    Here's a way you might like better. It doesn't hang your program to get it done:

    Code:
    
    Option Explicit
    '
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Long, ByVal lpWindowName As Long) As Long
    Private Declare Function GetParent Lib "user32" (ByVal hWnd As Long) As Long
    Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
    Private Declare Function GetWindow Lib "user32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
    Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long
    Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
    '
    
    Private Sub Form_Load()
        Dim CmdStr As String
        Dim TheFilePath As String
        Dim Pid As Long
    
    
        ' Also, you should surround your TheFilePath in quotes, and also the program you're shelling to. 
        TheFilePath = "c:\test.txt"
        CmdStr = """C:\Windows\System32\Notepad.exe"" " & """" & TheFilePath & """"
        Pid = Shell(CmdStr, vbNormalFocus)
    
    
        MsgBox PidIsNotepad(Pid)
    
        MsgBox "Now go close notepad.  We're going to check again."
    
        MsgBox PidIsNotepad(Pid)
    
        Unload Me
    
    End Sub
    '
    
    
    Public Function PidIsNotepad(target_pid As Long) As Boolean
        PidIsNotepad = InStr(WindowText(hWndFromInstance(target_pid)), "Notepad") <> 0
    End Function
    
    Public Function hWndFromInstance(target_pid As Long) As Long
        Const GW_HWNDNEXT = 2
        Dim test_hwnd As Long
        Dim test_pid As Long
        Dim test_thread_id As Long
        '
        test_hwnd = FindWindow(ByVal 0&, ByVal 0&)
        Do While test_hwnd <> 0
            If GetParent(test_hwnd) = 0 Then ' Top level windows only. 
                test_thread_id = GetWindowThreadProcessId(test_hwnd, test_pid)
                If test_pid = target_pid Then
                    hWndFromInstance = test_hwnd
                    Exit Function
                End If
            End If
            test_hwnd = GetWindow(test_hwnd, GW_HWNDNEXT)
        Loop
        ' Returns zero if not found. 
    End Function
    
    Public Function WindowText(hWndOfInterest As Long) As String
        Dim s As String
        Dim l As Long
        '
        l = GetWindowTextLength(hWndOfInterest)
        s = Space$(l + 1)
        l = GetWindowText(hWndOfInterest, s, Len(s))
        If InStr(s, Chr$(0)) Then s = Left$(s, InStr(s, Chr$(0)) - 1)
        WindowText = RTrim$(s)
    End Function
    
    
    
    Enjoy,
    Elroy

    EDIT1: Also, Notepad should be in your environment's path, so you shouldn't need to specify the path. This will help on different Windows OS versions and also on peculiar Windows installations. Just Shell """Notepad.exe""" ... should get it done.
    Last edited by Elroy; Sep 1st, 2016 at 10:47 AM.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  6. #6
    Smooth Moperator techgnome's Avatar
    Join Date
    May 2002
    Posts
    33,923

    Re: How to control an external application that my process spawns.

    Quote Originally Posted by DataMiser View Post
    Seems like it would be easier to just add a form with a multiline textbox into your program and show the file using that. That is probably the way I would handle it.
    I was thinking an RTB myself...

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  7. #7
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    8,173

    Re: How to control an external application that my process spawns.

    Yes actually, I must also agree with DataMiser and techgnome. If this is just information that you're showing to them on a one-time basis, and then discarding it, I'd also tend to just create a form with a TextBox or RichTextBox in it and show them the data. That way, you'd maintain complete control of things. You could even lock the textbox so they couldn't change/edit the data. I suppose printing would be a touch of work, but even that wouldn't be any big deal.

    Regards,
    Elroy
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  8. #8

    Thread Starter
    Fanatic Member
    Join Date
    Mar 2010
    Posts
    661

    Re: How to control an external application that my process spawns.

    Thanks to everybody in here for their help.
    I tested Elroy's code in post #5
    It pops up a file in Notepad and immediately shows a messagebox that says "False.
    Then after I close that Notepad, the last messagebox also says "False".
    In other words, always "False".
    I even thought maybe because I had some other (unrelated textfile) open in another instance of Notepad, that caused the problem, but my guess was not correct. I closed all files open in Notepad, then did a fresh run of this code, and still it always shows "False" both before and after I close Notepad.
    Any idea as to why?

    Also, I noticed that the Shell command returns a value. MSDN says https://msdn.microsoft.com/en-us/lib...(v=vs.60).aspx it is of type Double, but you have declared PId as Long. Why?
    Also if that PId is the task Id of the Notepad instance that the Shell command opens, isn't there any function for example any API or whatever that can check on that PId and returns true (when that Notepad instance is still open) or false (when that Notepad instance is closed)?
    How about any other function to force close that instance of Notepad?
    I guess there should be, but I couldn't find.
    The whole purpose of that Shell command's return value I guess is that you could later use it (for example to check whether it is still open or to force close it). Otherwise what good is that return value?
    Thanks

  9. #9
    PowerPoster
    Join Date
    Dec 2004
    Posts
    25,621

    Re: How to control an external application that my process spawns.

    Also if that PId is the task Id of the Notepad instance that the Shell command opens, isn't there any function for example any API or whatever that can check on that PId and returns true (when that Notepad instance is still open) or false (when that Notepad instance is closed)?
    How about any other function to force close that instance of Notepad?
    see post #2, once you get the hwnd from the taskid, you can use sendmessage with WM_CLOSE to close the window

    i tested the sample from the link and it worked correctly
    you could also use the setwindowtext API to just update the text in the existing notepad window

    i also tested some code from randy birch, that should do similar, either update the text or close the window, but it needed some modification to work correctly, but it would do so
    i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
    Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next

    dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part

    come back and mark your original post as resolved if your problem is fixed
    pete

  10. #10

    Thread Starter
    Fanatic Member
    Join Date
    Mar 2010
    Posts
    661

    Re: How to control an external application that my process spawns.

    Hi.
    Thanks for the help.
    I tried the sample code recommended in post #2, that is this code:
    http://www.vb-helper.com/howto_shell_get_hwnd.html
    I downloaded that sample program, ran it, and it works. It shows the PId, hWnd and the title.
    However, in that sample program, the FindWindow API has been declared this way:
    Code:
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (Optional ByVal lpClassName As String, Optional ByVal lpWindowName As String) As Long
    I cannot integrate it into my own program.
    The problem is that I already have another functionality in my program that depends on this FindWindow API being declared differently.
    In my program, the FindWindow API is declared like this:
    Code:
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Any, ByVal lpWindowName As String) As Long
    If I use this declaration in that sample program, the sample program does not run. It shows a runtime error "Argument not optional" on this line:
    Code:
         test_hwnd = FindWindow()
    And if I do the opposite thing, and use this sample program's FindWindow API declaration in my own program, then my own program does not work. It will run without a runtime error, but does not do what I expect it to do.
    Here is what my own program is expected to do:
    Code:
       s = "Test1.txt - NotePad"
       If FindWindow(0&, s) Then
         'if found make it the active window
          AppActivate s
       End If
    It is supposed to bring that Notepad window to the front, but the FindWindow doesn't find it.

    So, I am stuck with two different declarations of this FindWindow API function.
    One of them (but not the other) works in my own program and does not work in the above-mentioned sample program.
    And the other declaration works in the sample program but not in my own program.
    How can I make both these two things work in one program?
    Please advise.
    Thanks.

  11. #11
    PowerPoster
    Join Date
    Dec 2004
    Posts
    25,621

    Re: How to control an external application that my process spawns.

    just change the name of one of the declare statements, then make sure you call the correct one
    like
    Code:
    Private Declare Function myFindWindow Lib "user32" Alias "FindWindowA" (Optional ByVal lpClassName As String, Optional ByVal lpWindowName As String) As Long
    or change your code, like
    Code:
       s = "Test1.txt - NotePad"
       If FindWindow(, s) Then
         'if found make it the active window
          AppActivate s
       End If
    Last edited by westconn1; Sep 5th, 2016 at 05:40 AM.
    i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
    Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next

    dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part

    come back and mark your original post as resolved if your problem is fixed
    pete

  12. #12

    Thread Starter
    Fanatic Member
    Join Date
    Mar 2010
    Posts
    661

    Re: How to control an external application that my process spawns.

    Thanks a lot.
    I renamed one of the two API function declarations, and now both of them peacefully coexist.
    I thought there would be ONE and ONLY one correct declaration of an API function in VB6. Therefore I thought that out of the two declarations one of them was wrong (even though it mysteriously worked) and the other right. But, now I see that two different declarations of one same API function can be both present and work fine. That is a big surprise that I don't understand.
    But, that is beside the point.
    The main point is this:
    I renamed one of the two API function declarations, and now both my functionalities work perfectly.
    Additionally, I did some research and found that I can use a different approach for activating (bringing to the front) a window that is behind other windows. In the past I used an approach based on the Window's title:
    Code:
       s = "Test1.txt - NotePad"
       If FindWindow1(0&, s) Then
         'if found make it the active window
          AppActivate s
       End If
    Now, that I am equipped with the windows handle, I prefer to bring the window to the front by referring to that window's handle. I researched this and found this solution:
    Code:
       APIRetVal = SetForegroundWindow(task_hWnd)
       ErrNumDll = Err.LastDllError
    And this approach works perfectly (almost), but there are still two little problems with this:

    1. How can I programmically find out whether that window is open or not before attempting to bring it to the front?
    The above code brings to the front a window that is open. If the window is closed, the above code just issues a runtime error.
    The problem is not to just avoid the runtime error. That can be easily avoided, but what if I just want to know if the window is open or not without bringing it to the front?
    So, how can I programmically find out whether that window is open or not without attempting to bring it to the front?

    2. If the window is minimized in Taskbar, the above approach brings it to the front, but doesn't maximize it or restore it to its normal size. The window that is brought to the front by this method remains minimized in the taskbar. How can I restore that window to its normal size? And how can I programmically find out whether I have to do this or not without attempting to do it? In other words how can I programmically find out that the window is minimized in taskbar or not?
    Please advise.
    Thanks.

  13. #13
    PowerPoster
    Join Date
    Dec 2004
    Posts
    25,621

    Re: How to control an external application that my process spawns.

    How can I programmically find out whether that window is open
    use the same code to find the window handle immediately before trying to setforegroundwindow, if the windowhandle is 0 then no window to bring to front

    you should be able to do all in #2 by using the handle to the window, showwindow API allows you to set the windowstate SW_SHOWNORMAL should work in place of setforeground window
    i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
    Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next

    dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part

    come back and mark your original post as resolved if your problem is fixed
    pete

  14. #14
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    8,173

    Re: How to control an external application that my process spawns.

    Hmmm, I'm just getting back. Strange that my code in post #5 worked differently for you than it did for me. I just tested again and I get "True" and then "False".

    But using the ProgramID returned from Shell, you can certainly determine if that program is still running or not. And, from a quick read of what westconn1 has provided, you can certainly Show, Hide, Minimize, Normalize, or Maximize it if we're truly talking about Notepad.

    However, with other more complex programs, things may not be so simple. For instance, many programs may show a modal form over a non-modal form (or possibly nested modal forms). When a program has many open forms, you can't necessarily just pick one form to show to the user.

    Regards,
    Elroy
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  15. #15
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    678

    Re: How to control an external application that my process spawns.

    Quote Originally Posted by IliaPreston
    Also, I noticed that the Shell command returns a value. MSDN says https://msdn.microsoft.com/en-us/lib...(v=vs.60).aspx it is of type Double, but you have declared PId as Long. Why?
    You are right. It returns 'Double'. Situations when System assigns PID bigger than 2147483647 are rare. But it's possible, and if one happens you'll get Overflow. So, need to convert. Example is below.
    Quote Originally Posted by IliaPreston
    if I open a text file in Notepad, does that instance of Notepad have a handle that my program can programmically read and store in a variable?
    Quote Originally Posted by Elroy
    MsgBox PidIsNotepad(Pid)
    I think, for reliability reason, it is better to obtain and save process' handle right after creating a process instead of PID because old PID can be assigned to another process (rare chance, but it can).
    Opened process' handle cannot be reused by system even if process was closed in usual way.
    Quote Originally Posted by IliaPreston
    2. If I have a variable containing that handle or whatever, how can I programmically determine whether the window associated with that handle (for example Notepad) is still open or not?
    You can use GetWindowThreadProcessId API to obtain Process ID your window handle associated with.
    Next, compare this PID with PID you previously saved when you launched notepad by VB 'Shell' function. If they are identical, your window is still alive.

    Also, you should firstly check your saved PID, whether your process still exist.
    You can do so by passing process handle (if you opened it) to GetExitCodeProcess API. lpExitCode should be STILL_ACTIVE.

    Note 1: If you plan to use another way (like examples above my post with FindWindow) and if at the same time you plan to save windows handles right after process created (not by button click),
    you should wait until created process complete its initialization. Otherwise, your program can call FindWindow earlier than the window will be created and it will find nothing. Good way to do such waiting is using WaitForInputIdle API.

    Note 2: Also, consider the way when user will do with notepad things that you did not foresee, e.g. he can open another file using the same instance of notepad -), so, at least, you have to ensure window title begins with 'File name' you initially pass.

    Note 3: Make attention, that FindWindow can be used only if you know exact window name and/or class. A window title of Notepad is differ between XP and Vista+, also in different OS localizations.

    To do enumeration by partial title name, you can use EnumWindows API. It pass each window handle of desktop to callback function. Next, you can get PID your window handle belongs to and compare it with known PID that you saved earlier when you launched 'Shell'.
    If PIDs are identical, now you have one of window handles associated with your spawned process.
    EnumWindows is more reliable method than GetWindow presented earlier in the topic (see remarks in MSDN EnumWindows description).

    Quote Originally Posted by IliaPreston
    How can I restore that window to its normal size?
    You have to use ShowWindow API + flag SW_RESTORE.

    Quote Originally Posted by IliaPreston
    If the window is closed, the above code just issues a runtime error.
    You can use SetForegroundWindow API instead of VB AppActivate. It will not produce runtime error and you'll be able to check is it completed successfully by value returned.

    Quote Originally Posted by IliaPreston
    how can I programmically find out that the window is minimized in taskbar or not?
    To determine minimized / maximized SW_ state, you can use GetWindowPlacement API. It fill struct. with showCmd field set to SW_MINIMIZE.
    Also, you can use simple IsIconic API.

    Quote Originally Posted by IliaPreston
    If FindWindow(0&, s) Then
    All 2 parameters are strings, so you should not mix types. It's ANSI version of function, so prototype of function should have 'string' types as westconn1 show.
    If you would like to pass NULL, you should specify vbNullString constant.
    Also, you never find this sample of text in Notepad's title on Vista+, because notepad on XP uses - (minus), and on Vista and newer he uses — (dash):

    Code:
       s = "test1.txt — NotePad"
       Hwnd = FindWindow(vbNullString, s)
       
       If Hwnd <> 0 Then
           'if found make it the active window
           AppActivate s
       End If
    Quote Originally Posted by IliaPreston
    How about any other function to force close that instance of Notepad?
    Some programs doesn't respond to WM_CLOSE message. In this case you can send WM_QUIT to force terminate window (without saving changes). Or you can force close process (without saving changes) by using TerminateProcess API passing process handle opened with PROCESS_TERMINATE access rights.
    If we talk specifically about 'Notepad', normally, you should use WM_CLOSE. If you do it, notepad will prompt user if he wants to save changes.
    There is a difference between how you send this message: by SendMessage API or by PostMessage API.
    If you use SendMessage API, your program will freeze until user click any button in prompt window of notepad.
    If you use PostMessage API, your program returns control immediately after sending the message and will not wait.
    Malware analyst, VirusNet developer, HiJackThis Fork author || my CodeBank works

  16. #16
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    678

    Re: How to control an external application that my process spawns.

    Regarding my recomendations, here is an example of what you described in 1-st post and further:

    Form:

    Code:
    
    Option Explicit
    
    Private Declare Function GetVersionEx Lib "kernel32.dll" Alias "GetVersionExW" (lpVersionInformation As Any) As Long
    
    Private Sub Form_Load()
    
        'global variables used: 
        'gProcHandle() 
        'gProcPID() 
        'gWndHandle() 
    
        InitVariables
    
        'test files 
        gPath(0) = Environ("Userprofile") & "\desktop\" & "test1.txt"
        gPath(1) = Environ("Userprofile") & "\desktop\" & "test2.txt"
        gPath(2) = Environ("Userprofile") & "\desktop\" & "test3.txt"
    
        ReDim gProcHandle(UBound(gPath))
        ReDim gProcPID(UBound(gPath))
        ReDim gWndHandle(UBound(gPath))
    End Sub
    
    Sub InitVariables()
        'global variables used: 
        'bIsWinVistaOrLater 
    
        Dim inf(68) As Long
        inf(0) = 276: GetVersionEx inf(0): bIsWinVistaOrLater = (inf(1) >= 6)
    End Sub
    
    Private Sub cmdCommand1_Click()
        OpenFiles
    End Sub
    
    Private Sub cmdCommand2_Click()
        ShowWindows
    End Sub
    
    Private Sub cmdCommand3_Click()
        CloseNotepads
    End Sub
    
    
    Module:

    Code:
    
    Option Explicit
    
    Private Declare Function OpenProcess Lib "kernel32.dll" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
    Private Declare Function GetExitCodeProcess Lib "kernel32.dll" (ByVal hProcess As Long, lpExitCode As Long) As Long
    Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
    Private Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
    Private Declare Function IsWindowVisible Lib "user32" (ByVal hWnd As Long) As Long
    Private Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As Long, ByVal dwMilliseconds As Long) As Long
    Private Declare Function SetForegroundWindow Lib "user32.dll" (ByVal hWnd As Long) As Long
    Private Declare Function SetWindowPos Lib "user32.dll" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
    Private Declare Function ShowWindow Lib "user32.dll" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
    Private Declare Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
    Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
    Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long
    Private Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Private Declare Function SendMessageW Lib "user32" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
    Private Declare Function TerminateProcess Lib "kernel32.dll" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    
    Private Const STILL_ACTIVE      As Long = &H103&
    Private Const SW_RESTORE        As Long = 9&
    Private Const HWND_TOP          As Long = 0&
    Private Const SWP_NOSIZE        As Long = 1&
    Private Const SWP_NOMOVE        As Long = 2&
    Private Const WM_CLOSE          As Long = &H10&
    Private Const WM_QUIT           As Long = &H12&
    Private Const PROCESS_TERMINATE As Long = &H1&
    Private Const PROCESS_QUERY_INFORMATION As Long = 1024&
    Private Const PROCESS_QUERY_LIMITED_INFORMATION As Long = &H1000&
    
    Public bIsWinVistaOrLater As Boolean
    
    Public gPath(2)        As String
    Public gProcHandle()   As Long
    Public gProcPID()      As Long
    Public gWndHandle()    As Long
    
    Public Sub OpenFiles()  'open all files 
        Dim ff As Integer
        Dim i As Long
    
        For i = 0 To UBound(gPath)
            'Create text file 
            ff = FreeFile(): Open gPath(i) For Output As #ff: Close #ff
    
            'launch notepad, open txt-file and get process handle & PID 
            ShellEx gPath(i), gProcPID(i), gProcHandle(i)
        Next
    
        'we can get and save window handles now or any time after. 
        'Here an example, how to get it now (right after creating the process): 
    
        'wait until all process complete initialization 
        For i = 0 To UBound(gPath)
            WaitForInputIdle gProcHandle(i), 1000& ' give them 1 sec. timeout 
        Next
    
        'enumerate all windows and save a window handles we found to global gWndHandle() array 
        EnumWindows AddressOf EnumWnd, 0&
    End Sub
    
    ' launch notepad & obtain handle + PID 
    Function ShellEx(sPath As String, out_PID As Long, out_Handle As Long) As Boolean
        On Error GoTo ErrorHandler
    
        out_PID = CDWordToLong(Shell("notepad.exe" & " " & """" & sPath & """", vbNormalNoFocus))
    
        If out_PID <> 0 Then
            out_Handle = OpenProcess(IIf(bIsWinVistaOrLater, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_QUERY_INFORMATION), False, out_PID)
            ShellEx = True
        End If
        Exit Function
    ErrorHandler:
        Debug.Print "Error: " & Err.Number & ". " & Err.Description
    End Function
    
    Function CDWordToLong(DWord As Double) As Long
        If DWord > 2147483647 Then
            CDWordToLong = DWord - 4294967296#
        Else
            CDWordToLong = DWord
        End If
    End Function
    
    
    Malware analyst, VirusNet developer, HiJackThis Fork author || my CodeBank works

  17. #17
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    678

    Re: How to control an external application that my process spawns.

    Continue...

    Code:
    
    Public Sub ShowWindows()    'show all windows 
        Dim i As Long
        Dim lExit As Long
        Dim ProcCnt As Long
        Dim ProcessID As Long
    
        'global variables used: 
        'gProcHandle() 
        'gProcPID() 
        'gWndHandle() 
    
        For i = 0 To UBound(gPath)
            GetExitCodeProcess gProcHandle(i), lExit
    
            If lExit = STILL_ACTIVE Then
    
                ProcCnt = ProcCnt + 1
    
                GetWindowThreadProcessId gWndHandle(i), ProcessID
    
                ' if this window handle still belongs to tracked process 
                If ProcessID = gProcPID(i) Then
    
                    ShowWindow gWndHandle(i), SW_RESTORE
    
                    SetWindowPos gWndHandle(i), HWND_TOP, 0&, 0&, 0&, 0&, SWP_NOSIZE Or SWP_NOMOVE   'always on top 
    
                    SetForegroundWindow gWndHandle(i)
                End If
    
            Else
                CloseHandle gProcHandle(i)
            End If
        Next
    
        If ProcCnt = 0 Then
            MsgBox "All processes have been closed. Nothing to show."
        End If
    End Sub
    
    Private Function EnumWnd(ByVal hWnd As Long, ByVal lParam As Long) As Boolean
        Dim i               As Long
        Dim ProcessID       As Long
        Dim sTitle          As String
        Dim sExample        As String
        Static TotalFound   As Long
    
        'global variables used: 
        'gProcPID() 
        'gWndHandle() 
    
        EnumWnd = True ' to continue enumeration 
    
        ' get PID by window handle 
        Call GetWindowThreadProcessId(hWnd, ProcessID)
    
        For i = 0 To UBound(gPath)
    
            'if it's one of our PID 
            If ProcessID = gProcPID(i) Then
    
                If IsWindowVisible(hWnd) Then
                    'should compare with title 
    
                    sTitle = GetWindowTitle(hWnd)
    
                    ' find "test1.txt - Notepad" (without "- Notepad") 
                    sExample = GetFileName(gPath(i)) & " "
    
                    If InStr(1, sTitle, sExample, vbTextCompare) = 1 Then
                        gWndHandle(i) = hWnd                ' found it 
                        TotalFound = TotalFound + 1
    
                        If TotalFound = UBound(gPath) + 1 Then ' has found window for all PIDs ? 
                            TotalFound = 0
                            EnumWnd = False                 ' abort enumeration 
                        End If
                    End If
                End If
            End If
        Next
    End Function
    
    Function GetFileName(sPath As String) As String
        Dim pos As Long
        pos = InStrRev(sPath, "\")
        If pos <> 0 Then
            GetFileName = Mid$(sPath, pos + 1)
        Else
            GetFileName = sPath
        End If
    End Function
    
    Function GetWindowTitle(hWnd As Long) As String
        Dim CapLength As Long
        Dim Cap As String
    
        CapLength = GetWindowTextLength(hWnd)
        If CapLength > 0 Then
            Cap = String$(CapLength, 0)
            GetWindowText hWnd, Cap, CapLength + 1
        End If
        GetWindowTitle = Cap
    End Function
    
    Public Sub CloseNotepads()
        Dim i As Long
        Dim lExit As Long
        Dim ProcCnt As Long
        Dim ProcessID As Long
        Dim hProcTerminate As Long
        Dim TotalMS As Long
    
        'global variables used: 
        'gProcHandle() 
        'gProcPID() 
        'gWndHandle() 
    
        For i = 0 To UBound(gPath)
            GetExitCodeProcess gProcHandle(i), lExit
    
            If lExit = STILL_ACTIVE Then
    
                ProcCnt = ProcCnt + 1
    
                GetWindowThreadProcessId gWndHandle(i), ProcessID
    
                ' if this window handle still belongs to tracked process 
                If ProcessID = gProcPID(i) Then
    
                    If frmMain.chkCheck1.Value = 0 Then ' no force mode 
    
                        'Sync. mode 
                        'SendMessageW gWndHandle(i), WM_CLOSE, 0&, 0& 
    
                        'Async. mode 
                        PostMessage gWndHandle(i), WM_CLOSE, 0&, 0&
    
                    Else
    
                        PostMessage gWndHandle(i), WM_QUIT, 0&, 0&
    
                        'timeout 500 ms. to allow application to process the message 
                        Do
                            DoEvents
                            Sleep 50
                            TotalMS = TotalMS + 50
                            GetWindowThreadProcessId gWndHandle(i), ProcessID
                        Loop Until TotalMS >= 500 Or ProcessID <> gProcPID(i)
    
                        ' timeout exceeded and window still exists -> terminate process 
                        If ProcessID = gProcPID(i) Then
                            hProcTerminate = OpenProcess(PROCESS_TERMINATE, False, gProcPID(i))
                            If hProcTerminate <> 0 Then
                                TerminateProcess hProcTerminate, 0&
                                CloseHandle hProcTerminate
                            End If
                        End If
                    End If
    
                    CloseHandle gProcHandle(i)
                End If
            Else
                CloseHandle gProcHandle(i)
            End If
        Next
    
        If ProcCnt = 0 Then
            MsgBox "No opened processes exist."
        End If
    End Sub
    
    
    Demo app. attached.

    EDIT. CloseHandle gWndHandle(i) replaced into gProcHandle(i). Made a mistake.

    Good luck,
    Alex.
    Attached Files Attached Files
    Last edited by Dragokas; Sep 7th, 2016 at 03:12 AM. Reason: fix
    Malware analyst, VirusNet developer, HiJackThis Fork author || my CodeBank works

  18. #18

    Thread Starter
    Fanatic Member
    Join Date
    Mar 2010
    Posts
    661

    Re: How to control an external application that my process spawns.

    Thanks for all the great help and advice.
    I downloaded and tried the project "Windows_Restore2" provided in posts #16 and #17.
    There are a number of issues with it:

    1. If I click on "Open Files", it opens the three files, but doesn't appropriately bring them to the front. Only one of the three is kind-of brought to the front. By "kind-of" I mean the window titlebar is white as opposed to the normal color that a front window should have (in my computer brown). Please note that I am running Windows 10, and in Windows 10, any window that is not on the front, has its titlebar white.
    When I then click on "Show All Windows" then the three windows are brought to the front.
    I can easily fix this by changing "Sub cmdCommand1_Click" and leaving "Sub cmdCommand2_Click" intact, as follows:
    Code:
    Private Sub cmdCommand1_Click()
        OpenFiles
        ShowWindows
    End Sub
    
    Private Sub cmdCommand2_Click()
        ShowWindows
    End Sub
    2. If I click on "Open Files", it opens the three files, but then if I one more time click on that same button "Open Files", it AGAIN opens the three files, resulting in SIX Notepad files being open on the screen!
    This is definitely wrong. I don't know how to fix it.

    3. The way you are calling the Shell function like this:
    Code:
    out_PID = CDWordToLong(Shell("notepad.exe" & " " & """" & sPath & """", vbNormalNoFocus))
    is good in that you are enclosing the file path by double quotes.
    But I guess (correct me if I am wrong) that even the notepad.exe itself should be enclosed by double-quotes as well. I made that change as follows and it works:
    Code:
    out_PID = CDWordToLong(Shell("""notepad.exe""" & " " & """" & sPath & """", vbNormalNoFocus))
    Do you agree with this change or do you see any problem with it?

    4. In the following code:
    Code:
            If lExit = STILL_ACTIVE Then
                
                ProcCnt = ProcCnt + 1
                
                GetWindowThreadProcessId gWndHandle(i), ProcessID
                
                ' if this window handle still belongs to tracked process
                If ProcessID = gProcPID(i) Then
                
                    ShowWindow gWndHandle(i), SW_RESTORE
                    
                    SetWindowPos gWndHandle(i), HWND_TOP, 0&, 0&, 0&, 0&, SWP_NOSIZE Or SWP_NOMOVE   'always on top
                    
                    SetForegroundWindow gWndHandle(i)
                End If
    
            Else
                CloseHandle gProcHandle(i)
            End If
    The way you increment ProcCnt, is inside the If lExit = STILL_ACTIVE Then statement as it should be, but I guess (correct me if I am wrong) that this incrementation should be also inside the second (inner) If statement If ProcessID = gProcPID(i) Then. Shouldn't it?

    5. In the OpenFiles sub, you loop through the files and open them, then you once again loop through them and do "WaitForInputIdle". Why not have only one loop in which to open a file, and then immediately do "WaitForInputIdle", before proceeding to the next run of the loop?

    6. Why do you give each file one second timeout to open? If the text file is huge, it could take several seconds. Why not wait indefinitely until the file open is complete?

    7. Can this whole approach be used for any other application (other than Notepad)?
    The reason why I am asking this is that it would be more helpful if I genericized it to use for any application (not just Notepad) that my program spawns.
    One way to genericize it would be to pass the commandline to the ShellEx function as follows:
    Code:
    Function ShellEx(sCmdLine As String, out_PID As Long, out_Handle As Long) As Boolean
        On Error GoTo ErrorHandler
        
        out_PID = CDWordToLong(Shell("""notepad.exe""" & " " & """" & sPath & """", vbNormalNoFocus))
    and calling it like this:
    Code:
        CmdLine = """notepad.exe""" & " " & """" & gPath(i) & """"
        ShellEx CmdLine, gProcPID(i), gProcHandle(i)
    In this way, I can use this code for spawning any application, and not just Notepad.
    But, do you think that this is ok?
    Or do you see any potential problem with my generic approach?
    Please advise.
    Thanks

  19. #19
    PowerPoster
    Join Date
    Dec 2004
    Posts
    25,621

    Re: How to control an external application that my process spawns.

    that it would be more helpful if I genericized it to use for any application
    you should look at using the shellexecute or shellexecuteex API in this case

    Do you agree with this change or do you see any problem with it?
    for notepad.exe double quotes are not required, but will not be a problem, if the exe file name (or path) could contain spaces then the double quotes are essential, especially if you use a variable for the exe to shell
    shellexecute API does not require double quotes for either the command or argument

    i will leave the other questions for alex when he returns
    i do my best to test code works before i post it, but sometimes am unable to do so for some reason, and usually say so if this is the case.
    Note code snippets posted are just that and do not include error handling that is required in real world applications, but avoid On Error Resume Next

    dim all variables as required as often i have done so elsewhere in my code but only posted the relevant part

    come back and mark your original post as resolved if your problem is fixed
    pete

  20. #20
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    678

    Re: How to control an external application that my process spawns.

    1. If I click on "Open Files", it opens the three files, but doesn't appropriately bring them to the front.
    I did it specifically, that windows do not interfere, for test reason. Just change vbNormalNoFocus to vbNormalFocus. I don't know all technical task, so it's only code example.
    2. If I click on "Open Files", it opens the three files, but then if I one more time click on that same button "Open Files", it AGAIN opens the three files, resulting in SIX Notepad files being open on the screen!
    This is definitely wrong. I don't know how to fix it.
    I don't know what you expect when clicking second time "Open files". It wasn't designed for this purpose.
    If you would like to open same files without those already opened you need to track this moment somewhere in code.
    I just showed you example how to store all this staff in arrays and how to do the main tasks you describe.
    Describe more specifically, if you are unable to modify the code for your purposes and would like further help.

    But I guess (correct me if I am wrong) that even the notepad.exe itself should be enclosed by double-quotes as well.
    No problem, but in this case it is superfluous, because double quotes need to specify only in cases where you have a space character(s) in path / filename.

    The way you increment ProcCnt, is inside the If lExit = STILL_ACTIVE Then statement as it should be, but I guess (correct me if I am wrong) that this incrementation should be also inside the second (inner) If statement If ProcessID = gProcPID(i) Then. Shouldn't it?
    Maybe, you don't understand the meaning of 'IF ProcessID = gProcPID(i)'. ProcessID var. holds PID of process of enumerated window. Process can have more than 1 window or no windows (if we say about 'notepad', it's not this case, he always has at least 1 window, but my code designed for versatility).
    So, if you place 'ProcCnt = ProcCnt + 1' inside next 'IF' and imagine that if process will have no windows, ProcCnt will not increment and you'll get message "All processes have been closed. Nothing to show.", but process is exist.

    5. In the OpenFiles sub, you loop through the files and open them, then you once again loop through them and do "WaitForInputIdle". Why not have only one loop in which to open a file, and then immediately do "WaitForInputIdle", before proceeding to the next run of the loop?
    Because, current behavior will be faster (with such program as 'Notepad' you willn't notice it, but the difference may be significant for other programs).
    Your propose: open 1-st file, wait until it complete its initialization. Only after that, open next file.
    Current code: open all files immediately, without delay. It gives a chance that while the other files will be opened, part of them already completed their initialization, so in my second For...Next loop it's possible that 'WaitForInputIdle' will wait only last opened notepads' processes.

    6. Why do you give each file one second timeout to open? If the text file is huge, it could take several seconds. Why not wait indefinitely until the file open is complete?
    If you find a program that you open a reliable, of course you can specify 'INFINITE' flag. But if that program will hang for some reason and has not finished initialization of its window, your program will hang too, forever, so specifying the maximum time that you think will be enough to open any file will be more reliable, on my opinion.
    7. Can this whole approach be used for any other application (other than Notepad)?
    You are welcome. If you like it, be free to use it. Many programs support opening file by command line, like example with notepad. Sometimes it is necessary to indicate special keys. But it is possible to find them in registry HKCR key. Anyway you can use this code to open program and open the file in it later in any other way.

    I don't know complete technical task, so I can't answer about this or other cases.
    But, if I need something like this task with notepad I would follow advice in Post # 4.

    My whole approach may be used to run other programs with modification. Because it also filter title of program by this mask "FileName e.t.c." (for 'Notapad', it's 'FileName - Notepad'). So this approach allow not to close program if you clicked in this program 'Open' -> some other text file. You can try, and you will see. But it will not help if you open same name of file in another folder. If you don't need such protection, just remove this checking 'If InStr(1, sTitle, sExample, vbTextCompare) = 1'.
    It also will not help if program do not show opened file name in title. And it's, of course, can not be considered as a reliable method of determining whether a file is still open in the program. There are some programs that use host process to open several files (like MS Excel: many opened files, many windows, but 1 process). This code also need changes for this case.

    I changed behavior a little bit, so, now if you click button "Open files" twice it will check is file opened, if not, it will open this particular file. If all files already opened, it will do nothing.
    Code:
    
    Public Sub OpenFiles()  'open all files 
        Dim i As Long
        Dim lExit As Long
        Dim sTitle As String
        Dim ProcessID As Long
        Dim sExample As String
        Dim NotOpened As Boolean
    
        For i = 0 To UBound(gPath)
            'check if process still run (actually, also need to check if gProcHandle(i) = 0, but we omit it, not scary -) 
            GetExitCodeProcess gProcHandle(i), lExit
    
            NotOpened = False
    
            If lExit = STILL_ACTIVE Then
    
                'get PID of window 
                GetWindowThreadProcessId gWndHandle(i), ProcessID
    
                ' if process has no window that we track (we saved its handle earlier) 
                If ProcessID = gProcPID(i) Then
    
                    ' also, we'll check has file still opened in this process by comparing the caption in title of window 
                    sTitle = GetWindowTitle(gWndHandle(i))
    
                    ' find "test1.txt - Notepad" (without "- Notepad") 
                    sExample = GetFileName(gPath(i)) & " "
    
                    If InStr(1, sTitle, sExample, vbTextCompare) <> 1 Then
    
                        NotOpened = True ' another file opened 
                    End If
                Else
                    NotOpened = True ' window is closed 
                End If
            Else
                NotOpened = True ' process is dead 
            End If
    
            If NotOpened Then
                'launch notepad, open txt-file and get process handle & PID 
                ShellEx gPath(i), gProcPID(i), gProcHandle(i)
            End If
        Next
    
        'we can get and save window handles now or any time after. 
        'Here an example, how to get it now (right after creating the process): 
    
        'wait until all process complete initialization 
        For i = 0 To UBound(gPath)
            WaitForInputIdle gProcHandle(i), 1000& ' give them 1 sec. timeout 
        Next
    
        'enumerate all windows and save a window handles we found to global gWndHandle() array 
        EnumWindows AddressOf EnumWnd, 0&
    End Sub
    
    Attached Files Attached Files
    Malware analyst, VirusNet developer, HiJackThis Fork author || my CodeBank works

  21. #21

  22. #22
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    678

    Re: How to control an external application that my process spawns.

    DWORD - is unsigned integer.
    I don't know exactly how runtime convert it to VB Double.
    If without sign addition, it will become a real Double.
    Otherwise, why would MS made a return type Double.
    Malware analyst, VirusNet developer, HiJackThis Fork author || my CodeBank works

  23. #23
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,203

    Re: How to control an external application that my process spawns.

    Quote Originally Posted by Dragokas View Post
    DWORD - is unsigned integer.
    I don't know exactly how runtime convert it to VB Double.
    If without sign addition, it will become a real Double.
    Otherwise, why would MS made a return type Double.
    You perhaps didn't understand. As you saw for conversion to Double runtime uses FILD instruction that takes SIGNED integer value and converts one to float. Therefore it doesn't matter. If Shell had returned a Long value it would have had a same result.
    I was already discussing about it without a result.

  24. #24
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    678

    Re: How to control an external application that my process spawns.

    FILD instruction that takes SIGNED integer value
    Now it's clear. Thank you.
    In this case, 'Double' doesn't require explicit conversion with the sign addition.
    Malware analyst, VirusNet developer, HiJackThis Fork author || my CodeBank works

  25. #25

    Thread Starter
    Fanatic Member
    Join Date
    Mar 2010
    Posts
    661

    Re: How to control an external application that my process spawns.

    Thanks a lot for the great help and advice.
    I downloaded the new version of the Windows_Restore project, that is Windows_Restore3.zip (attached to post #20)
    I tested it and it works without a hitch.
    It is excellent. Thanks again.
    However, I am trying to modify it a little bit to fit my purpose, and there is some confusion, and I am not sure how to do it.
    What I am trying to do is to add new functions to open, show and close one (only one) file.
    Existing functions can also remain, as they may one day be helpful, but now, I have to be able to do this for a single file at a time.
    The reason is that all text files that my application is going to open are not known in advance. A run of the process may open 3 text files in 3 Notepad calls. Another run of the process may open 5 or 6 or 2 of them (depending on the process parameters). And all of these files are not opened at the end of the process. Some at the end, some in the middle, etc.
    So, what I have to do is to manage the array of these text files in my program (four separate arrays: 1. Array of file paths, 2. Array of Prochandle, 3. Array of PID, 4. Array of WndHandle).
    This whole thing requires that I be able to do the three functionalities of this project "Windows_Restore3"
    1. Open files
    2. Show all windows
    3. Close all notepads
    for a single file:
    1. Open a single file
    2. Show a single window
    3. Close a single notepad
    Once I can do the above three functionalities on a single file, the rest (managing four arrays for my text files and other mechanics) is just simple programming logic that I can do easily (dynamically adding an item to all these four arrays when the need arises to open a new text file in Notepad, calling the function open_A_single_file, etc, etc.)
    So, I am trying to add new functions to Windows_Restore3 to deal with a single text file, but there are some confusions.
    For example based on the function OpenFiles, I am writing a similar function Open_A_Single_File.
    At first it is easy: I add 4 parameters to this function (instead of using the 4 global variables) and remove the two loops in it:
    Code:
    Public Sub Open_A_Single_File(ByRef Path_OneFile As String, ByRef ProcHandle_OneFile As Long, ByRef ProcPID_OneFile As Long, ByRef WndHandle_OneFile As Long)  'open just one file
        Dim i As Long
        Dim lExit As Long
        Dim sTitle As String
        Dim ProcessID As Long
        Dim sExample As String
        Dim NotOpened As Boolean
        
            'check if process still run (actually, also need to check if ProcHandle_OneFile = 0, but we omit it, not scary -)
            GetExitCodeProcess ProcHandle_OneFile, lExit
            
            NotOpened = False
        
            If lExit = STILL_ACTIVE Then
                
                'get PID of window
                GetWindowThreadProcessId ProcPID_OneFile, ProcessID
                
                ' if process has no window that we track (we saved its handle earlier)
                If ProcessID = ProcPID_OneFile Then
        
                    ' also, we'll check has file still opened in this process by comparing the caption in title of window
                    sTitle = GetWindowTitle(ProcPID_OneFile)
                
                    ' find "test1.txt - Notepad" (without "- Notepad")
                    sExample = GetFileName(Path_OneFile) & " "
                    
                    If InStr(1, sTitle, sExample, vbTextCompare) <> 1 Then
                        
                        NotOpened = True ' another file opened
                    End If
                Else
                    NotOpened = True ' window is closed
                End If
            Else
                NotOpened = True ' process is dead
            End If
            
            If NotOpened Then
                'launch notepad, open txt-file and get process handle & PID
                ShellEx Path_OneFile, ProcPID_OneFile, ProcHandle_OneFile
            End If
        
        'we can get and save window handles now or any time after.
        'Here an example, how to get it now (right after creating the process):
        
        'wait until all process complete initialization
        WaitForInputIdle ProcHandle_OneFile, 1000&     ' give them 1 sec. timeout
        
        'enumerate all windows and save a window handles we found to global ProcPID_OneFile() array
        EnumWindows AddressOf EnumWnd, 0&
    End Sub
    But, then the last line
    Code:
        'enumerate all windows and save a window handles we found to global ProcPID_OneFile() array
        EnumWindows AddressOf EnumWnd, 0&
    is confusing. Should I change it or leave it as is?
    Aside from calling it, should I also have a copy of the definition of this function:
    Code:
    Private Function EnumWnd(ByVal hWnd As Long, ByVal lParam As Long) As Boolean
        Dim i               As Long
        Dim ProcessID       As Long
        Dim sTitle          As String
        Dim sExample        As String
        Static TotalFound   As Long
        
        'global variables used:
        'gProcPID()
        'gWndHandle()
        
        EnumWnd = True ' to continue enumeration
        
        ' get PID by window handle
        Call GetWindowThreadProcessId(hWnd, ProcessID)
        
        For i = 0 To UBound(gPath)
        
            'if it's one of our PID
            If ProcessID = gProcPID(i) Then
        
                If IsWindowVisible(hWnd) Then
                    'should compare with title
            
                    sTitle = GetWindowTitle(hWnd)
                
                    ' find "test1.txt - Notepad" (without "- Notepad")
                    sExample = GetFileName(gPath(i)) & " "
                    
                    If InStr(1, sTitle, sExample, vbTextCompare) = 1 Then
                        gWndHandle(i) = hWnd                ' found it
                        TotalFound = TotalFound + 1
                        
                        If TotalFound = UBound(gPath) + 1 Then ' has found window for all PIDs ?
                            TotalFound = 0
                            EnumWnd = False                 ' abort enumeration
                        End If
                    End If
                End If
            End If
        Next
    End Function
    as well? For example like this:
    Code:
    Private Function EnumWnd_A_Single_File(ByVal hWnd As Long, ByVal lParam As Long) As Boolean
    Please note that the reason I am asking this is that this function also loops through "For i = 0 To UBound(gPath)".
    So, now that I am trying to deal with only a single file, how do I change this?
    Please advise.
    Thanks again.

  26. #26
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    678

    Re: How to control an external application that my process spawns.

    "Main" array here is gPath - list of paths to .txt.
    If you need to add extra file, you just 'redim' this and other global arrays and add new path to gPath.
    To remove file from processing, just do ' gPath(x) = "" as well as other arrays '. Also, add checking of 0 length of string to 'Function ShellEx'. You can write simple function-wrapper to add/delete path.

    ShellEx returns PID and Process handle.
    EnumWnd updates List of Window handles (gWndHandle) based on filter by PID and filename in title of window.

    When new process started, ShellEx and EnumWnd called. No need to touch them.

    I rewrote this into class with a demo app. and you can easily manipulate windows for your case now.
    All info about windows now stored in array Win() of UDT:

    Code:
    
    Private Type WINDOW_PROPERTIES
        FilePath    As String   ' Path to file (e.g. those opened by Notepad) 
        FileName    As String   ' Internal. The same, without directory (will be used in filter by title of window) 
        ProcHandle  As Long     ' Handle of process this window associated with 
        WndHandle   As Long     ' Handle of this window 
        PID         As Long     ' PID of process this window associated with 
    End Type
    
    You can find description of class members commented in source as well as usage examples in form module.
    There are:
    - AddFile(sFullPath As String, Optional bCreateFile As Boolean)
    - RemoveFile(sFullPath As String)
    - OpenFiles(Optional sFilePath As String, Optional index As Long)
    - ShowWindows(Optional sFilePath As String, Optional index As Long)
    - CloseWindows(Optional sFilePath As String, Optional index As Long)
    - ShellEx(sPath As String, out_PID As Long, out_Handle As Long)
    Properties:
    - ForceQuit
    - FilterByTitle
    - FilesInProcessing
    Attached Images Attached Images  
    Attached Files Attached Files
    Malware analyst, VirusNet developer, HiJackThis Fork author || my CodeBank works

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width