.NET,剪贴板和元文件

编程入门 行业动态 更新时间:2024-10-21 13:01:38
本文介绍了.NET,剪贴板和元文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我正在尝试使用.NET和C#绘制一个复制到剪贴板的图元文件,然后再用另一个应用程序(在这种情况下为Word 2003,但这没关系)。 br /> 我天真以为我能够使用Clipboard类从剪贴板中获取一个 的EnhancedMetafile或MetafilePict对象,用它来/> 创建一个新的Metafile对象,然后使用 Graphics.DrawImage绘制该Metafile对象。 它似乎没有就这么简单。 当我试图从剪贴板中获取EnhancedMetafile对象时,.NET 崩溃了。当我调试器运行时,MDA告诉我,我有一个 FatalExecutionEngineError,并且它是由于不正确使用不安全的 代码或者.NET中的错误。因为我没有使用任何不安全的代码,所以在.NET中留下了一个 的错误。 ???怎么会这么简单崩溃.NET? 无论如何,所以我尝试使用MetafilePict格式。这不是崩溃的.NET,但是返回的对象是一个MemoryStream。在和/ b $ b本身中,这似乎是合理的。您可以使用MemoryStream构造一个新的Metafile对象 。但是,返回的MemoryStream只有16个字节,而且是b $ b长,太短了,不能成为我想要的元文件。当然,我得到 a尝试使用这个MemoryStream对象构建一个 元文件的全新异常,但即使我没有,我肯定它不是我想要的数据。 我正在尝试使用的代码非常简单: mfstream =( Stream)Getboard.GetData(DataFormats.MetafilePict); metafile = new Metafile(mfstream); (或者只是崩溃的情况下的DataForms.EnhancedMetafile)在 ..NET代码中) 我的意思是,我写了其他代码来做检查剪贴板的事情 格式等等,但有问题的代码归结为这两行。 (是的,当我检查剪贴板中的格式时,两者都是增强的 元文件和MetaFile Pict都在那里。 我用Google搜索了一下这个问题,发现了两类消息: 的人也是无法弄清楚如何去做o这个,以及使用inter-op的建议。我实际上可以通过互操作来实现它,但我结束了 必须基本上使用* no * .NET的东西...我只是p /调用将元文件绘制到DC所需的基本 剪贴板和元文件函数, 传递e.Graphics对象为OnPaint处理程序提供的DC (目前,我正试图让这个工作起来......虽然 一旦我可以在屏幕上成功绘制元文件,我还有其他计划 吧:)。 如果我想这样做,我只会写一个原生的Win32程序。我想b $ b假设我最终可能会这样做,但我希望在 ..NET中这样做。我已经被宠坏了没有必要做任何真正的工作来构建UI。 :) 按照建议的在线消息的方式使用inter-op不起作用。特别是 ,他们建议使用常规的Win32剪贴板函数来获取元文件的句柄,然后构造一个新的元文件对象 使用那个句柄。当我尝试这个时,我得到了相同的一般性GDI +错误 异常,我只使用.NET的东西(尽管可能是因为不同的原因... b $ b原因...异常过于通用而无法确定)。 鉴于此过程中出现的兴趣程度,以及缺少有关*的有用信息的b $ b如何做到这一点,我没有希望。但我认为我需要尝试... b / b 任何阅读此内容的人都会成功使用.NET获取图元文件(增强版 或其他方式)从剪贴板中将其绘制到您的表单(或做任何事情 其他)?如果是这样,你怎么做?我做错了什么? 谢谢! Pete

解决方案

Peter Duniho < Np ********* @ NnOwSlPiAnMkwrote:

>我正在尝试使用.NET和C#绘制图元文件通过另一个应用程序复制到剪贴板(在这种情况下,Word 2003,但这不重要)。 使用inter-op的方式在线消息建议不起作用。特别是,他们建议使用常规的Win32剪贴板函数来获取图元文件的句柄,然后使用该句柄构造一个新的图元文件对象。当我尝试这个时,我得到了相同的一般GDI +错误例外,我只是使用.NET的东西(虽然可能是出于不同的原因......这个例外太过通用而无法确定)。

