Why doesn’t the window manager have a SetClipboardDataEx helper function?


Jonathan Wilson asks why the clipboard APIs still require GlobalAlloc and friends. Why is there not a SetClipboardDataEx or something that does what SetClipboardData does but without needing to call GlobalAlloc?

Okay, here’s your function:

HANDLE SetClipboardDataEx(UINT uFormat, void *pvData, DWORD cbData)
{
    if (uFormat == CF_BITMAP ||
        uFormat == CF_DSPBITMAP ||
        uFormat == CF_PALETTE ||
        uFormat == CF_METAFILEPICT ||
        uFormat == CF_DSPMETAFILEPICT ||
        uFormat == CF_ENHMETAFILE ||
        uFormat == CF_DSPENHMETAFILE ||
        uFormat == CF_OWNERDISPLAY) {
        return NULL; // these are not HGLOBAL format
    }

    HANDLE hRc = NULL;
    HGLOBAL hglob = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT,
                                cbData);
    if (hglob) {
        void *pvGlob = GlobalLock(hglob);
        if (pvGlob) {
            CopyMemory(pvGlob, pvData, cbData);
            GlobalUnlock(hglob);
            hRc = SetClipboardData(uFormat, hglob);
        }
        if (!hRc) {
            GlobalFree(hglob);
        }
    }
    return hRc;
}

Whoop-dee-doo.

Historically, Windows doesn’t go out of its way to include functions like this because you can easily write them yourself, or you can at least find a framework library that did it for you. Windows focused on doing the things that only Windows could do, providing you the building blocks with which you can create your own programs.

Besides, the classic clipboard is so old-school. The OLE clipboard provides a much richer interface, where you can generate data dynamically (for example as a stream) and expose it in formats other than just a chunk of bytes. Since SetClipboardData is old-school, if the window manager folks had written a function like SetClipboardDataEx, people would instead have asked the not unreasonable question, “Why did you bother to write a function that provides no essential new functionality to an old interface that was supplanted over a decade ago?”

