Enumerating Controls of a Dialog Resource at Runtime

What you use it for is up to you, of course. The function by itself doesn't do anything, it only parses through the dialog resource. At various points in the code you'll see exactly where you can grab info and make use of it. I needed to roll my own property sheet and property page classes for use on a CDialogBar, and decided the code might be useful to others. This code was compiled under VC5 Service Pack 3, but I assume VC6 will run it as well.

In the code you'll notice sections like this:


wPointer++;
WORD wMenuOrd = (*wPointer);
wPointer++;
This is where you come in and do stuff with the value retreived. The data structures are DWORD-aligned and always variable in length, we have to be careful to account for everything. IE don't yank sections of unused code unless you're accounting for the byte length elsewhere.

The dialog resources use NULL-terminated Unicode strings... two bytes per character. I've put in boiler plate code to load them up as CStrings using the LPCWSTR cast. Obviously if you don't need a particular string, you can yank that assignment. Or conversely, if you like working in wide-character strings, you can change it to use _bstr_t or whatever flavor you prefer.

Uses for this function

  • Property sheet on a CDialogBar. Using the resource ID for every control on a dialog resource allowed me to separate the dialog bar template from the pages that would be layered on it. What a pain it is to deal with layers and layers of controls. CPropertySheet is great, but it's not a toolbar. I have one main dialog bar template during development with nothing but the Tab control on it. Every page has its own template with matching width/height. These templates are really only placeholders for the controls. When it comes time to build, I bulk copy all of the page controls to the dialog bar by manually editing myapp.rc. Then in a custom page class, I parse through the page templates one at a time, assigning the resource IDs for those controls to a specific page number. When the user switches tabs, I hide/show the respective controls by making calls to GetDlgItem(). There's more to it than that quick explanation, but you get the idea.
  • Dynamically resizable CFormView derivatives. Knowing where every control is located on a dialog template relative to the overall width/height of that dialog would allow you to calculate their new positions during resizing. You wouldn't have to add new code every time you placed a new control on the page, or worry about changing code if you alter the resource name.

Okay, on with the code!