我能够使用interop来获取元文件的句柄,然后使用该句柄构造一个新的Metafile对象 ,没有任何 例外。这是我的代码。它演示了互操作(1)将EMF 复制到剪贴板上,(2)将剪贴板中的EMF粘贴到位图上。 我真的不喜欢我不知道剪贴板/元文件的内容是什么。 我无法用它做任何有用的东西。我不知道如何使用将元文件复制到剪贴板上除非通过使用 中间磁盘文件。而且我无法找到如何从剪贴板粘贴 图元文件,即你遇到的相同问题。 在控制台程序中,我无法''在 全部做任何剪贴板操作。 Clipboard.GetDataObject方法只返回n​​ull。我的怀疑是因为元文件有点令人厌烦,并且不能完全适应他们正在构建的类层次结构,因此它们是 只是没有实现它们。这只是一种怀疑。 使用System; 使用System.Collections.Generic; 使用System.Text; 使用System.Runtime.InteropServices; 使用System.Drawing; //使用System.Windows.Forms添加对System.Drawing 的引用; //使用System.Drawing.Imaging添加对System.Windows.Forms的引用 ; class program { 内部结构RECT {public int left; public int top; public int right; public int bottom;} [DllImport(" gdi32.dll")] internal static extern IntPtr CreateEnhMetaFile(IntPtr hdcRef,IntPtr zero,ref RECT rc,IntPtr zero2); [DllImport(" gdi32.dll")]内部静态extern IntPtr CloseEnhMetaFile(IntPtr hdc); [DllImport(" user32.dll")] [返回:MarshalAs(UnmanagedType.Bool)] 内部静态extern bool OpenClipboard(IntPtr hwnd); [DllImport(" user32.dll")] [return:MarshalAs(UnmanagedType.Bool)] 内部静态extern bool EmptyClipboard(); [DllImport( " user32.dll")]内部静态extern IntPtr SetClipboardData(uint format,IntPtr h); [DllImport(" user32.dll")] internal static extern IntPtr GetClipboardData(uint format); [DllImport(" user32.dll")] [return:MarshalAs(UnmanagedType.Bool)] 内部静态extern bool CloseClipboard(); [DllImport(" user32.dll")] internal static extern IntPtr GetDC(IntPtr hwnd); [DllImport(" user32.dll")]内部静态extern Int32 ReleaseDC(IntPtr hwnd,IntPtr hdc); [DllImport(" gdi32.dll")]内部静态extern bool DeleteObject(IntPtr hObject); [DllImport(" gdi32.dll")] internal static extern IntPtr CreateCompatibleDC(IntPtr hdc) ; [DllImport(" gdi32.dll")] internal static extern int DeleteDC(IntPtr hdc); [DllImport(" ; gdi32.dll")]内部静态extern IntPtr SelectObject(IntPtr hdc,IntPtr hgdiobj); [DllImport(" gdi32.dll")]内部静态extern int BitBlt(IntPtr hdcDst,int xDst,int yDst,int w,int h,IntPtr hdcSrc,int xSrc,int ySrc,int rop); static int SRCCOPY = 0x00CC0020; static uint CF_ENHMETAFILE = 14; public delegate void DrawCallback(Graphics g); ///< summary> ///绘制到我们存储在剪贴板上的EMF ///< / summary> ///< param name =" hwnd">将拥有剪贴板的窗口的句柄 (使用IntPtr.Zero为null)< / param> ///< param name =" width">宽度,以图形的逻辑单位< / param> ; ///< param name =" height"> 图纸的逻辑单位高度< / param> ///< param name =" dpmm">每毫米逻辑单位数< / param> ///< param name =" callback">用户提供的回调这是实际的 绘图< / param> 静态void CopyToClipboard(IntPtr hwnd,int width,int height,float dpmm,DrawCallback回调) {//宽度,高度是我们的DrawCallback将绘制的大小( 逻辑单位) // dpmm是每毫米的逻辑单位数 float widthmm =(float)width / dpmm; float heightmm = (float)height / dpmm; IntPtr hdc = GetDC(hwnd); RECT rc = new RECT(); rc.top = 0; rc.left = 0; rc.right =(int)(widthmm * 100.0); rc.bottom =(int)的(高mm * 100.0); // (以0.01mm为单位) IntPtr mdc = CreateEnhMetaFile(hdc,IntPtr.Zero,ref rc,IntPtr.Zero); 图形mg = Graphics.FromHdc(mdc); mg.PageUnit = GraphicsUnit.Millimeter; mg.PageScale = 1.0F / dpmm; 回调(mg); mg.Dispose(); IntPtr hemf = CloseEnhMetaFile(mdc ); ReleaseDC(hwnd,hdc); // OpenClipboard(hwnd); EmptyClipboard( ); SetClipboardData(CF_ENHMETAFILE,hemf); CloseClipboard(); } ///< summary> ///绘制位图b在坐标(x,y)的屏幕上 ///< / summary> static void SplashImage(System.Drawing.Bitmap b,int x,int y ) {IntPtr hbm = b.GetHbitmap(); IntPtr sdc = GetDC(IntPtr.Zero); IntPtr hdc = CreateCompatibleDC( sdc); SelectObject(hdc,hbm); BitBlt(sdc,x,y,b.Width,b.Height,hdc,0,0,SRCCOPY) ; DeleteDC(hdc); ReleaseDC(IntPtr.Zero,sdc); DeleteObject(hbm); } static void DrawRandomLines(Graphics g) {Random r = new Random(); Pen p = new Pen(Color .Red); for(int i = 0; i< 10; i ++) {g.DrawLine(p,r.Next(1000),r.Next (1000),r.Next(1000), r.Next(1000)); } p =新笔(颜色。蓝色); g.DrawLine(p,0,0,1000,1000); g.DrawLine(p,1000,0,0,1000); } static void Main(string [] args) { //首先我们''我会尝试粘贴一个EMF fr将剪贴板放到位图上, 并将其溅到屏幕上 OpenClipboard(IntPtr.Zero); IntPtr hemf = GetClipboardData(CF_ENHMETAFILE); CloseClipboard(); if(hemf!= IntPtr.Zero) {Metafile mf = new Metafile(hemf,true); // true表示我们不需要b $ b需要自己释放血液 位图b =新位图(1000,1000); 图形g = Graphics.FromImage(b); g.FillRectangle(Brushes.White,0,0,1000,1000); GraphicsUnit unit = GraphicsUnit.Millimeter ; RectangleF rsrc = mf.GetBounds(ref unit); g.DrawImage(mf,new Rectangle(0,0,1000,1000),rsrc,unit); SplashImage(b,0,0); System.Threading.Thread.Sleep(1000); } //现在我们将EMF复制到剪贴板上 CopyToClipboard(IntPtr.Zero,1000,1000,10,DrawRando mLines); } } - Lucian