Comments (29)
  1. Tom says:

    While I understand where you are coming from, I think your justification is weak since there are examples to the contrary.  For example, CoTaskMemAlloc() is simply a wrapper around the following code (probably improperly formatted):

    <pre>

    LPVOID CoTaskMemAlloc(DWORD dwSize)

    {

       IMalloc *pMalloc;

       LPVOID retval = NULL;

       if (SUCCEEDED(CoGetMalloc(1, pMalloc)))

       {

           retval = pMalloc->Alloc(dwSize);

           pMalloc->Release();

       }

       return retval;

    }

    </pre>

    There are any number of common tasks that could stand to have convenience wrappers, but I can understand the reluctance to create them.

    [The existence of exceptions does not invalidate the basic principle, especially since different teams perform each calculation from minus 100 points differently, at different points in time, with different priorities, and under different conditions. SetClipboardData is a very old function, and the calculus back in the old days (hence the word “Historically”) was biased strongly against convenience functions. -Raymond]
  2. I used to drop in convenience functions like this into the libraries that I developed until I started having to support them for a considerable number of developers. Every API created and documented is another API that has to be maintained and tested. Now I only put in the bare minimum of what the library has to support and release sample applications to show how to call the APIs in a more "convenient" manner. If I have to go through all this for the stuff I do, I can imagine it’s orders of magnitude more complex for MS to create, test, release, document, and maintain a new API function.

  3. Funny, I was just dealing with the clipboard a few days ago, and was wondering the same thing.

    I will say, not having a "helper" function means if you’ve never dealt with the clipboard before, you have to do some research to figure out all of the necessary functions you actually need to use. But it’s not too difficult I guess.

  4. jlechem says:

    I am a bit surprised by Ray’s response here, it seems a bit dismissive to me.  Not his usual style, but I digress.  After coding this kind of thing multiple times it’s not that hard to write.  However I think the burden should be on the API not the developer to handle clipboard functionality at that level.  I guess they decided .net developers were too dumb for that kind of code and actually created a class called Clipboard that handles all the stuff for you.

  5. Alexandre Grigoriev says:

    It might make sense, though, to have a few helper functions like SetClipboardText(W/A), GetClipboardText(W/A), to prevent those lazy programmers from screwing up the whole desktop by forgetting to close the clipboard. Those who neet to set more complex data types, are on their own.

    And it may make sense to provide some recovery from misbehaving applications. For example, if an app is holding the clipboard open for some predefined time, and another app is opening it, the clipboard should be closed for the first (misbehaving) app.

    [Historically, programmers were assumed not to be lazy. -Raymond]
  6. Lorenzo says:

    Alexandre, don’t forget that back in the old days, programmers were assumed to be smart and hardworking. And such a programmer would never forget to close the clipboard!

  7. porter says:

    >  if (SUCCEEDED(CoGetMalloc(1, pMalloc)))

    You missed WINOLEAPI in the function prototype and & before the pMalloc.

    For every "….Ex()" function you’ll have somebody demanding "…..ExEx()".

    The mantra "do a few things and do them well" has been replaced by "I want it all and I want it now". Parents are familiar with that attitude.

  8. Alexandre Grigoriev says:

    Raymond, Lorenzo,

    Yep, the programmers read the docs, always check the buffers for overflow, check the allocations for success, don’t save files to protected locations, test the applications to work under a limited user, don’t overwrite system DLLs, test the apps under checked OS build, don’t use undocumented APIs. I just don’t understand why Raymond had to work so hard to make compatibility shims.

  9. @Alexandre: Of course programmers screw up. I’d still prefer MS didn’t spend development cycles protecting lousy programmers from themselves. I’ve run into too many developers that have an attitude of, "Well, the OS/library/runtime/GC will clean up after me." Maybe I’m old school. Maybe that’s why .NET and Java give me the heebee-jeebees. There just need to be fewer lazy programmers in the world, not more.

  10. waleri says:

    >>  no essential new functionality to an old interface that was supplanted over a decade ago?

    Because this interface is still widely used. It is much easier to put essential data into clipboard using "old school" API. Newer API provides richer functionality but all those IDataSource and stuff are probably very flexible, very abstract but really pain in the A to use.

  11. rs says:

    My own implementation was very similar. I did not include the format check in the beginning, but took care to restore to the last error code after GlobalUnlock (which calls GetLastError even on success):

    err = GetLastError();

    GlobalUnlock(hglob);

    SetLastError(err);

    In general, I think that it is a good idea to have simple helper functions and wrappers in the API. If there is one officially supported version, there is less room for implementation errors. I find functions such as AppendMenu useful even if they exist only for backwards compatibility (and InsertMenuItem exists).

  12. Evan says:

    @Paul: "I’d still prefer MS didn’t spend development cycles protecting lousy programmers from themselves."

    That’s ’cause you’re looking at it the wrong way. Instead, look at it as protecting users from lousy programmers.

  13. @Evan: Well, there’s another solution to lousy programmers that involves providing a steady stream of new trainee baristas for Starbucks, but that probably wouldn’t be too popular.

  14. Jonathan says:

    I for one appreciate the convenience in such helper functions, like RegSetKeyValue (introduced in Vista). In my experience, the choice is 1 function properly implemented in the library, or 100 functions implemented by 100 different library users, each borken in a slightly different way.

    The .NET framework, specifically in 2.0, does include an astounding wealth of such functions, and is a joy to program against, compared to Win32.

  15. porter says:

    > In general, I think that it is a good idea to have simple helper functions and wrappers in the API. If there is one officially supported version, there is less room for implementation errors.

    Because now have two problems instead of one(TM). The APIs, the helpers and wrappers.

  16. Alexandre Grigoriev says:

    @Porter, Paul,

    Then, why have that dialog template stuff and CreateDialog at all. And resource API. Let programmers figure that out, let them just call CreateWindow for all those buttons and stuff. And while we’re at it, why have those controls. You know why? Because people will get it wrong. I’ve seen those custom buttons, and they already do wrong, as simple as they are.

    You think that reading/writing a string or anything from the registry is simple? Wish so. Even MS elite – kernel programmers – can get it wrong. I remember USB stack crashing with BSOD when a registry value was not a single REG_BINARY byte, but a REG_DWORD instead.

  17. Nick says:

    "Historically, Windows doesn’t go out of its way to include functions like this because you can easily write them yourself, or you can at least find a framework library that did it for you."

    I think we’ve finally discovered the Second Step!

    1. Come across programming problem.

    2. Ask question on Raymond’s blog and work on something else for about 3 years while waiting for the answer.

    3. Profit!

  18. porter says:

    Rather than just thinking of "loads of APIs", think in terms of layered APIs. Think of it in terms of a protocol stack. The lower ones belong in USER32.DLL, etc the upper ones belong in helper DLLs and libraries, and even in the development systems, not in the core DLLs.

    Consider the great unwinding currently taking place of trying to take the windows out of windows. Because of the APIs that were gaily thrown into the core DLLs, it has made it so much harder to produce a version of windows that can run with only a command line prompt.

    What am I talking about? Think single source for mobile systems, desktop tops, laptops, slates, high end servers, phones, embedded systems etc.

  19. @Alexandre: Nice reductio ad absurdum, but the idea is to make the API as simple as possible, but no simpler. Sure, we could all code in assembly language and make direct calls into the kernel, but nobody wants to do that. On the other hand, an API that tried to do every little thing would never be finished. It’s a matter of balance. I’m for smaller surface area.

  20. Crescens2k says:

    The funny thing about the whole "add helpers because people get things wrong" opinion is that I’ve seen people get it wrong even with helpers.

    Having helper functions does not stop people getting things wrong, the same as how not having helpers means people don’t get it right.

    When you see people do things like mess up the console access in a .net application, or mess up other supposidly easy things in a .net application that says to me that it doesn’t matter whether the helpers are there or not since someone somewhere will get it wrong. It will be the same with these helper functions, they wouldn’t protect against the people who really want to get it wrong.

  21. reduce bloat says:

    20 years ago the right approach was a minimal OS. Now, with multigigabyte OS, 30 lines of code isn’t that much at all.

    In total, there will be less code, less bloat, and less bugs because all applications doesn’t need to implement common functions themself.

    Raymonds excuses are lousy as usual.

  22. Lawrence says:

    For someone who entered their name as "reduce bloat", your suggestion that Windows should provide additional functions to replicate existing functions in a marginally more convenient way is rather… interesting.

  23. Henke37 says:

    There is also another reason, some people might optimize by using domain specific knowledge and thusly eliminating work. While I would be surprised if setting the clipboard was a performance issue, it works as an example. Someone could just allocate the memory for the string using GlobalAlloc to begin with, one memory copy less to worry about.

  24. Joe says:

    CoTaskMemAlloc() does *a lot* more than this.

    look up CoRegisterMallocSpy(), and that is just the start

  25. Neil says:

    The OLE interface is all well and good until some third-party DLL injects itself into your process and calls CoInitializeEx with incompatible parameters, causing OleInitialize to fail.

    It also defaults to delayed rendering, which is not useful if your app crashes or is suspended in a debugger (particularly the one in which you want to paste. Oops!)

  26. Dave Harris says:

    Jonathan – "I for one appreciate the convenience in such helper functions, like RegSetKeyValue (introduced in Vista)."

    But they belong with the app. If they are part of the O/S that is not installed with the app, the app can’t rely on them being there. If I have to detect whether I’m running on an O/S older than Vista, and then code my own implementation of RegSetKeyValue for that case, I’m better off using my own implementation always. Any new O/S-provided function has to provide a lot of value to be worth the compatibility hassles.

    If the function is provided by a library I statically link to, or install myself with my app, then the barrier to use is much lower.

  27. porter says:

    >The OLE interface is all well and good until some third-party DLL injects itself into your process and calls CoInitializeEx with incompatible parameters, causing OleInitialize to fail.

    If it is your process then call OleInitialize in your main. Problem solved. If you are a DLL, then respect the apartment you are running in.

    If you are a DLL and there is no current apartment you have the freedom of choice of what apartment you want for the small scope of your function.

  28. ton says:

    @Paul

    "Maybe that’s why .NET and Java give me the heebee-jeebees. There just need to be fewer lazy programmers in the world, not more."

    It’s not laziness to use .NET or Java it’s smart pragmatism,using the right tool for the job and a preference for providing  business value quicker than you could when writing in Win32. Go ahead and convince yourself that writing everything in win32 makes you superior while you introduce buffer and integer overflow vulnerabilities into your app while the rest of us "lazy" programmers have the VM warn us of these errors instead. :P

    You’re confusing helper functions and convenience as the problem when in reality it’s incompetence that causes problems regardless of what tool is used (Win32,.NET,Java).

  29. Paul M. Parks says:

    @ton: I use .NET and Java. They just give me the heebie-jeebies. C gives me the heebie-jeebies, too, albeit for different reasons, but I still use it where necessary. No language or platform is a panacea, and none can protect you from bad programmers.

Comments are closed.