BOOL EnumerateDialogResource(UINT iResourceID)
{
 // Dialog base unit -> pixel unit conversion macros
 #define DLGUNIT_TO_PIXEL_X(dlgX)	\
  ( (dlgX * LOWORD( ::GetDialogBaseUnits() )) / 4)

 #define DLGUNIT_TO_PIXEL_Y(dlgY)	\ 
  ( (dlgY * HIWORD( ::GetDialogBaseUnits() )) / 8)

 // DWORD-alignment macro
 #define DWORD_ALIGN(x)  x = ((x + 3) & ~3)
	
 LPVOID lpResource = NULL;
 HGLOBAL hgResource = NULL;	
 HINSTANCE hInst = AfxGetResourceHandle();

 // Find resource handle
 HRSRC hrDlg = ::FindResource(hInst, 
  MAKEINTRESOURCE(iResourceID), RT_DIALOG);

 if (hrDlg == NULL)
 {
  return FALSE;
 }
	
 // Load it
 hgResource = LoadResource(hInst, hrDlg);
 if (hgResource == NULL)
 {
  return FALSE;
 }

 // Lock it
 lpResource = LockResource(hgResource);
 if (lpResource == NULL)
 {
  return FALSE;		
 }
		
 // Use these to enumerate the resource controls
 LPDLGTEMPLATE lpTemplate;
 LPDLGITEMTEMPLATE lpItemTemplate;	
	
 // Use these to keep track of where we're at
 DWORD dwDlgTemplateSize = sizeof (DLGTEMPLATE);		
 DWORD dwDlgItemTemplateSize = sizeof (DLGITEMTEMPLATE);		
 DWORD dwDataPosition = 0;

 // This guy will move through the global data
 WORD *wPointer = NULL;

 // Cast a BYTE pointer to the global data 
 // so we can manipulate it
 BYTE *pbyGlobalData = (BYTE *)(lpResource);	
	
 // This is the dialog itself
 lpTemplate = (LPDLGTEMPLATE)pbyGlobalData;
 dwDataPosition = dwDlgTemplateSize;

 // The dialog's menu is the first 
 // item after the dialog template (1.)	
 wPointer = (WORD *)(pbyGlobalData + dwDataPosition);
	
 if ((*wPointer) == 0x0000)
 {
  // No menu resource
  wPointer++;
 }
 else
 {
  if ((*wPointer) == 0xffff)
  {			
   // There's one other element which specifies
   // the ordinal value of the menu
   wPointer++;
   WORD wMenuOrd = (*wPointer);
   wPointer++;
  }
  else
  {
   // It's a string specifying the 
   // name of a menu resource
   CString strMenuResource ((LPCWSTR)wPointer);			
   while ((*wPointer++) != 0x0000);
  }		
 }

 // Next comes the window class
 if ((*wPointer) == 0x0000)
 {
  // The system uses the predefined dialog 
  // box class for the dialog box
  wPointer++;
 }
 else
 {
  if ((*wPointer) == 0xffff)
  {
   // One other element which specifies the 
   // ordinal value of a predefined system window class
   wPointer++;
   WORD wClassOrd = (*wPointer);
   wPointer++;
  }
  else
  {
   // It's a string specifying the name of a registered window class
   CString strWndClass ((LPCWSTR)wPointer);
   while ((*wPointer++) != 0x0000);
  }
 }

 // Now comes the title of the dialog box
 if ((*wPointer) == 0x0000)
 {
  // There is no title
  wPointer++;
 }
 else
 {		
  CString strDlgTitle ((LPCWSTR)wPointer);
  while ((*wPointer++) != 0x0000);
 }

 // Check for the font point size and 
 // typeface members.  These may or may not be there.	
 if (lpTemplate->style & DS_SETFONT)
 {
  WORD wPointSize = (*wPointer);
  wPointer++;
		
  // The font name
  CString strTypeFace ((LPCWSTR)wPointer);
  while ((*wPointer++) != 0x0000);
 }

 // Now that we've passed all of the DLGTEMPLATE 
 // stuff, we need to make sure we're aligned on a DWORD boundary
 DWORD_ALIGN (dwDataPosition);

 // These fields are not in pixel values; need to 
 // map the values properly. See the DLGTEMPLATE definition
 // for more info on these and the other fields
 UINT iDlgX = DLGUNIT_TO_PIXEL_X(lpTemplate->x);
 UINT iDlgY = DLGUNIT_TO_PIXEL_Y(lpTemplate->y);
 UINT iDlgWidth = DLGUNIT_TO_PIXEL_X(lpTemplate->cx);
 UINT iDlgHeight = DLGUNIT_TO_PIXEL_Y(lpTemplate->cy);

 // Use this to keep track of each DLGITEMTEMPLATE size
 DWORD dwItemSize = 0;

 // How many controls are we dealing with? 
 UINT iNumControls = lpTemplate->cdit;
	
 // Spin through all of the controls
 for (int j = 0; j < iNumControls; j++)
 {
  // First point to the item's template
  lpItemTemplate = (LPDLGITEMTEMPLATE)(pbyGlobalData + dwDataPosition);

  // Fields for DLGITEMTEMPLATE:
  // DWORD style
  // DWORD dwExtendedStyle
  // short x
  // short y
  // short cx
  // short cy
  // WORD id

  // Like the DLGTEMPLATE, you'll need to map the 
  // dialog base units for X and Y to 
  // pixel values... DLGUNIT_TO_PIXEL_X and DLGUNIT_TO_PIXEL_Y

  // Point to the arrays after the DLGITEMTEMPLATE (2.)
  wPointer = (WORD *)(pbyGlobalData 
   + dwDataPosition + dwDlgItemTemplateSize);
		
  // Check out the window class of this control
  if ((*wPointer) == 0xffff)
  {
   // It's a predefined system class (button, edit box, static, 
   // list box, scroll bar, or combo box.)
   wPointer++;
   WORD wClassOrd = (*wPointer);
   wPointer++;
  }
  else
  {
   // It's the name of a registered windows class
   CString strTypeFace ((LPCWSTR)wPointer);
   while ((*wPointer++) != 0x0000);
  }
		
  // Check out the title array
  if (*wPointer == 0xffff)
  {
   // This is the ordinal value of a resource 
   // in an executable, such as an icon
   wPointer++;
   WORD wResourceOrd = (*wPointer);
   wPointer++;
  }
  else
  {
   // This is the initial text for the control
   CString strInitialText ((LPCWSTR)wPointer);
   while ((*wPointer++) != 0x0000);
  }
		
  // Check out the creation array.  Use this to 
  // determine how large the creation array is.  The creation 
  // array is passed as a pointer in the lParam parameter of the 
  // WM_CREATE message.
  WORD wCADataSize = 0;

  if (*wPointer == 0x0000)
  {
   // There is no creation array
   wCADataSize = sizeof(WORD);
  }
  else
  {
   // The first item represents the size of the array (which includes
   // the first item, too!)
   wCADataSize = (*wPointer);

   // The creation array is really a CREATESTRUCT
   CREATESTRUCT *lpCreateStruct = (CREATESTRUCT *)wPointer;
  }

  // Figure out how big the item is; it needs to be DWORD-aligned
  dwItemSize = (((BYTE *)wPointer) 
   - ((BYTE *)lpItemTemplate)) + wCADataSize;

  DWORD_ALIGN (dwItemSize);
		
  // That's it... the entire DLGITEMTEMPLATE has been accounted for
  dwDataPosition += dwItemSize;
 }	
	
 // Cleanup
 if (lpResource != NULL && hgResource != NULL)
 {
  UnlockResource(hgResource);
  FreeResource(hgResource);
 }
 return TRUE;
}

