views:

49

answers:

3

In a Win32 app is there a Windows message or some other notification that will get sent to a child window when it is placed into a different parent

+1  A: 

This is easy to test in a Windows Forms app. This is what I saw:

msg=0x18 (WM_SHOWWINDOW) hwnd=0x60c60 wparam=0x1 lparam=0x0 result=0x0
msg=0x46 (WM_WINDOWPOSCHANGING) hwnd=0x60c60 wparam=0x0 lparam=0x563e01c result=0x0
msg=0x85 (WM_NCPAINT) hwnd=0x60c60 wparam=0x1 lparam=0x0 result=0x0
msg=0x14 (WM_ERASEBKGND) hwnd=0x60c60 wparam=0xffffffff930119e8 lparam=0x0 result=0x0
msg=0x47 (WM_WINDOWPOSCHANGED) hwnd=0x60c60 wparam=0x0 lparam=0x563e01c result=0x0
msg=0xf (WM_PAINT) hwnd=0x60c60 wparam=0x0 lparam=0x0 result=0x0
msg=0xe (WM_GETTEXTLENGTH) hwnd=0x60c60 wparam=0x0 lparam=0x0 result=0x0
msg=0xd (WM_GETTEXT) hwnd=0x60c60 wparam=0x6 lparam=0x3fd7928 result=0x0

WM_SHOWWINDOW would be a good time to check if the parent changed. Not 100% sure if this is a side effect of the WF code taking care of the changed parent, the odds are fairly high. There is otherwise no dedicated message for it, the assumption is that the program already knows since it called SetParent or SetWindowLongPtr explicitly.

Hans Passant
I'd start by trying `WM_WINDOWPOSCHANGED`.
Adrian McCarthy
A: 

There is no single notification specifically for this. However, some frameworks, like Borland's VCL, wrap windows in classes, and thus issue their own notifications when the class objects are moved around from one parent to another (for example, VCL has CM_CONTROLLISTCHANGING, CM_CONTROLLISTCHANGE, and CM_CONTROLCHANGE notifications).

Can you provide more information about what exactly you want to accomplish by detecting a change in parent window?

Remy Lebeau - TeamB
My window is inside a plugin which is being dynamically inserted into the window hierarchy of the host program, and I need to detect when it is moved between different places in that hierarchy of the host.
solsberg
Then I suggest you log the messages that your plugin window actually receives, and then pick the most appropriate one for your needs.
Remy Lebeau - TeamB
Yes, this is what I'm doing for now, calling GetParent() when I get a WM_WINDOWPOSCHANGED message and comparing with the previous parent handle
solsberg
A: 

Sort-of... I've done this before using messaging between Windows, and a thread to listen on them. Remember, you do NOT want to modify the UI from any thread then the one that CREATED it...

Here is some example code of a Parent Window, which gets notified of a change by one of its children Windows. The same principle applies when doing what you are talking about. The parent Windows isn't really pumping messages while the child is open, (it IS, but I forget what is going through its mind at the time)... It's been 10 years since I had to do anything like this... But the code below is designed for a parent Window with a grid, and a child "Add/Edit" window that opens, and when you add or edit an item, it will update the parent GRID behind the Modal Edit window. This was designed in MFC, so you an imagine, you just need to add some HWND vars to the function calls to make it work under Win32, since Win32 and MFC are so inter-related.

First, in the parent Window, I setup a thread:

DWORD WINAPI PopulateGridThread(void *ptr)
{
   CMeterReadings *pThis = (CMeterReadings*)ptr;
   pThis->hGridMutex = CreateMutex(NULL, FALSE, "Wims.MeterReadingGridUpdateMutex");
   WaitForSingleObject(pThis->hGridMutex, 0);
   if(WaitForSingleObject(pThis->hGridMutex, 0) != WAIT_OBJECT_0) {
      return -2;
   }
   try {
      if(pThis->m_Get.GetCheck() == FALSE)
      {
         if(pThis->m_Grid.IsEmpty())
         {
            CloseHandle(pThis->hGridMutex);
            CloseHandle(pThis->hThreadHandle);
            pThis->hThreadHandle = INVALID_HANDLE_VALUE;
            pThis->m_DateFilter.EnableWindow(pThis->m_UseDate.GetCheck());
            pThis->m_UseDate.EnableWindow(TRUE);
            pThis->m_MeterFilter.EnableWindow(TRUE);
            return -3;
         }
      }

      pThis->hCursor = LoadCursor(NULL, IDC_APPSTARTING);
      pThis->m_Get.SetCheck(FALSE);
      pThis->m_DateFilter.EnableWindow(FALSE);
      pThis->m_UseDate.EnableWindow(FALSE);
      pThis->m_MeterFilter.EnableWindow(FALSE);
      pThis->m_Grid.PushRow();
      pThis->m_Grid.ResetContent();
      bool        bSuccess = false;
      long        nId = CCommonDialog::GetItemData(pThis->m_MeterFilter);
      bool        bUseDate = pThis->m_UseDate.GetCheck() == TRUE;
      CProDate   dtFilterDate;
      pThis->m_DateFilter.GetTime(dtFilterDate);

      if(nId == NULLCONSTANT || nId == LB_ERR)
      {
          bSuccess  = pThis->m_EquipmentPtr.LoadFirstMeterReading(bUseDate ?   CProDate::GetDateOnly(dtFilterDate) : CProDate::NullDate(), true);
      }
      else
      {
          bSuccess = pThis->m_EquipmentPtr.LoadFirstMeterReadingByMeterId(nId, bUseDate ? CProDate::GetDateOnly(dtFilterDate) : CProDate::NullDate());
   }      
   if(pThis->m_EquipmentPtr.GetRecordsReturned() > 5000) 
   {
      if(ThrowQuestion("This expansion could take a long time.  Do you wish to continue?", pThis) == IDNO)
      {
         bSuccess = false;
      }
   }      
   pThis->m_Get.SetWindowText("&Stop");
   if(bSuccess)
   {
      pThis->m_Grid.Redraw(false);
      do
      {
         pThis->m_Grid.AddGridRow();
         pThis->m_Grid.SetCellFormat(COLUMN_ADJUSTMENT,               "Yes;No");
         pThis->m_Grid.SetCheck(COLUMN_ADJUSTMENT,                    pThis->m_EquipmentPtr.AdjustmentIndc);
         pThis->m_Grid.AddDatesToGrid(pThis->m_EquipmentPtr.ReadingDate,     pThis->m_EquipmentPtr.EffectiveDate);
         pThis->m_Grid.AddTextToGrid(COLUMN_METER,                    pThis->m_EquipmentPtr.MeterDesc);
         /* Cut the rest of the fields, as they aren't important... */
      }
      while(pThis->m_EquipmentPtr.LoadNextMeterReading());
   }
   pThis->m_Grid.FinishGrid();
   pThis->m_Grid.Redraw(true);
   pThis->m_Grid.PopRow();
   pThis->m_Grid.RedrawWindow();
}
CATCH_COM_ERROR("CMeterReadings::PopulateGridThread()")
CloseHandle(pThis->hGridMutex);
CloseHandle(pThis->hThreadHandle);
pThis->hThreadHandle = INVALID_HANDLE_VALUE;
pThis->m_DateFilter.EnableWindow(pThis->m_UseDate.GetCheck());
pThis->m_UseDate.EnableWindow(TRUE);
pThis->m_MeterFilter.EnableWindow(TRUE);
pThis->hCursor = LoadCursor(NULL, IDC_ARROW);
pThis->m_Get.SetWindowText("&Get");
return 1;

}

Then, in the child Window, I would send a message back to the parent, when it was time to update. I did this by creating a thread that simply sent the message, so that the rest of the dialog would continue to function... (Or in your case, you could send a message directly to the Child Windows HWND to make it update...)

void _cdecl GridUpdateThread(void *ptr)
{
   CEnterMeterReadingsDlg *pthis = (CEnterMeterReadingsDlg*)ptr;
   if(pthis->NotifyParent())
   {
      pthis->NotifyParent()->SendMessage(CMeterReadings::GRID_UPDATE_MESSAGE, 0, 0);
   }
}

Then, all this was set into motion when the user selected "NEXT" on the dialog, instead of OK, or CANCEL...

_beginthread(GridUpdateThread, NULL, this);

Well, hopefully this will help you some, or give you some ideas...

LarryF
Useful information, but I think you're answering a different question.
Adrian McCarthy
In my exact example, yes. But, the point I was making was a way he could use to monitor things, by, perhaps using an override for SetParent(), or something of that nature. The point of the example was to show how to talk between two windows using threads, and custom messages. I think he could do what he wanted to using a method like I mention... There isn't anything built in that's going to do it for him...
LarryF