Link to home
Start Free TrialLog in
Avatar of learning_t0_pr0gram
learning_t0_pr0gram

asked on

Make window child to my MDI

Is it possible to make a window (outside of my application) a child to my own MDIForm?  Like if I have:

Window = FindWindow("windowClass", vbNullString)

Could I turn Window into a child to my MDI Form?
Avatar of learning_t0_pr0gram
learning_t0_pr0gram

ASKER

And instead of starting a new Question, could someone tell me what Seek does?

Seek #1, 25

I'll give 10 pts to whoever tells me what it does.
SOLUTION
Avatar of Harisha M G
Harisha M G
Flag of India image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Ahh, I knew it was possible :P

I'm still trying to learn all of the API's.

Thanks IM.
Hey IM.  If you see this do you know how to tell if the Child window has closed?  For some reason it doesn't find the window anymore once it's added as a Child to my MDI form.
Hmm....have to think about that one a little...

When you add it using SetParent() it isn't really an MDI child and isn't really its own form anymore either.  We can't subclass it to tell when it has closed because it isn't part of our process.  We may have to use some kind of timer and periodically enumerate the child windows of your main form...

I'll see what I can come up with.
Anything that works will be good.  I see it can find the window using FindWindowEx(Me.hWnd, ....) but i'm still having trouble keeping track of all the windows and looping through etc to see if they're closed or not.  I'd like an easier way if there is one.  The question is already closed so you don't have to give further help unless you want to.
>> I see it can find the window using FindWindowEx(Me.hWnd, ....)

That is exactly the route I was thinking of doing first.  Use a recursive subroutine to descend into the child windows.  Each time you open a form, you can use FindWindow() to get the hWnd and then store it in a collection.  You can use any value you want as the key for the hWnd.  Call the recursive routine from the timer and update your collection accordingly.  When you go to open the form, you should first check the collection using the key to see if the form is already open.

Alternatively, we can use various ShellAndWait() algorithms to monitor the processes we have spawned.  You can make a boolean value for each child window that you spawn and then toggle it back when you know it is closed.  Here is a basic shell and wait algorithm:

    Option Explicit

    Private Const SYNCHRONIZE = &H100000
    Private Const WAIT_TIMEOUT = &H102

    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
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

    Private Sub Command1_Click()
        ShellAndWait "calc.exe"
        MsgBox "Done"
    End Sub

    Private Sub ShellAndWait(ByVal cmd As String)
        Dim lPid As Long
        Dim lHnd As Long

        lPid = Shell(cmd, vbNormalFocus)
        If lPid <> 0 Then
            lHnd = OpenProcess(SYNCHRONIZE, 0, lPid)
            If lHnd <> 0 Then
                Do
                    DoEvents ' keep application responsive
                    Sleep 50 ' keep CPU usage from ramping to 100%
                Loop Until WaitForSingleObject(lHnd, 0) <> WAIT_TIMEOUT
                CloseHandle (lHnd)
            End If
        End If
    End Sub

We would need to modify it so it wouldn't be a "holding" routine.  Instead, we add the PID from the Shell() command into a collection.  At the same time we add the name of a boolean variable into another collection.  A Timer control routine can iterate the collection and use OpenProcess() and CloseHandle() on each one checking to see if the process is still open.  If it is not, we can use the CallByName() function along with the corresponding variable name from the second collection to toggle the correct variable (I'll show you how to do that if you don't know how).

Let me know if you want to see the modifed shell and wait algorithm.

~IM
Well right now I have this:

Private Sub Get_All_IM_Windows(ByVal Win As Long)
Dim LastWin As Long, a As Long
Win = GetWindow(Win, GW_HWNDFIRST)
LastWin = GetWindow(Win, GW_HWNDLAST)
Do
    Win = GetWindow(Win, GW_HWNDNEXT) 'next window
    If GetClass(Win) = "AIM_IMessage" Then
        AIM_WINDOWS.Add Win
    End If
    DoEvents
Loop Until Win = LastWin Or Win = 0
End Sub

This adds all open AIM windows's handles to my collection (AIM_WINDOWS).  So AIM_WINDOWS already contains each window I have in my MDIForm.  When it adds an AIM window to my MDI form, it adds the Username of the person to a ListBox.  What I want to do is remove the name from the ListBox when the window isn't found anymore.  All Index's should be the same.  username with index 0 in the listbox should be the same as index 1 in the collection, 1 in the listbox would be 2 in the collection, etc.
Then you would need two different collections.  One to hold the AIM handles you initially find and another that gets updated each time you call Get_All_IM_Windows() from your polling routine.

Then you can iterate the first collection and see if those values are still present in the updated collection.  As you iterate the first collection, if the value is not in the second collection then remove it from the first collection and the listbox (AIM window has been closed).  If the value from the first collection is in the second collection then only remove it from the second collection (AIM window still open).  When you are done iterating the first collection, anything left in the second collection is a new AIM window that needs to be added to your first collection and your listbox.

Hope that made sense....
I do get what you're saying.  I appreciate all your help.  I will see if I can do what you suggested when I get home.  It sounds like it might work.
For some reason I can't get it to work.  This is the first time i've known how to do something but couldn't make it work right.  My coding to do it keeps building up and up and it still won't work.  Have a look:

Private Sub Check_All_IM_Windows()
Dim Win As Long, x As Long, y As Long
Win = FindWindowEx(Me.hWnd, 0, "AIM_IMessage", vbNullString)
Set AIM_HANDLES = Nothing
'###### Add all Child Windows to AIM_HANDLES
Do
    If GetClass(Win) = "AIM_IMessage" Then
        AIM_HANDLES.Add Win
    End If
    Win = GetWindow(Win, GW_HWNDNEXT) 'next window
    DoEvents
Loop Until Win = 0

For x = 1 To AIM_WINDOWS.Count
NextX:
    For y = 1 To AIM_HANDLES.Count
        If AIM_HANDLES.Item(y) = AIM_WINDOWS.Item(x) Then
            AIM_WINDOWS.Remove x
            If x <= AIM_WINDOWS.Count Then GoTo NextX Else GoTo Re_Add
        End If
    Next
    Form1.List1.RemoveItem (x - 1)
    AIM_WINDOWS.Remove x
Next

Re_Add:

For x = 1 To AIM_HANDLES.Count
    AIM_WINDOWS.Add AIM_HANDLES.Item(x)
Next
End Sub

As you can see it got pretty sloppy when I kept trying new things.  It's pretty easy to follow.  It DOES detect when I close a window, but it keeps removing Index 0 in the Listbox.  Can't figure out why.  I'm probably just mis-reading something, dunno.  Any Idea?
Is that a no? :(
Hi learning_t0_pr0gram,

I haven't forgotten you.  =)

Just been real busy sorry.  I'll see what I can do...
Oh ok.  I'll get you some points for all your help anyway.  I haven't had much time to answer questions on the site so my points are low but i'll answer a few and get you some.
Oh man.  I kind of gave up on this a few days ago and then decided to give it one more thought.  I was going the wrong way about it, it was so simple!

Private Sub Check_All_IM_Windows()
Dim x As Long
For x = 1 To AIM_WINDOWS.Count
    If GetClass(AIM_WINDOWS.Item(x)) = "" Then
        AIM_WINDOWS.Remove x
        Form1.List1.RemoveItem x - 1
        x = x - 1
    End If
Next
End Sub