Notes:

  • Blatant rip from MSDN:

    In a standard template for a dialog box, the DLGTEMPLATE structure is always immediately followed by three variable-length arrays that specify the menu, class, and title for the dialog box. When the DS_SETFONT style is given, these arrays are also followed by a 16-bit value specifying point size and another variable-length array specifying a typeface name. Each array consists of one or more 16-bit elements. The menu, class, title, and font arrays must be aligned on WORD boundaries.

  • In a standard template for a dialog box, the DLGITEMTEMPLATE structure is always immediately followed by three variable-length arrays specifying the class, title, and creation data for the control. Each array consists of one or more 16-bit elements.

    Each DLGITEMTEMPLATE structure in the template must be aligned on a DWORD boundary. The class and title arrays must be aligned on WORD boundaries. The creation data array must be aligned on a WORD boundary.



Comments

  • Gold Classic styler ghd promociones hermosos y prácticos especiales

    Posted by wanzilucky on 06/05/2013 08:03pm

    [url=http://www.planchasespanaghdtop.net/plancha-pelo-ghd/]baratas ghd outlet[/url] Pocos meses atrás, cuando yo estaba buscando un programador de máquina de ritmo, tuve la suerte de tropezar con una aplicación para PC que me dejó impresionado y aún hoy, estoy seguro de que no hay nada que pueda igualarlo.Este programa se llama Dub baratos alisadores de pelo ghd Turbo, y sospecho que es el último kit para hacer sus propios ritmos. No puedo creer que usted puede conseguir un teclado totalmente funcional con la octava, y caja de ritmos, secuenciador de la pista, premade ritmos y mucho más. [url=http://www.planchasespanaghdtop.net/plancha-pelo-ghd/]ghd outlet[/url] El mercado de la moda sigue de forma continua sufriendo de tipo éticos que se concentran en sus bienes particulares en cuanto a bajo costo y también contra las falsificaciones de la ley y también reproducciones. Sin embargo, una sola marca, sin duda, se experimenta la crisis particular a través de este ataque: GHD tienen sentido la presión total en cuanto a este tipo de ataque contra sus bienes.Un mes pasado, como lo hice empezar a navegar en la red diseñado para un producto contemporáneo. Una vía aérea importante en los pueblos de registro de web personalizado de discutir el peinado atrajo a los párpados de los ojos propios.Como ya he finalizado el y también habló a través de muchas personas que desean diseñador. Para el período de varias conversaciones agradables, como me enteré de eso plancha GHD, como resultado de las probabilidades. [url=http://comprarsaleghd.webstarts.com/]planchas ghd baratas[/url] Una mujer en los años veinte niña, declaró a alrededor de maravilla pasar por varios.Fue posteriormente que GHD plancha que da una mano a la de ser mucho más contemporáneo, e incluso de sí mismo.No obstante, la aventura era un poco fuera de lo común, mientras hablaba acerca de la aplicación dentro de mi cabeza. Poco después, lo he probado para encontrar la aplicación en la red e incluso aprender sobre ella, buscando el conocimiento espléndida diferentes.GHDs incorrectas resultan ser menos anormal, principalmente porque se puede pensar, en todo caso, GHD propia conseguido ofrecer unos 5.000 miles de planchas para el pelo en las últimas décadas y, tal vez. Lo más probable es después de 5 años aproximadamente extremadamente significativo para hacer uso de, simplemente están siempre va a visitar mal en su momento.

    Reply
  • ghd merchandise returret

    Posted by motherdhmm on 05/30/2013 05:01am

    [url=http://www.buy-beatsdrdre.com/category/2012-dr-dre-beats-outlet]dr dre beats[/url] Hår behandlinger, såsom hår farve, blegning, opretning, permanenter og visse frisurer (fletninger og cornrows) kan også bidrage til en persons hårtab. En tilstand, som kaldes [url=http://www.buy-beatsdrdre.com/]beats by dre headphones[/url] Straks efter sammen med din gode hår dage manke styling ædle metaller, erhverve vidunderlige såvel som fysiske egnethed ud over velfærd retsmiddel langs med lægge på det med hensyn til at bruge. Leder af hår vil i alle sandsynlighed ender med at blive avanceret disheveled det samme som bølger kontakt med velkendte mænd og kvinder langs med design og stil plus formatindstillinger. Lige før en person vælger du at efterspørgslen disheveled vises, er du i stand til at oprette din egen personlige personlige bruge højere blot ved besidder en design og storheden brug. Den nøjagtige udformning og elegance draw på vil sandsynligvis fastholde de faktiske private bølger for at kunne du overlegen partier ekstra. Hår på grund af dette vil helt sikkert næsten helt sikkert ofte være innovative sammenholdt med fabelagtig. Du kan konvertere hoved af hår Implementer mens betydeligt, mens alle ønsker når du besidder denne bestemte visse gode hår dage manke hår styling stål. [url=http://www.blog.cheapbeatsbydre.co.nz/]beats by dre[/url] En ideel kvinde er ofte afbilledet til at være en person med en slank figur med langt hår. Hår har altid været vigtigt for hver kvinde, fordi det en eller anden måde afspejler deres personlighed. Have lange skinnende hår kan forføre en mand, kan det lander en kvinde i en shampoo kommercielle, eller få komplimenter fra andre. Det kan afspejle en kvindes sundhed og giver et indtryk af, at hun er sund, og at hun tager sig godt af sig selv. Selvfølgelig ville det ikke alle kvinder vil have langt hår.

    Reply
  • no need to write code by yourself

    Posted by Legacy on 03/31/2003 12:00am

    Originally posted by: Yi Zhang

    I just found out that we can use the functions provided in AFXIMPL.H, DLGTEMPL.CPP, OCCIMPL.H and OCCMGR.CPP (there are under the directory C:\Program Files\Microsoft Visual Studio\VC98\MFC\SRC for a typical installation) to do whatever you want to DLGTEMPLATE / DLGTEMPLATEEX / DLGITEMTEMPLATE / DLGITEMTEMPLATEEX. Followed are some examples of these functions:
    1. AFX_STATIC DLGITEMTEMPLATE* AFXAPI _AfxFindFirstDlgItem(const DLGTEMPLATE* pTemplate)
    2. AFX_STATIC DLGITEMTEMPLATE* AFXAPI _AfxFindNextDlgItem(DLGITEMTEMPLATE* pItem, BOOL bDialogEx)

    Docs of CDialogTemplate:
    http://www.cppdoc.com/example/mfc/classdoc/MFC/CDialogTemplate.html#GetSizeInPixels(SIZE*)


    Hope this helps.

    Reply
  • Does not work with DIALOGEX

    Posted by Legacy on 03/31/2003 12:00am

    Originally posted by: Sven Eggert

    Warning this does not function
    with DIALOGEX-resources Why?

    Reply
  • BUG FIXED! - It won't work without it

    Posted by Legacy on 10/25/2000 12:00am

    Originally posted by: Magnus Ekvall

    Great work! I was trying to find out how to do this for half a day, before I founf this. One tiny little bug though...

    The dwDataPosition is set to the size of the DLGTEMPLATE structure, even though there is more data after it. Adding this line...

    dwDataPosition = DWORD(wPointer) - DWORD(pbyGlobalData);

    ...just before the line...

    DWORD_ALIGN (dwDataPosition);

    ...solves the problem.

    // M. Ekvall

    Reply
  • Please post code for Property sheet on a CDialogBar

    Posted by Legacy on 01/04/2000 12:00am

    Originally posted by: Peter Tran

    The enumeration of controls can be real handy, Ty. Could you post your code for implementing property sheets on a a CDialogBar? Thanks.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • VMware vCloud® Government Service provided by Carpathia® is an enterprise-class hybrid cloud service that delivers the tried and tested VMware capabilities widely used by government organizations today, with the added security and compliance assurance of FedRAMP authorization. The hybrid cloud is becoming more and more prevalent – in fact, nearly three-fourths of large enterprises expect to have hybrid deployments by 2015, according to a recent Gartner analyst report. Learn about the benefits of …

Most Popular Programming Stories

More for Developers

RSS Feeds