Why do new controls tend to use COM instead of window messages?


Commenter David wonders why new controls tend to use COM instead of window messages. "It seems that there must have been a decision to only develop COM controls after the invention of COM."

There have been plenty of Win32 controls invented after the invention of COM. In fact, the entire common controls library was developed after the invention of COM. All your old friends like the list view, tree view, and property sheets are good old Win32 controls. But it's true that the newer stuff tends to use COM. Why is that?

I am not aware of any grand pronouncement on this subject. Each team makes a decision that they feel is best for their customers. But if you think about it, it's not an unreasonable choice: Suppose you were writing a new C++ object. Would you prefer to use this:

class Thing {
public:
  enum MESSAGENUMBER {
  MESSAGE_INSERTITEM,
  MESSAGE_DELETEITEM,
  MESSAGE_DELETEALLITEMS,
  MESSAGE_SETLABELTEXT,
  MESSAGE_GETNEXTITEM,
  MESSAGE_ADDBITMAP,
  ...
  };
  LRESULT Message(MESSAGENUMBER m, WPARAM wParam, lPARAM lParam);
private:
  ...
};

or would you rather use this:

class Thing {
public:
  BOOL InsertItem(Item *item, Item *itemParent, Item *itemInsertAfter);
  BOOL DeleteItem(Item *item);
  BOOL DeleteAllItems();
  BOOL SetLabelText(Item *item, PCWSTR pszText);
  Item *GetNextItem(Item *item);
  BOOL AddBitmap(HINSTANCE hinst, PCWSTR pszResource, COLORREF crTransparent);
  ...
private:
  ...
};

It's just less of a hassle using separate member functions, where you don't have to try to pack all your parameters into two parameters (cryptically named WPARAM and LPARAM) on the sending side, and then unpack the parameters on the window procedure side.

The overhead of sending a message can add up for high-traffic messages. A C++ method call is pretty direct: You set up the parameters and call the method. Whereas when you send a window message, it bounces around inside the window manager until it magically pops out the other side.

Again, these are my personal remarks and are not the official position of Microsoft on anything. But if you were writing a control, which would you prefer to have to implement? And if you were using a control, which interface would you rather use?