彼得, 我记得这个问题在v1.1天回来了,但我认为它已经修复了 in v2.0。在v1.1中,我写了一个带有静态GetEMF() 函数的ClipboardEx类来完成繁琐的工作。我没有粘贴所有常量 等等,但你应该得到我正在做的事情的要点。无论如何,我已经用这个代码取得了很大的成功。 Jason Newell www.jasonnewell 公共抽象类ClipboardEx { public static System.Drawing.Imaging.Metafile GetEMF(IntPtr hWnd) { try { / *尝试打开剪贴板。 * / // if(OpenClipboard(GetClipboardOwner())) if(User32.OpenClipboard(hWnd)) { / *检查剪贴板数据格式。 * / 如果 (User32.IsClipboardFormatAvailable((uint)Clipboard Formats.CF_ENHMETAFILE)) { / *获取指向数据的指针。 * / IntPtr ptr = User32.GetClipboardData((uint)ClipboardFormats.CF_ ENHMETAFILE); if(!ptr.Equals(IntPtr) .Zero)) { / *返回元文件。 * / 返回新的Metafile(ptr,true); } else { 抛出新的System.Exception(从剪贴板中提取 CF_ENHMETAFILE时出错。) } } else { 抛出新的System.Exception(CF_ENHMETAFILE不是 在剪贴板中可用。"); } } 其他 { throw new System.Exception(打开剪贴板时出错。); } } catch(System.Exception e) { 扔e; } 终于 { / *重要的是关闭剪贴板。 * / User32.CloseClipboard(); } } } Peter Duniho写道:

我正在尝试使用.NET和C#绘制复制到剪贴板的元文件 另一个应用程序(在这种情况下是Word 2003,但这不重要)。 我天真以为我能够使用Clipboard类来获取一个 来自剪贴板的EnhancedMetafile或MetafilePict对象,使用它来创建一个新的Metafile对象,然后使用 Graphics绘制该Metafile对象.DrawImage。 看起来好像不那么简单。 当我尝试从剪贴板中获取EnhancedMetafile对象时, .NET 崩溃。当我调试器运行时,MDA告诉我,我有一个 FatalExecutionEngineError,并且它是由于不正确使用不安全的 代码或者.NET中的错误。因为我没有使用任何不安全的代码,所以在.NET中留下了一个 的错误。 ???怎么会这么简单崩溃.NET? 无论如何,所以我尝试使用MetafilePict格式。这不是崩溃的.NET,但是返回的对象是一个MemoryStream。在和/ b $ b本身中,这似乎是合理的。您可以使用MemoryStream构造一个新的Metafile对象 。但是,返回的MemoryStream只有16个字节,而且是b $ b长,太短了,不能成为我想要的元文件。当然,我得到 a尝试使用这个MemoryStream对象构建一个 元文件的全新异常,但即使我没有,我肯定它不是我想要的数据。 我正在尝试使用的代码非常简单: mfstream =( Stream)Getboard.GetData(DataFormats.MetafilePict); metafile = new Metafile(mfstream); (或者只是崩溃的情况下的DataForms.EnhancedMetafile)在 .NET代码中) 我的意思是,我写了其他代码来做检查剪贴板的事情 格式等等,但有问题的代码归结为这两行。 (是的,当我检查剪贴板中的格式时,两者都是增强的 元文件 ;和MetaFile Pict在那里)。 我用Google搜索了一下这个问题,发现了两类消息: 的人也可以弄清楚怎么做这样做,以及使用inter-op的建议。我实际上可以通过互操作来实现它,但我结束了 必须基本上使用* no * .NET的东西...我只是p /调用将元文件绘制到DC所需的基本 剪贴板和元文件函数, 传递e.Graphics对象为OnPaint处理程序提供的DC (目前,我正试图让这个工作起来......虽然 一旦我可以在屏幕上成功绘制元文件,我还有其他计划 吧:)。 如果我想这样做,我只会写一个原生的Win32程序。我想b $ b毕竟我可能会这样做,但我希望在 .NET中这样做。我已经被宠坏了没有必要做任何真正的工作来构建UI。 :) 按照建议的在线消息的方式使用inter-op不起作用。特别是 ,他们建议使用常规的Win32剪贴板函数来获取元文件的句柄,然后构造一个新的元文件对象 使用那个句柄。当我尝试这个时,我得到了相同的一般性GDI +错误 异常,我只使用.NET的东西(尽管可能是因为不同的原因... b $ b原因...异常过于通用而无法确定)。 鉴于此过程中出现的兴趣程度,以及缺少有关*的有用信息的b $ b如何做到这一点,我没有希望。但我认为我需要尝试... b / b 任何阅读此内容的人都会成功使用.NET获取图元文件(增强版 或其他方式)从剪贴板中将其绘制到您的表单(或做任何事情 其他)?如果是这样,你怎么做?我做错了什么? 谢谢! Pete

" Lucian Wischik" < lu *** @ wischik写信息 新闻:ak *************************** ***** @ 4ax ...

我能够使用互操作来获取元文件的句柄,然后 使用该句柄构造一个新的Metafile对象,没有任何 异常。这是我的代码。它演示了互操作(1)将EMF 复制到剪贴板上,(2)将剪贴板中的EMF粘贴到位图上。

谢谢! 您是否成功获得该代码以使用 $ b上的图元文件$ b剪贴板的其他一些过程?我注意到在你的示例代码中,你是正在创建元文件以及使用它。当然,你是使用原生的Win32 API创建它,但是我想知道a)代码是否只能使用某个元文件工作 ,或者b)事实是你正在使用.NET绘制 元文件有所不同(即使元文件本身是使用原生Win32函数初始化的)。 我问的原因是检索图元文件的代码基本上与我尝试的代码相同,但我的代码在调用元文件时失败了 构造(一般GDI +错误)。唯一的区别是元文件的 来源。

在控制台程序中,我无法在 全部。 Clipboard.GetDataObject方法只返回n​​ull。

嗯,原生API需要一个窗口句柄AFAIK。如果你传递NULL到 打开剪贴板,Windows默认为你的进程的主窗口,但它仍然使用 窗口。由于控制台程序没有窗口,因此剪贴板操作可能会失败并不会让我感到惊讶。

My 怀疑元文件有点令人讨厌,并且不适合他们正在构建的类层次结构,所以他们 只是没有实现它们。那只是一种怀疑。

嗯,我想这是可能的。但是为什么要在这个案例中公开一个图元文件类? ?从表面上看,Metafile类*出现*只是在常规Windows图元文件之上的一个 包装器。它似乎与.NET hiearchy非常吻合,与Bitmap共享相同的状态,因为 是从Image派生的对象。使用GDI +,有新的图元文件记录来处理,但是GDI +可以在.NET之外访问,所以这比一个.NET的东西更像是一个GDI +的东西。实际的对象层次结构似乎很容易。 此外,元文件是基本Windows进程间b / b 操作的核心组件,尤其是通过剪贴板。大多数程序都不理解其他程序的数据格式,但元文件是一种标准格式, 允许一个程序轻松地将其输出嵌入到另一个程序中。我会认为它应该是.NET的一个高优先级(即使看起来它不是)。 我想没有其他人使用 只是.NET工作,我不得不同意微软会因为处理而受到惩罚b $ b .NET中的元文件。但我仍然无法解释原因。它似乎并不像它应该那么难,即使它很难,它也很重要。 顺便说一下,有一点是也让我感到紧张的代码就像你发布的那样(我在其他地方见过的)是来自剪贴板的句柄 由Metafile对象自动处理当你传递真实时对于 构造函数的第二个参数。这很糟糕,因为从GetClipboardData函数获得的句柄 由剪贴板拥有,而不是由应用程序支持。如果它已被释放,那么剪贴板本身会在其中结束一个 无效句柄。 我尝试了两个true的代码。和假的对于该参数, 值不会影响我构建新的Metafile对象的成功。 但是,恕我直言,该参数的正确值是false。 实际上,如果你想对它挑剔,我会说正确的值是 实际上是真的 *和*剪贴板中的图元文件应该从剪贴板中获取后立即复制 ,然后关闭 剪贴板并构建.NET图元文件。在绘图完成之前,要么关闭剪贴板,要么不要关闭,以确保没有其他 进程使图元文件句柄无效。我的猜测是假。是好的 够了只是为了看到让事情变得有效。 无论如何,感谢您的投入...很高兴知道有人已经获得了 *在.NET下工作的一些*相似的剪贴板操作,即使.NET 本身似乎不支持它。 Inter-op是一种痛苦,但是在没有.NET表单的情况下完成一个完整的GUI是更加痛苦的恕我直言。我想这是两个邪恶中的两个小b / b 。 :) Pete

