Solved

OLE control subclassing SysDateTimePick32 in comctl32.dll

Posted on 1997-05-17
11
1,209 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
 
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
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
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

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Hibernate methods 2 58
sum28 challenge 31 96
fizzArray  challenge 1 47
Delphi Mdi application Child forms get behind control 7 114
A theme is a collection of property settings that allow you to define the look of pages and controls, and then apply the look consistently across pages in an application. Themes can be made up of a set of elements: skins, style sheets, images, and o…
Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
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…

757 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

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now