Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

OLE control subclassing SysDateTimePick32 in comctl32.dll

Posted on 1997-05-17
11
1,229 Views
Last Modified: 2013-11-25
I'm attempting to use VC++ 4.0's OLE ControlWizard to subclass the SysDateTimePick32 control in comctl32.dll.  This DLL comes with IE3.0, and also ships with IE4.0.  I found out about the SysDateTimePick from a Visual Basic Programmer's Journal article, but I don't have any documentation on the control.  It's a standard Windows control that looks like a combo box, where the part that drops down is a calendar instead of a list.  The text part contains the current date (time) selected.

In a non-OLE control project, calling CreateWindowEx with SysDateTimePick32 as the window class correctly creates and displays the window.  In the OLE control project, I am not calling CreateWindowEx.  Instead, in MyControl::PreCreateWindow, I'm setting the lpszClass member of the CREATESTRUCT parameter to SysDateTimePick32, and performing other initialization there.  You can duplicate this exactly by instructing ControlWizard to subclass any Windows control and then modifying PreCreateWindow as follows:

BOOL CDateTimePickCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
      HINSTANCE hinstLib;
      MYPROC ProcAdd;

      hinstLib = LoadLibrary("comctl32");
      ASSERT (hinstLib != NULL);

      ProcAdd = (MYPROC) GetProcAddress(hinstLib, "InitCommonControlsEx");
      ASSERT (ProcAdd != NULL);

      tagInitCommonControlsEx      iccex;
      iccex.lngSize = sizeof(iccex);
      iccex.lngICC = ICC_DATE_CLASSES;
      (ProcAdd) (&iccex);

      cs.lpszClass = _T("SysDateTimePick32");
      cs.cx = 120;
      cs.cy = 20;
//      cs.style = 0x4;

      return COleControl::PreCreateWindow(cs);
}

These declarations are in the application's header file:

typedef struct tagInitCommonControlsEx {
    long lngSize;
    long lngICC;
} tagInitCommonControlsEx;

#define ICC_DATE_CLASSES 0x100 // month picker, date picker, time picker, updown

typedef VOID (*MYPROC)(struct tagInitCommonControlsEx *);

The problem is that the OLE control does not display the text part at all.  It responds to a mouse click on the right end of the control by dropping down the calendar.  If you set cs.style to 0 (the default is WS_CHILD) in PreCreateWindow, interestingly enough the text part does show, but in what looks to be a caption area; the window will not be sited on the container, however, which is "wrong".  I don't know if the solution is in setting the window style appropriately, or something else.

Please help!
0
Comment
Question by:dansari
  • 9
  • 2
11 Comments
 
LVL 3

Author Comment

by:dansari
ID: 1301601
Edited text of question
0
 
LVL 3

Author Comment

by:dansari
ID: 1301602
I just realized that the control does respond to the mouse click on the combo-box part of the control, to drop down the calendar.

But it does not draw the current date in the text area, which is also what I need.
0
 
LVL 3

Author Comment

by:dansari
ID: 1301603
Edited text of question
0
Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

 
LVL 3

Author Comment

by:dansari
ID: 1301604
Adjusted points to 200
0
 
LVL 3

Author Comment

by:dansari
ID: 1301605
Adjusted points to 240
0
 
LVL 3

Author Comment

by:dansari
ID: 1301606
Adjusted points to 250
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 1301607
The fundamental problem with your code is that you change the cs structure content and then call the base class implementation of PreCreateWindow(), which causes the changes you made to the create struct to be reset.

You should code your routine more like this:

BOOL CDateTimePickCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
   if (!COleControl::PreCreateWindow(cs))
      return FALSE;

   HINSTANCE hinstLib;
   MYPROC ProcAdd;

   hinstLib = LoadLibrary("comctl32");
   if (hInstLib == NULL)
      return FALSE;

   ProcAdd = (MYPROC) GetProcAddress(hinstLib, "InitCommonControlsEx");
   if (ProcAdd == NULL)
      return FALSE;

   tagInitCommonControlsEx iccex;
   iccex.lngSize = sizeof(iccex);
   iccex.lngICC = ICC_DATE_CLASSES;
   if (! (ProcAdd)(&iccex))
      return FALSE;

   cs.lpszClass = _T("SysDateTimePick32");
   cs.cx = 120;
   cs.cy = 20;
   // cs.style = 0x4;

   return TRUE;
}

By comparing this to what you originally posted, you'll realize
that your code has some other problems: mainly, bad error detection. If LoadLibrary() fails, you must fail the PreCreateWindow() call. Performing an ASSERT() here isn't enough. The same advice goes for your GetProcAddress() call as well as your actual call to the InitCommonControlsEx() function.

The next big problem you have is that you never call FreeLibrary() on the COMCTL32 handle you've loaded for yourself.