I''m trying to use .NET and C# to draw a metafile copied to the clipboard by another application (Word 2003 in this case, but it shouldn''t matter). I naively thought that I''d be able to use the Clipboard class to get an EnhancedMetafile or MetafilePict object from the Clipboard, use that to create a new Metafile object, and then draw that Metafile object using Graphics.DrawImage. It doesn''t seem to be that simple. When I try to get the EnhancedMetafile object from the Clipboard, .NET crashes. When I have the debugger running, the MDA tells me I''ve got a FatalExecutionEngineError and that it''s due either to improper use of unsafe code or a bug in .NET. Since I''m not using any unsafe code, that leaves a bug in .NET. ??? How could something this simple crash .NET? Anyway, so I tried using the MetafilePict format instead. This doesn''t crash .NET, but the object that''s returned is a MemoryStream. In and of itself, that seems reasonable. You can construct a new Metafile object using a MemoryStream. However, the MemoryStream returned is only 16 bytes long, much too short to be the metafile I''m trying to get. Of course, I get a whole new exception trying to use this MemoryStream object to construct a Metafile, but even if I didn''t, I''m sure it wouldn''t be the data I want. The code I''m trying to use is pretty straightforward: mfstream = (Stream)Clipboard.GetData(DataFormats.MetafilePict ); metafile = new Metafile(mfstream); (or DataForms.EnhancedMetafile for the case that just crashes within the ..NET code) I mean, I''ve written other code to do things like check the clipboard formats and whatnot, but the problematic code boils down to those two lines. (And yes, when I check the formats in the clipboard, both "Enhanced Metafile" and "MetaFile Pict" are in there). I Googled the issue a bit, and found two categories of messages: people who also can''t figure out how to do this, and suggestions to use inter-op. I actually was able to inter-op to sort of accomplish it, but I wound up having to basically use *no* .NET stuff...I just p/invoked the basic clipboard and metafile functions required to draw a metafile to a DC, passing the DC provided by the e.Graphics object for the OnPaint handler (which is where, for the moment, I''m trying to get this to work...though once I can draw the metafile succesfully on the screen, I have other plans for it :) ). If I wanted to do it that way, I''d just write a native Win32 program. I suppose I may wind up doing that after all, but I was hoping to do this in ..NET. I''ve gotten spoiled not having to do any real work to build a UI. :) Using inter-op in the way the online messages suggested did not work. In particular, they recommend using the regular Win32 clipboard functions to get the handle to the metafile, and then construct a new Metafile object using that handle. When I try that, I get the same general "GDI+ error" exception that I get just using .NET stuff (though perhaps for different reasons...the exception is too generic to know for sure). Given the degree of interest that there appears in doing this, and the lack of useful information about *how* to do it, I''m not hopeful. But I figure I gotta try... Anyone reading this ever successfully use .NET to get a metafile (enhanced or otherwise) from the clipboard and draw it to your form (or do anything else with it)? If so, how do you do it? What am I doing wrong? Thanks! Pete

