问题描述
我需要在剪贴板上设置透明图像的帮助。我不断收到句柄无效的信息。基本上,我需要第二眼来查看以下代码。 (完整的工作项目位于。)
I need help setting a transparent image to the clipboard. I keep getting "handle is invalid". Basically, I need a "second set of eyes" to look over the following code. (The complete working project at ftp://missico.net/ImageVisualizer.zip.)
这是一个图像Debug Visualizer类库,但是我将包含的项目作为可执行文件运行以进行测试。 (请注意,该窗口是一个工具箱窗口,并且在任务栏中显示为false。)我已经厌倦了必须在工具箱窗口中执行屏幕捕获,使用图像编辑器打开屏幕捕获,然后删除添加的背景的原因,因为是一个屏幕截图。所以我想我会尽快将透明图像放到剪贴板上。好吧,问题是... Clipboard.SetImage没有透明性支持。谷歌可以挽救……不完全。
This is an image Debug Visualizer class library, but I made the included project to run as an executable for testing. (Note that window is a toolbox window and show in taskbar is set to false.) I was tired of having to perform a screen capture on the toolbox window, open the screen capture with an image editor, and then deleting the background added because it was a screen capture. So I thought I would quickly put the transparent image onto the clipboard. Well, the problem is...no transparency support for Clipboard.SetImage. Google to the rescue...not quite.
这是我到目前为止所拥有的。我从许多来源那里拉来的。请参阅代码以获取主要参考。我的问题是使用CF_DIBV5时出现无效句柄。我需要使用BITMAPV5HEADER和CreateDIBitmap吗?
This is what I have so far. I pulled from a number of sources. See the code for the main reference. My problem is the "invalid handle" when using CF_DIBV5. Do I need to use BITMAPV5HEADER and CreateDIBitmap?
任何来自您的GDI / GDI +向导的帮助将不胜感激。
Any help from you GDI/GDI+ Wizards would be greatly appreciated.
public static void SetClipboardData(Bitmap bitmap, IntPtr hDC) {
const uint SRCCOPY = 0x00CC0020;
const int CF_DIBV5 = 17;
const int CF_BITMAP = 2;
//'reference
//'http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/816a35f6-9530-442b-9647-e856602cc0e2
IntPtr memDC = CreateCompatibleDC(hDC);
IntPtr memBM = CreateCompatibleBitmap(hDC, bitmap.Width, bitmap.Height);
SelectObject(memDC, memBM);
using (Graphics g = Graphics.FromImage(bitmap)) {
IntPtr hBitmapDC = g.GetHdc();
IntPtr hBitmap = bitmap.GetHbitmap();
SelectObject(hBitmapDC, hBitmap);
BitBlt(memDC, 0, 0, bitmap.Width, bitmap.Height, hBitmapDC, 0, 0, SRCCOPY);
if (!OpenClipboard(IntPtr.Zero)) {
throw new System.Runtime.InteropServices.ExternalException("Could not open Clipboard", new Win32Exception());
}
if (!EmptyClipboard()) {
throw new System.Runtime.InteropServices.ExternalException("Unable to empty Clipboard", new Win32Exception());
}
//IntPtr hClipboard = SetClipboardData(CF_BITMAP, memBM); //works but image is not transparent
//all my attempts result in SetClipboardData returning hClipboard = IntPtr.Zero
IntPtr hClipboard = SetClipboardData(CF_DIBV5, memBM);
//because
if (hClipboard == IntPtr.Zero) {
// InnerException: System.ComponentModel.Win32Exception
// Message="The handle is invalid"
// ErrorCode=-2147467259
// NativeErrorCode=6
// InnerException:
throw new System.Runtime.InteropServices.ExternalException("Could not put data on Clipboard", new Win32Exception());
}
if (!CloseClipboard()) {
throw new System.Runtime.InteropServices.ExternalException("Could not close Clipboard", new Win32Exception());
}
g.ReleaseHdc(hBitmapDC);
}
}
private void __copyMenuItem_Click(object sender, EventArgs e) {
using (Graphics g = __pictureBox.CreateGraphics()) {
IntPtr hDC = g.GetHdc();
MemoryStream ms = new MemoryStream();
__pictureBox.Image.Save(ms, ImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
Image imag = Image.FromStream(ms);
// Derive BitMap object using Image instance, so that you can avoid the issue
//"a graphics object cannot be created from an image that has an indexed pixel format"
Bitmap img = new Bitmap(new Bitmap(imag));
SetClipboardData(img, hDC);
g.ReleaseHdc();
}
}
推荐答案
您可以采取一些措施来收紧代码库,而我在CF_DIBV5上做了一些功课,可能会对您有所帮助。
There are a few things you can do to tighten up the codebase a bit, and I've done some homework on CF_DIBV5 that you may find helpful.
首先,在 __ copyMenuItem_Click()
中,我们有四个完整的图像副本,这远远超出了必要。
First off, in __copyMenuItem_Click()
, we have four complete copies of your image, which is much more than necessary.
-
__ pictureBox.Image
-
Image imag = Image。 FromStream(ms);
-
新位图(imag)
-
位图img =新位图(新位图(imag));
(外部位图)
__pictureBox.Image
Image imag = Image.FromStream(ms);
new Bitmap(imag)
Bitmap img = new Bitmap(new Bitmap(imag));
(the outer bitmap)
此外,您的 MemoryStream
, imag
,新位图(imag)
和 img
不会被丢弃,这可能会导致问题。
Furthermore, your MemoryStream
, imag
, new Bitmap(imag)
, and img
do not get disposed, which could result in problems.
没有更改代码的意图(并且可能没有解决句柄问题),则可以这样重写方法:
Without changing the intent of the code (and probably without solving the handle issue), you could rewrite the method like this:
private void __copyMenuItem_Click(object sender, EventArgs e)
{
var image = __pictureBox.Image;
using (var g = __pictureBox.CreateGraphics())
using (var bmp = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb))
using (var bmpg = Graphics.FromImage(bmp))
{
IntPtr hDC = g.GetHdc();
bmpg.DrawImage(image, 0, 0, image.Width, image.Height);
SetClipboardData(bmp, hDC);
g.ReleaseHdc();
}
}
接下来需要注意的下一件事是该行:
The next thing that looked like it would require attention was the line:
IntPtr hClipboard = SetClipboardData(CF_DIBV5, memBM);
我相当确定您必须整理出BITMAPV5HEADER结构,以便在使用CF_DIBV5时将位传递给剪贴板。我之前错了,但是我要验证memBM是否确实包含标头。一个很好的指标是第一个DWORD(UInt32)是否具有值124(标头的大小(以字节为单位))。
I am fairly certain that you must marshal out the BITMAPV5HEADER structure to pass bits to the clipboard when using CF_DIBV5. I've been wrong before, but I would verify that memBM actually contains the header. A good indicator is whether the first DWORD (UInt32) has the value 124 (the size of the header in bytes).
我的最后一句话比第二对更值得推荐的眼睛。我发现GIMP,Paint.NET,Fireworks和PhotoScape等照片应用程序似乎对CF_DIBV5(Format17)粘贴的支持不佳或根本不存在。您可能会考虑将PNG格式复制到剪贴板,并以不透明的位图作为备份,以防万一目标应用程序不支持PNG。我使用扩展方法来简化此操作:
My final remarks are more recommendation than a second pair of eyes. I found that photo applications like GIMP, Paint.NET, Fireworks, and PhotoScape appear to have poor or non-existant support for CF_DIBV5 (Format17) pasting. you might consider copying to the clipboard the PNG format, with an opaque bitmap as backup just in case the target application does not support PNG. I use an extension method to facilitate this:
public static void CopyMultiFormatBitmapToClipboard(this Image image)
{
using (var opaque = image.CreateOpaqueBitmap(Color.White))
using (var stream = new MemoryStream())
{
image.Save(stream, ImageFormat.Png);
Clipboard.Clear();
var data = new DataObject();
data.SetData(DataFormats.Bitmap, true, opaque);
data.SetData("PNG", true, stream);
Clipboard.SetDataObject(data, true);
}
}
使用扩展方法,您的<$ c可以将$ c> __ copyMenuItem_Click()方法简化为以下内容,并且可以完全删除 SetClipboardData()
方法:
With the extension method in hand, your __copyMenuItem_Click()
method could be reduced to the following, and the SetClipboardData()
method could be removed altogether:
private void __copyMenuItem_Click(object sender, EventArgs e)
{
__pictureBox.Image.CopyMultiFormatBitmapToClipboard();
}
现在,正如我们已经在另一个线程上讨论的那样,PNG支持可能不会削减它为了你。我已经在一些应用程序上测试了这种方法;但是,由您来确定这是否足以满足您的需求。
Now, as we already discussed on another thread, PNG support may not cut it for you. I've tested this approach on a few applications; however, it will be up to you to determine whether this is sufficient transparency support for your requirements.
- GIMP:支持透明性
- 烟花(3.0):支持透明度
- PhotoScape:白色背景
- Paint.NET:白色背景
- MS PowerPoint 2007:支持透明度
- MS Word 2007:白色背景
- MS Paint(Win7):白色背景
- GIMP: transparency supported
- Fireworks (3.0): transparency supported
- PhotoScape: white background
- Paint.NET: white background
- MS PowerPoint 2007: transparency supported
- MS Word 2007: white background
- MS Paint (Win7): white background
对于堆栈溢出,我讨论的所有内容的讨论时间都太长了。我的博客中提供了其他示例代码和讨论:
Discussion of everything I looked into would be too lengthy for Stack Overflow. I have additional sample code and discussion available at my blog: http://www.notesoncode.com/articles/2010/08/16/HandlingTransparentImagesOnTheClipboardIsForSomeReasonHard.aspx
祝你好运!
这篇关于需要帮助将具有透明背景的图像设置到剪贴板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!