OLE control subclassing SysDateTimePick32 in comctl32.dll

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!
LVL 3
dansariAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

dansariAuthor Commented:
Edited text of question
0
dansariAuthor Commented:
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
dansariAuthor Commented:
Edited text of question
0
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

dansariAuthor Commented:
Adjusted points to 200
0
dansariAuthor Commented:
Adjusted points to 240
0
dansariAuthor Commented:
Adjusted points to 250
0
mikeblasCommented:
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
dansariAuthor Commented:
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
dansariAuthor Commented:
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
mikeblasCommented:

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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
dansariAuthor Commented:
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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft Development

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.