(That said, I can't think of many common controls that are COM-based. All the ones I know about still use boring window messages.)

Comments (25)
  1. Anonymous says:

    The link to the original comment doesn’t work. Its URL contains hte host name (blogs.msdn.com/) twice. Oh, and in the first paragraph after the code samples (the one that starts “It’s just less of a hassle…”), you open one bracket but don’t close it O:-) .

    Apart from that, it’s a really interesting article, as most of the stuff you write. Being a Visual Basic programmer, I have the luck to access all controls through COM. But every now and then I need to send a message to a control and, it’s a lot less clean (and more easily broken) than calling a method.

    [Fixed, thanks. -Raymond]
  2. Anonymous says:

    If there is only a COM Interface, you have an unnecessary memory allocation overhead for returning objects in Inproc calls.

    Therefore I want both options.

    [Who said information had to be returned in COM objects? COM methods are allowed to fill in structures, same as window messages. -Raymond]
  3. Anonymous says:

    Thanks to ATL’s message map, you can actually have the best of both worlds. That is, a clean C++ implementation on the control side of what is really exposed as a plain old boring windows control.

    I’m sure you can do this with MFC as well.

  4. Anonymous says:

    As a C programmer (mainly), I prefer simple functions that work for every thing (no class magic or messaging involved): Thing_InsertItem(thing, item, parent, predecessor), Thing_DeleteItem(thing, item), etc.

  5. Anonymous says:

    > As a C programmer (mainly),

    Then define COBJMACROS and that is exactly the interface you get for COM objects.

  6. Anonymous says:

    I still write in ‘C’.  So obviously, I like that windows controls use the the Win32 ‘C’ interface.  I have at least an illusion that they are clean and fast.

    Isn’t there an implied contract that there will be a low level message based interface, using only the format and structures  (WM_COMMAND or WM_NOTIFY) common to the Win32 API?  As a counter example the new Ribbon control in Win7 seems (at first glance) to be moving far away from this.

    One of the really great things about Microsoft in the past is that MS has made tremendous efforts to maintain compatibility (I’m think of the bits in Raymond’s book about Win95).  Shouldn’t this also apply to the style and type of interface?

    [Actually, it’s quite the contrary. COM method dispatch is much, much faster than a message send. Think about it – a COM method call is a vtable lookup and an indirect call. A message send involves handle validation, looking up the window procedure, calling any hooks, thunking the message, then calling the destination window procedure, which in turn has to pass the message number to a switch statement. Backwards compatibility says that if a control had a message-based interface, it will continue to have one. But it doesn’t mean that new controls have to use those same interfaces. -Raymond]
  7. Anonymous says:

    > One of the really great things about Microsoft in the past is that MS has made tremendous efforts to maintain compatibility

    I was impressed all the way back with the transition from Windows 1 to 2 where it interpreted the CreateWindow flags differently depending on the version of Windows SDK used to link the binary.

  8. Anonymous says:

    Another answer to this question would be to require controls instead to be CLR-only. Say Win7 required Net4 to be mandatory installed, so all newly written controls for that OS could be CLR-flavored.

    Of course there would still be plenty of interop and marshalling support around, but this time going *from* native *to* the CLR.

    In my experience window messages are less error prone than COM when you have large projects with multiple languages, i.e. VB and VC. For example, a UDT with BSTRs created in VB6 requires "inject_statement("#pragma pack(push,4)")" to be properly opened in VC6. But a struct in VB.NET can be opened directly in VC9.

    COM is an excellent infrastructure but so is the CLR. And we could leave lots of issues behind us, like discovering that this particular BSTR in that interface can be ANSI only…

  9. Anonymous says:

    @porter This is a nitpick and you know it. Perhaps, I should have said ‘has often’ made …  Compare and contrast with Apple who have made no such effort.

  10. Anonymous says:

    @davidlmorris: I believe @porter was being complimentary. The implication was that CreateWindow 1.0 had different documented behaviour to CreateWindow 2.0. Thus if your .exe was linked by linker 1.0 it continued to behave as it should.

    Windows 1.0 and 2.0 doco is thin on the ground…

  11. Anonymous says:

    @steveg  Quite right jumped to soon. I take my response to @porter back completely.

    Sorry, @porter, clearly have completely miss-read what you had said.

  12. Anonymous says:

    @davidlmorris

    educated guess: Windows certainly won’t stop supporting existing code out of thin air.

    But that does not preclude that it should continue to be expanded with constrained (message number, lParam, wParam) triple. That clearly works only for simplest of cases, and even then it’s ugly: it’s e.g. full of casts and implicit contracts that have to be explained in length in MSDN.

    Take e.g. EM_GETLINE:

    *(LPWORD)lpszBuffer = (WORD)nMaxLength; // Ugh!

    return (int)::SendMessage(

     m_hWnd,

     EM_GETLINE,

     nIndex,

     (LPARAM)lpszBuffer);

    Compare the to a potential

    int Edit::GetLine(int nIndex, DWORD dwMaxLen, LPTSTR lpszBuffer)

    and a call:

    SomeEdit.GetLine(nIndex, nMaxLength, lpszBuffer);

    Clearly, there’s much less to explain in the latter case. Nor is there syntax-induced-cast gibberish to read.

    In fact, the bigger question (and perhaps an interesting one), is why was all of Windows UI initially made with (m, l, w) interface at all? It’s clearly inferior, ease-of-use-wise, to many other approaches.

  13. Anonymous says:

    > In fact, the bigger question (and perhaps an interesting one), is why was all of Windows UI initially made with (m, l, w) interface at all? It’s clearly inferior, ease-of-use-wise, to many other approaches.

    You lack a sense of history.

    Windows was written back in the days of Microsoft C3.0. The two contemporary systems were Macintosh and XWindows. The windows messaging system is more powerful than the Mac approach, and simpler than Xt Widget writing. It is also a very lean system in memory footprint. It then spawned Presentation Manager which took the window procedure to it’s logical conclusion making window adornments invidual windows.

    C++ is fine for building standalone applications but it’s appalling to build an operating system out of it, it is too fragile and C++ linkages deserve their own place in hell.

  14. Anonymous says:

    Of course the nice thing about Windows messages is that it made dynamic subclassing possible. (Remember CTL3D.DLL?)

  15. Anonymous says:

    That’s great, but what’s going to happen to interprocess calls?

  16. Anonymous says:

    > That’s great, but what’s going to happen to interprocess calls?

    Out of interest, why do people still need to send arbitrary messages to other proceses’ windows? There are plenty of other IPC mechanisms available.

  17. Anonymous says:

    > There are plenty of other IPC mechanisms available.

    Indeed, but the other program must support them in the first place. Have you ever had a need to get stuff out of a utility, automatically? A very simple one, does the job and is not concerned about rest of the world, and also is not able to output data the way you want. The data is there, in the textboxes, but you have to send an interprocess message to reach the data.

  18. Anonymous says:

    > The data is there, in the textboxes, but you have to send an interprocess message to reach the data.

    I would hazard that GetWindowText() will be around for a while.

  19. Anonymous says:

    @porter: I would hazard that GetWindowText() will be around for a while.

    And doesn’t GetWindowText internally use Windows messages? It’s just a prettier front end. You could just do the SendMessage yourself.

    Windows messages are the only IPC mechanism guaranteed to be supported by a GUI program.

    Heck, maybe during an automated test or something, your controlling test program can use it to blink offending fields when something goes wrong.

  20. Anonymous says:

    @porter:

    I don’t think my example has to do anything with C++ in it’s essence. Only on the surface, it does. E.g. COM works with straight C, as is well noted in this thread.

    You are also mistaken when bashing about C++ linkage. Linkage is not addressed in neither C++ nor C language standards. The fact that "C linkage" (seem to) work better is only due to it’s constraints and simplicity of C. And even the C linkage isn’t portable between compilers without agreeing on calling convention and respecting data alignment.

  21. Anonymous says:

    @porter:

    IIRC, Turbo Vision of Borland, a text-screen-based UI uses simpler, yet more powerful mechanism. Does it predate Windows, I don’t know, but that could well be.

    I maintain that (m, l, w) triple is just silly. E.g.

    struct

    {

     int message;

     union

     {

       int iParam;

       unsigned uParam;

       void* pParam;

     };

    }

    is more simple, more explicit and more open. Not a lot of thinking is needed to see that right off the bat.

    IMO, someone at MS thought, at the time, that WPARAM/LPARAM ought to be enough for everybody ;-)

    [Um, your system is just (m, l)! You can do that with (m, l, w); just ignore the w. -Raymond]
  22. Anonymous says:

    > ? Do I have to rewrite it as ….

    The marshalling is only when you cross process boundaries. If you have your parent window in a different process, then the answer would have been yes.

  23. Anonymous says:

    >> And even the C linkage isn’t portable between compilers without agreeing on calling convention and respecting data alignment.

    Agreed, which is exactly why platforms have ABIs, Windows has __stdcall and OS/2 had _System.

    >> You could just do the SendMessage yourself.

    However the SendMessage() mechanism has to mess around knowing what many messages mean and doing appropriate interprocess marshalling. It didn’t matter on Win16 with a single memory image. It’s a shame all the marshalling was lumped on SendMessage instead of the specific APIs.

    [What “specific APIs” are you referring to? I can see the reverse argument: “Why did Microsoft write the marshalling code *twice* (once for SendMessage and again for the specific API)? That’s not only a waste of code but it pretty much guarantees that they will gradually fall out of sync.” -Raymond]
  24. Anonymous says:

    >> Why did Microsoft write the marshalling code *twice* (once for SendMessage and again for the specific API)?

    My suggestion is they only wrote marshalling once, for the specific APIs (eg GetWindowText) and not at all in SendMessage. Hence SendMessages job is to send the wParam and lParam parameters to the correct wndproc in the correct thread and return the lresult and that’s it. However, it’s all academic now.

    [Note that most messages do not have corresponding APIs. So you would have questions like “How come WM_WININICHANGE marshals but WM_GETTEXT doesn’t? My 16-bit program just uses SendMessage for everything; now I have to go track down every SendMessage in my program and see if it needs to be changed. And what about this function:
    LRESULT ForwardMessageToParent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    { return SendMessage(GetParent(hwnd), uMsg, wParam, lParam); }
    

    ? Do I have to rewrite it as

    LRESULT ForwardMessageToParent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    { hwnd = GetParent(hwnd);
      switch (uMsg) {
      case WM_GETTEXT: return GetWindowText(hwnd, (PCTSTR)lParam, wParam);
      case WM_SETTEXT: return SetWindowText(hwnd, (PCTSTR)lParam);
      … /* what other cases do I need? */ …
      default: return SendMessage(hwnd, uMsg, wParam, lParam);
     }
    }

    Screw you Microsoft for making it so hard to port from Win16 to Win32. -Raymond]

  25. Anonymous says:

    @Raymond (“Um, your system is just (m, l)! You can do that with (m, l, w)”)

    Yes, of course, it’s certainly nothing smart. I merely wanted to point out that the design could point out more clearly what is the purpose of the second parameter (to hold additional data related to message, be it an integer, or a pointer to whatever). That is the case with l, w, too, but is IMO more obscure. ( If nothing else, e.g. I had trouble remembering that WPARAM was 32, not 16 bits;-) ).

    @porter: I don’t know about OS/2, but my guess is that there is no “official” Windows ABI, as you put it ( Raymond, help me out here ;-) ). There is only calling convention and structure data alignment, mandated by the Windows SDK headers, and these are simply consequence of a given C compiler settings. IOW, it’s about how it’s compiled, not about how it’s specified. Even the calling convention is quite accidental: __stdcall (a.k.a pascal) is used because at the time (now, too? don’t know), x86 instruction “ret X”, that is possible with _stdcall (but not with __cdecl), was slightly more efficient, and (perhaps more importantly) because some of the initial Windows code was written with a Pascal compiler.

    [There definitely is an ABI (the x64 ABI is even online; it’s a long, boring document). But it’s hardly a coincidence that the ABI agrees with the compiler. After all, there isn’t much value to a compiler that doesn’t support the ABI! -Raymond]

Comments are closed.