.B ekiM


0
 
LVL 3

Author Comment

by:dansari
ID: 1301608
I thought that you should make changes to the CREATESTRUCT
*before* calling COleControl::PreCreateWindow - if you look at the MFC source, you'll see that CreateWindowEx is called from around there with the CREATESTRUCT parameters.

Anyway, I tried your code, and it gives me exactly the same result - the window is 'empty', but displays the button on the right end when you click on it, and drops down the date/time picker.
0
 
LVL 3

Author Comment

by:dansari
ID: 1301609
I'm aware that the ASSERTs were poor error-handling code; however, at the time I didn't care at all about this - first of all I want the control to be drawn properly.

I would like this method to work.  However, an alternative that I tried was to create another window with CreateWindow in PreCreateWindow, making the parent the same as that in the CREATESTRUCT.  This works fine.  The problem here is that I need my ActiveX control to subclass that window.  If I use SubclassWindow in PreCreateWindow or OnCreate, I get failed ASSERTS in MFC, and I can't figure a way around this.

If anybody can get either technique to work, it would be much appreciated!

0
 
LVL 11

Accepted Solution

by:
mikeblas earned 280 total points
ID: 1301610

Sorry, I somehow got the impression that you were subclassing the C++ object, not the actual Windows control. And I'm sorry it took so long to get back to you; every time I try to use Experts Exchange, I end up spending the rest of the evening reporting bugs to problems@experts-exchange.com.

Anwyway, we kind of regret putting the feature to subclass existing Windows controls into the OLE Control wizard. It makes it appear that it's trivially easy to subclass an existing windows control in an OLE control, but it really isn't. The problem is getting the control to render into an arbitrary DC.

OLE Controls need to render into whatever DC their container provides.  Some Windows controls are completely inept at doing this, as you've found with the SysDateTime control. You can get some of the way there by writing your own OnDraw() override, like this:

void CSubCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
   if (m_hWnd == NULL)
      CreateWindowForSubclassedControl();

   if (m_hWnd != NULL)
   {
      CRect rcClient;
      GetClientRect(&rcClient);

      if (rcClient.Size() != rcBounds.Size())
      {
         pdc->SetMapMode(MM_ANISOTROPIC);
         pdc->SetWindowExt(rcClient.right, rcClient.bottom);
         pdc->SetViewportExt(rcBounds.Size());
      }
      pdc->SetWindowOrg(0, 0);
      pdc->SetViewportOrg(rcBounds.left, rcBounds.top);
      ::CallWindowProc(
         *GetSuperWndProcAddr(),
         m_hWnd, WM_PAINT, (WPARAM)(pdc->m_hDC),
         (LPARAM)(0));
   }
}

You'll find that this code works in Test Container, but won't work if you add the control to a MFC dialog box (including the Visual C++ IDE's dialog box editor itself). That's because Test Container has the control paint into a metafile and then renders the metafile, while MFC containment doesn't use metafiles if the control has a window.  Some other containers (like VB or Access) will probably paint the control correctly.

The bug in the Windows SysDateTime control is that it should be able to respond to WM_PRINT (not WM_PAINT!) properly, but it doesn't. It only works with WM_PAINT, and only (therefore) against a compatible DC... which is the source of the problems you're seeing.

The best way to handle this scenario is to create the control as a child window of your control instead of subclassing it.  This'll change your architecture a little, and may force you to subclass the control by stealing its window proc directly--depending on how you wanted to react to user input.  (Eg, use SetWindowLong() instead of subclassing the control with MFC--this is how you'll get around the assertions.)

By the way, you do need to call base class implementations of PreCreateWindow() first, as PreCreateWindow() for any class will set the options that it wants. As it sets those options, it's likely to be undoing the changes that you yourself wanted. So, if you want to be sure that your changes are in effect, you'll need to call the base class first and then set the information you want afterwards so your changes aren't undone.  The Wizard does sometimes emit code that calls the base class first--and that's a bug in the Wizards.

.B ekiM

0
 
LVL 3

Author Comment

by:dansari
ID: 1301611
Thanks.  I couldn't get this code to work - it wasn't showing anything in Test Container or VB, so I just went with the other method of creating another child window in PreSubclassWindow and subclassing that with SetWindowLong.  This works.

How many points is that... 1120.  Whoa!  You deserve it.
0

Featured Post

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Ownerdraw of the grid button.  A singleton class implentation and usage. Continuing from the fifth article about sudoku.   Open the project in visual studio. Go to the class view – CGridButton should be visible as a class.  R…
With most software applications trying to cater to multiple user needs nowadays, the focus is to make them as configurable as possible. For e.g., when creating Silverlight applications which will connect to WCF services, the service end point usuall…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…

789 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question