解决方案

"Peter Duniho" <Np*********@NnOwSlPiAnMkwrote:

>I''m trying to use .NET and C# to draw a metafile copied to the clipboard byanother application (Word 2003 in this case, but it shouldn''t matter).Using inter-op in the way the online messages suggested did not work. Inparticular, they recommend using the regular Win32 clipboard functions toget the handle to the metafile, and then construct a new Metafile objectusing that handle. When I try that, I get the same general "GDI+ error"exception that I get just using .NET stuff (though perhaps for differentreasons...the exception is too generic to know for sure).

I''m able to use interop to get a handle to the metafile, and then construct a new Metafile object using that handle, without any exceptions. Here''s my code. It demonstrates interop to (1) copy an EMF onto the clipboard, (2) paste an EMF from the clipboard onto a Bitmap. I really don''t know what''s up with the clipboard/metafile stuff. I wasn''t able to do ANYTHING useful with it. I couldn''t find out how to copy a metafile onto the clipboard with except through using an intermediate diskfile. And I couldn''t find out how to paste a metafile from the clipboard, i.e. the same problems you were having. And in a console program I couldn''t do ANY clipboard manipulation at all. The Clipboard.GetDataObject method simply returned null. My suspicion is that metafiles are a little irksome, and didn''t fit cleanly into the class hierarchies they were building, so they just didn''t implement them. That''s just a suspicion. using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Drawing; // add reference to System.Drawing using System.Windows.Forms; // add reference to System.Windows.Forms using System.Drawing.Imaging; class Program { internal struct RECT {public int left; public int top; public int right; public int bottom;} [DllImport("gdi32.dll")] internal static extern IntPtr CreateEnhMetaFile(IntPtr hdcRef, IntPtr zero, ref RECT rc, IntPtr zero2); [DllImport("gdi32.dll")] internal static extern IntPtr CloseEnhMetaFile(IntPtr hdc); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool OpenClipboard(IntPtr hwnd); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool EmptyClipboard(); [DllImport("user32.dll")] internal static extern IntPtr SetClipboardData(uint format, IntPtr h); [DllImport("user32.dll")] internal static extern IntPtr GetClipboardData(uint format); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CloseClipboard(); [DllImport("user32.dll")] internal static extern IntPtr GetDC(IntPtr hwnd); [DllImport("user32.dll")] internal static extern Int32 ReleaseDC(IntPtr hwnd,IntPtr hdc); [DllImport("gdi32.dll")] internal static extern bool DeleteObject(IntPtr hObject); [DllImport("gdi32.dll")] internal static extern IntPtr CreateCompatibleDC(IntPtr hdc); [DllImport("gdi32.dll")] internal static extern int DeleteDC(IntPtr hdc); [DllImport("gdi32.dll")] internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); [DllImport("gdi32.dll")] internal static extern int BitBlt(IntPtr hdcDst, int xDst, int yDst, int w, int h, IntPtr hdcSrc, int xSrc, int ySrc, int rop); static int SRCCOPY = 0x00CC0020; static uint CF_ENHMETAFILE = 14; public delegate void DrawCallback(Graphics g); /// <summary> /// Draws onto an EMF that we store on the clipboard /// </summary> /// <param name="hwnd">Handle of a window that will own the clipboard (use IntPtr.Zero for null)</param> /// <param name="width">width in logical units of the drawing</param> /// <param name="height">height in logical units of the drawing</param> /// <param name="dpmm">how many logical units per millimeter</param> /// <param name="callback">user-supplied callback that does the actual drawing</param> static void CopyToClipboard(IntPtr hwnd, int width, int height, float dpmm, DrawCallback callback) { // width,height are the size that our DrawCallback will draw (in logical units) // dpmm is the number of logical units per millimeter float widthmm = (float)width/dpmm; float heightmm = (float)height/dpmm; IntPtr hdc = GetDC(hwnd); RECT rc = new RECT(); rc.top=0; rc.left=0; rc.right=(int)(widthmm*100.0); rc.bottom=(int)(heightmm*100.0); // (it''s in units of 0.01mm) IntPtr mdc = CreateEnhMetaFile(hdc,IntPtr.Zero,ref rc,IntPtr.Zero); Graphics mg = Graphics.FromHdc(mdc); mg.PageUnit = GraphicsUnit.Millimeter; mg.PageScale = 1.0F/dpmm; callback(mg); mg.Dispose(); IntPtr hemf = CloseEnhMetaFile(mdc); ReleaseDC(hwnd,hdc); // OpenClipboard(hwnd); EmptyClipboard(); SetClipboardData(CF_ENHMETAFILE,hemf); CloseClipboard(); } /// <summary> /// Draws a bitmap "b" onto the screen at coordinates (x,y) /// </summary> static void SplashImage(System.Drawing.Bitmap b, int x, int y) { IntPtr hbm = b.GetHbitmap(); IntPtr sdc = GetDC(IntPtr.Zero); IntPtr hdc = CreateCompatibleDC(sdc); SelectObject(hdc,hbm); BitBlt(sdc,x,y,b.Width,b.Height,hdc,0,0,SRCCOPY); DeleteDC(hdc); ReleaseDC(IntPtr.Zero,sdc); DeleteObject(hbm); } static void DrawRandomLines(Graphics g) { Random r = new Random(); Pen p = new Pen(Color.Red); for (int i=0; i<10; i++) { g.DrawLine(p,r.Next(1000), r.Next(1000), r.Next(1000), r.Next(1000)); } p = new Pen(Color.Blue); g.DrawLine(p,0,0,1000,1000); g.DrawLine(p,1000,0,0,1000); } static void Main(string[] args) { // First we''ll try to paste an EMF from the clipboard onto a bitmap, and splash it onscreen OpenClipboard(IntPtr.Zero); IntPtr hemf = GetClipboardData(CF_ENHMETAFILE); CloseClipboard(); if (hemf!=IntPtr.Zero) { Metafile mf = new Metafile(hemf,true); // true means that we don''t need to release the hemf ourselves Bitmap b = new Bitmap(1000,1000); Graphics g = Graphics.FromImage(b); g.FillRectangle(Brushes.White,0,0,1000,1000); GraphicsUnit unit = GraphicsUnit.Millimeter; RectangleF rsrc = mf.GetBounds(ref unit); g.DrawImage(mf, new Rectangle(0,0,1000,1000), rsrc, unit); SplashImage(b,0,0); System.Threading.Thread.Sleep(1000); } // Now we''ll copy an EMF onto the clipboard CopyToClipboard(IntPtr.Zero,1000,1000,10,DrawRando mLines); } } -- Lucian

