Date: | January 25, 2010 / year-entry #27 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20100125-00/?p=15183 |
Comments: | 29 |
Summary: | 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... |
Jonathan Wilson asks
why the clipboard APIs still require GlobalAlloc and friends.
Why is there not a 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 |
Comments (29)
Comments are closed. |
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.
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.
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.
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.
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.
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!
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.
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.
@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.
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.
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).
@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.
@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.
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.
Because now have two problems instead of one(TM). The APIs, the helpers and wrappers.
@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.
"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!
Come across programming problem.
Ask question on Raymond’s blog and work on something else for about 3 years while waiting for the answer.
Profit!
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.
@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.
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.
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.
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.
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.
CoTaskMemAlloc() does *a lot* more than this.
look up CoRegisterMallocSpy(), and that is just the start
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!)
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.
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.
@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).
@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.