Peter, I remember this problem back in the v1.1 days but I thought it was fixed in v2.0. In v1.1, I wrote a ClipboardEx class with a static GetEMF() function to do the grunt work. I didn''t paste in all of the constants and such but you should get the gist of what I''m doing. Anyway, I''ve used this code with much success. Jason Newell www.jasonnewell public abstract class ClipboardEx { public static System.Drawing.Imaging.Metafile GetEMF(IntPtr hWnd) { try { /* Attempt to open the Clipboard. */ //if (OpenClipboard(GetClipboardOwner())) if (User32.OpenClipboard(hWnd)) { /* Check the Clipboard data format. */ if (User32.IsClipboardFormatAvailable((uint)Clipboard Formats.CF_ENHMETAFILE)) { /* Get the pointer to the data. */ IntPtr ptr = User32.GetClipboardData((uint)ClipboardFormats.CF_ ENHMETAFILE); if (!ptr.Equals(IntPtr.Zero)) { /* Return the Metafile. */ return new Metafile(ptr, true); } else { throw new System.Exception("Error extracting CF_ENHMETAFILE from clipboard."); } } else { throw new System.Exception("CF_ENHMETAFILE is not available in clipboard."); } } else { throw new System.Exception("Error opening clipboard."); } } catch (System.Exception e) { throw e; } finally { /* Important to close the Clipboard. */ User32.CloseClipboard(); } } } Peter Duniho wrote:

I''m trying to use .NET and C# to draw a metafile copied to the clipboard by another application (Word 2003 in this case, but it shouldn''t matter). I naively thought that I''d be able to use the Clipboard class to get an EnhancedMetafile or MetafilePict object from the Clipboard, use that to create a new Metafile object, and then draw that Metafile object using Graphics.DrawImage. It doesn''t seem to be that simple. When I try to get the EnhancedMetafile object from the Clipboard, .NET crashes. When I have the debugger running, the MDA tells me I''ve got a FatalExecutionEngineError and that it''s due either to improper use of unsafe code or a bug in .NET. Since I''m not using any unsafe code, that leaves a bug in .NET. ??? How could something this simple crash .NET? Anyway, so I tried using the MetafilePict format instead. This doesn''t crash .NET, but the object that''s returned is a MemoryStream. In and of itself, that seems reasonable. You can construct a new Metafile object using a MemoryStream. However, the MemoryStream returned is only 16 bytes long, much too short to be the metafile I''m trying to get. Of course, I get a whole new exception trying to use this MemoryStream object to construct a Metafile, but even if I didn''t, I''m sure it wouldn''t be the data I want. The code I''m trying to use is pretty straightforward: mfstream = (Stream)Clipboard.GetData(DataFormats.MetafilePict ); metafile = new Metafile(mfstream); (or DataForms.EnhancedMetafile for the case that just crashes within the .NET code) I mean, I''ve written other code to do things like check the clipboard formats and whatnot, but the problematic code boils down to those two lines. (And yes, when I check the formats in the clipboard, both "Enhanced Metafile" and "MetaFile Pict" are in there). I Googled the issue a bit, and found two categories of messages: people who also can''t figure out how to do this, and suggestions to use inter-op. I actually was able to inter-op to sort of accomplish it, but I wound up having to basically use *no* .NET stuff...I just p/invoked the basic clipboard and metafile functions required to draw a metafile to a DC, passing the DC provided by the e.Graphics object for the OnPaint handler (which is where, for the moment, I''m trying to get this to work...though once I can draw the metafile succesfully on the screen, I have other plans for it :) ). If I wanted to do it that way, I''d just write a native Win32 program. I suppose I may wind up doing that after all, but I was hoping to do this in .NET. I''ve gotten spoiled not having to do any real work to build a UI. :) Using inter-op in the way the online messages suggested did not work. In particular, they recommend using the regular Win32 clipboard functions to get the handle to the metafile, and then construct a new Metafile object using that handle. When I try that, I get the same general "GDI+ error" exception that I get just using .NET stuff (though perhaps for different reasons...the exception is too generic to know for sure). Given the degree of interest that there appears in doing this, and the lack of useful information about *how* to do it, I''m not hopeful. But I figure I gotta try... Anyone reading this ever successfully use .NET to get a metafile (enhanced or otherwise) from the clipboard and draw it to your form (or do anything else with it)? If so, how do you do it? What am I doing wrong? Thanks! Pete

"Lucian Wischik" <lu***@wischikwrote in message news:ak********************************@4ax...

I''m able to use interop to get a handle to the metafile, and then construct a new Metafile object using that handle, without any exceptions. Here''s my code. It demonstrates interop to (1) copy an EMF onto the clipboard, (2) paste an EMF from the clipboard onto a Bitmap.

Thanks! Are you successful in getting that code to use a metafile put on the clipboard by some other process? I notice that in your example code, you are creating the metafile as well as consuming it. Granted, you are creating it using the native Win32 API, but I wonder if a) the code works only with a certain metafile, or if b) the fact that you''re drawing the metafile using .NET makes a difference (even though the metafile itself was initialized using the native Win32 function). The reason I ask is that the code to retrieve the metafile is basically identical to the code I tried, but my code fails on the call to the Metafile construction (with the general GDI+ error). The only difference is the source of the metafile.

And in a console program I couldn''t do ANY clipboard manipulation at all. The Clipboard.GetDataObject method simply returned null.

Well, the native API requires a window handle, AFAIK. If you pass NULL to open the clipboard, Windows defaults to your process''s main window, but a window it still used. Since a console program doesn''t have a window, it doesn''t surprise me that clipboard operations might fail.

My suspicion is that metafiles are a little irksome, and didn''t fit cleanly into the class hierarchies they were building, so they just didn''t implement them. That''s just a suspicion.

Well, I suppose that''s possible. But why expose a Metafile class at all in that case? On the surface, the Metafile class *appears* to simply be a wrapper on top of the regular Windows metafile stuff. It seems to fit in pretty well with the .NET hiearchy, sharing equal status with the Bitmap as an object derived from Image. With GDI+, there are new metafile records to handle, but GDI+ is accessible outside of .NET so this is more a GDI+ thing than a .NET thing. The actual object hierarchy seems easy enough. In addition, metafiles are a core component of basic Windows inter-process operations, especially via the clipboard. Most programs don''t understand other programs'' data formats, but metafiles are a standard format that allows one program to easily imbed their output into another program. I''d think it ought to be a high priority for .NET (even if it appears that it''s not). I guess absent input from someone else who has gotten this to work using just .NET, I''ll have to agree that Microsoft punted on dealing with metafiles in .NET. But I''m still at a loss to explain why. It doesn''t seem like it should be that hard, and even if it is hard, it''s also important. By the way, one thing that also makes me nervous about the code like you posted (and that I''ve seen elsewhere) is that the handle from the clipboard is automatically disposed by the Metafile object when you pass "true" for the constructor''s second parameter. This is bad, because the handle obtained from the GetClipboardData function is owned by the clipboard, not by the application. If it''s freed, then the clipboard itself winds up an invalid handle in it. I tried the code with both "true" and "false" for that parameter, and the value did not affect my success in constructing a new Metafile object. However, IMHO the correct value for that parameter is "false". Actually, if you want to get picky about it, I''d say the correct value is actually "true" *and* the metafile from the clipboard ought to be copied immediately after getting it from the clipboard, and before closing the clipboard and constructing the .NET Metafile. Either that, or don''t close the clipboard until after the drawing is done, to ensure that no other process invalidates the metafile handle. My guess is that "false" is "good enough" just to see about getting things to work though. Anyway, thanks for your input...it''s nice to know that someone has gotten *some* semblance of clipboard operations to work under .NET, even if .NET itself doesn''t appear to support it. Inter-op is a pain, but doing a full GUI without .NET forms is even more of a pain IMHO. I guess that''s the lesser of two evils. :) Pete

更多推荐

.NET,剪贴板和元文件

本文发布于:2023-11-03 15:20:38,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1555435.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:剪贴板   文件   NET

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!