|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
.NET, Clipboard, and metafilesanother 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" <NpOeStPe***@NnOwSlPiAnMk.com> wrote: I'm able to use interop to get a handle to the metafile, and then>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). > >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). 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 .net 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 .net 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 .net 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,DrawRandomLines); } } -- Lucian "Lucian Wischik" <lu***@wischik.com> wrote in message Thanks!news:aks7n2lvmi8t1gg8tdg36s6ehjeh6hvndb@4ax.com... > 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. 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 Well, the native API requires a window handle, AFAIK. If you pass NULL to > all. The Clipboard.GetDataObject method simply returned null. 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 Well, I suppose that's possible. But why expose a Metafile class at all in > suspicion is that metafiles are a little irksome, and didn't fit > cleanly into the .net class hierarchies they were building, so they > just didn't implement them. That's just a suspicion. 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 "Peter Duniho" <NpOeStPe***@NnOwSlPiAnMk.com> wrote: Yes, I developed my code using Word+Wordpad+Powerpoint as the source>Are you successful in getting that code to use a metafile put on the >clipboard by some other process? of my pasting operation, and target of my copying operation. (I only combined the two together once it was finished in preparation for posting here). >the fact that you're drawing the No it definitely doesn't.>metafile using .NET makes a difference (even though the metafile itself was >initialized using the native Win32 function). >Well, the native API requires a window handle, AFAIK. If you pass NULL to Passing NULL to OpenClipboard works for a *console* program when I use>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. the win32 api. (the code I pasted was a console program). But yes, reading the win32 docs, this does seem dodgy. It suggests that by passing NULL, some subsequent call to EmptyClipboard will fail. >> My Reading through the docs, almost all of the metafile constructors were>> suspicion is that metafiles are a little irksome, and didn't fit >> cleanly into the .net 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? for loading metafiles from somewhere else (be it stream or diskfile). At least with a bitmap, you can create the bitmap and draw on it and at every stage in the process it is a valid bitmap. The same isn't true for win32 metafiles: you create them, and draw on them, but you never actually receive the metafile handle until after you've "closed" the drawing process. That's what I mean to say that metafiles don't fit naturally into the framework. They're not really like bitmaps or other images. And other members of the Image class don't really work, like "RotateFlip" or "IsAlphaPixelFormat" or "FromHBitmap". I even wonder if "Metafile.Clone" is implemented? Now reading metafiles from disk or stream seems to work fine in the ..net framework. I think that's why they exposed the Metafile class, even though (as per above) they skimped on creating of metafiles. The final questions: how to read a metafile off the clipboard, and how to do it in the .net framework? What on earth is that 16-byte stream that you get out of GetData(Metafile)? (it seems to have the same format each time: the number 8, two words, and one dword). And why does GetData(EnhMetaFile) not work? completely bizarre, all this. >By the way, one thing that also makes me nervous about the code like you Good point. I agree.>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. >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. "Lucian Wischik" <lu***@wischik.com> wrote in message Yeah, looks like my guess was at least not entirely correct. Jason says news:tjr8n2ps0p1he06hhsqej378dqch4r80p8@4ax.com... > [...] > Passing NULL to OpenClipboard works for a *console* program when I use > the win32 api. (the code I pasted was a console program). But yes, > reading the win32 docs, this does seem dodgy. It suggests that by > passing NULL, some subsequent call to EmptyClipboard will fail. he's used the clipboard from a console application fine, so obviously it can work. I'm not sure what trouble you ran into, but I'll stop guessing now. :) I see what you mean. But I'm not sure why that would be a reason to simply > Reading through the docs, almost all of the metafile constructors were > for loading metafiles from somewhere else (be it stream or diskfile). > At least with a bitmap, you can create the bitmap and draw on it and > at every stage in the process it is a valid bitmap. The same isn't > true for win32 metafiles: you create them, and draw on them, but you > never actually receive the metafile handle until after you've "closed" > the drawing process. not finish implementing the Metafile functionality. I'd say either don't do it, or do it right. Seems like they took the intermediate "do it half-way" route. Of course, it's not clear to me whether it's the Clipboard stuff or the Metafile stuff that's done halfway, or both. After all, simply attempting to *get* the metafile data from the Clipboard when asking for an enhanced metafile fails. Has nothing to do with the Metafile class at that point. > That's what I mean to say that metafiles don't fit naturally into the Well, IMHO that's a class hierarchy design error. After all, given that > framework. They're not really like bitmaps or other images. And other > members of the Image class don't really work, like "RotateFlip" or > "IsAlphaPixelFormat" or "FromHBitmap". I even wonder if > "Metafile.Clone" is implemented? there's a perfectly good Bitmap class *derived* from the Image class, it's silly for the Image class itself to have bitmap-specific methods and properties. Rotate and flip stuff ought to work fine with metafiles. It should be simple enough to either recreate the metafile itself with the appropriate transformation matrix, or within the Metafile class track a transformation matrix applied whenever the metafile is drawn. But other methods such as pixel format and getting a bitmap handle, those don't belong in the Image class at all. That they are suggests that the class authors weren't even thinking about metafiles when they started. It's not a reason for metafiles to be left out...just an indication that they were. > [...] I completely agree. :)> The final questions: how to read a metafile off the clipboard, and how > to do it in the .net framework? What on earth is that 16-byte stream > that you get out of GetData(Metafile)? (it seems to have the same > format each time: the number 8, two words, and one dword). And why > does GetData(EnhMetaFile) not work? completely bizarre, all this. On the bright side, it turns out it was a simple error in my inter-op code that was preventing me from getting a metafile from the clipboard (see my other post). So at least I've got *something* working now. :) Anyway, thanks for the information and advice. Pete "Peter Duniho" <NpOeStPe***@NnOwSlPiAnMk.com> wrote: (1) Create a c# console app, past my code exactly as it appears, and>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. see if you still get the same failure. (2) tell us what the source of your metafile is so we can try to reproduce it. -- Lucian Hello:
Have you checked the versions of the dll's your're using for interop? Any system patch, service pack missing? There's a chance that it could be the problem... Good luck, i learned something about clipboarding reading your conversation :) Peter Duniho wrote: Show quote > "Lucian Wischik" <lu***@wischik.com> wrote in message > news:aks7n2lvmi8t1gg8tdg36s6ehjeh6hvndb@4ax.com... > > 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 .net 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 Hello:
Have you checked the versions of the dll's your're using for interop? Any system patch, service pack missing? There's a chance that it could be the problem... Good luck, i learned something about clipboarding reading your conversation :) Oscar Acosta Peter Duniho wrote: Show quote > "Lucian Wischik" <lu***@wischik.com> wrote in message > news:aks7n2lvmi8t1gg8tdg36s6ehjeh6hvndb@4ax.com... > > 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 .net 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 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.net 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)ClipboardFormats.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: Show quote > 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 > > "Jason Newell" <no@spam.com> wrote in message Thanks for the reply. Unfortunately, I've already basically tried that. As news:e0x5WE9FHHA.960@TK2MSFTNGP04.phx.gbl... > 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. I mentioned in the reply to Lucian that I just posted, it doesn't work for me. Does your code to work with metafiles put on the clipboard by other applications? Also, as with all the other examples, I note that you pass "true" for the "delete on dispose" parameter to the Metafile constructor. As I described in that same reply, I suspect that's dangerous to do. Though, I can't explain why if it's so dangerous, it shows up in every example (I've seen bugs perpetuated like that in other FAQ-like code posted, for .NET and other environments). Pete Peter,
Sorry it's not working for you. Yes, I was using a 3rd party CAD ActiveX control to put the Metafile on the clipboard. Something like: ActiveX.CopyEMFToClipboard(); ClipboardEx().GetEMF(); I guess I never put that much thought into the passing "true" into the constructor. It worked and I was happy ;-). You say that you're using Word to copy the Metafile to the clipboard. I'll whip up a test program to see if I can reproduce the problem and let you know. Jason Newell www.jasonnewell.net Peter Duniho wrote: Show quote > "Jason Newell" <no@spam.com> wrote in message > news:e0x5WE9FHHA.960@TK2MSFTNGP04.phx.gbl... >> 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. > > Thanks for the reply. Unfortunately, I've already basically tried that. As > I mentioned in the reply to Lucian that I just posted, it doesn't work for > me. Does your code to work with metafiles put on the clipboard by other > applications? > > Also, as with all the other examples, I note that you pass "true" for the > "delete on dispose" parameter to the Metafile constructor. As I described > in that same reply, I suspect that's dangerous to do. Though, I can't > explain why if it's so dangerous, it shows up in every example (I've seen > bugs perpetuated like that in other FAQ-like code posted, for .NET and other > environments). > > Pete > > Peter,
I wrote a small console application. I did a "Print Screen", pasted into Microsoft Word 2003, right clicked on the image and selected "Copy", then ran the console application. It worked fine on my machine. Metafile metafile = ClipboardEx.GetEMF(User32.GetDesktopWindow()); metafile.Save(@"C:\desktop.emf"); If you'd like, I can upload the source to my website and make it available for download if you want to try it. Jason Newell www.jasonnewell.net Jason Newell wrote: Show quote > Peter, > > Sorry it's not working for you. Yes, I was using a 3rd party CAD > ActiveX control to put the Metafile on the clipboard. Something like: > > ActiveX.CopyEMFToClipboard(); > ClipboardEx().GetEMF(); > > I guess I never put that much thought into the passing "true" into the > constructor. It worked and I was happy ;-). > > You say that you're using Word to copy the Metafile to the clipboard. > I'll whip up a test program to see if I can reproduce the problem and > let you know. > > Jason Newell > www.jasonnewell.net > > Peter Duniho wrote: >> "Jason Newell" <no@spam.com> wrote in message >> news:e0x5WE9FHHA.960@TK2MSFTNGP04.phx.gbl... >>> 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. >> >> Thanks for the reply. Unfortunately, I've already basically tried >> that. As I mentioned in the reply to Lucian that I just posted, it >> doesn't work for me. Does your code to work with metafiles put on the >> clipboard by other applications? >> >> Also, as with all the other examples, I note that you pass "true" for >> the "delete on dispose" parameter to the Metafile constructor. As I >> described in that same reply, I suspect that's dangerous to do. >> Though, I can't explain why if it's so dangerous, it shows up in every >> example (I've seen bugs perpetuated like that in other FAQ-like code >> posted, for .NET and other environments). >> >> Pete >> "Jason Newell" <no@spam.com> wrote in message That would be wonderful. Maybe I did something wrong with the inter-op news:%23%23ARDh9FHHA.1804@TK2MSFTNGP02.phx.gbl... > [...] > If you'd like, I can upload the source to my website and make it available > for download if you want to try it. declarations that's causing problems. Having actual working code in front of me would surely go a long way in helping understand what I'm doing wrong. Thanks! Pete
http://www.jasonnewell.net/public/Examples/ClipEMF.zip
Let me know how it goes. Jason Newell www.jasonnewell.net Peter Duniho wrote: Show quote > "Jason Newell" <no@spam.com> wrote in message > news:%23%23ARDh9FHHA.1804@TK2MSFTNGP02.phx.gbl... >> [...] >> If you'd like, I can upload the source to my website and make it available >> for download if you want to try it. > > That would be wonderful. Maybe I did something wrong with the inter-op > declarations that's causing problems. Having actual working code in front > of me would surely go a long way in helping understand what I'm doing wrong. > > Thanks! > Pete > > "Jason Newell" <no@spam.com> wrote in message Thanks!news:OwKLCs9FHHA.960@TK2MSFTNGP04.phx.gbl... > http://www.jasonnewell.net/public/Examples/ClipEMF.zip > > Let me know how it goes. Of course, even before I saw this message, I did manage to get my code to work. For some reason, it looks like I was trying to get the old version metafile only. Of course, the Metafile constructor requires an enhanced metafile (at least, that's what it looks like to me from the documentation). Once I made sure I was getting the right type of metafile from the clipboard, it worked fine. I could've sworn that I had already tried that, but it seems obvious at this point that I didn't. Still, I've downloaded your code and I will be checking it out, to make sure that I haven't made any other silly mistakes, and just to compare/contrast with the code I came up with to see if there's anything I left out, or a different technique that works better. And of course, I'm still frustrated that the .NET clipboard access stuff doesn't work for this. It makes me wonder what other sorts of things it doesn't work with. Maybe the issues I was having are more some general issue with the Clipboard class, rather than the Metafile class. Thanks again! Pete Peter,
Glad you got it working. Fortunately for you, others have already experienced this pain and had the answer. Take care. Jason Newell www.jasonnewell.net Peter Duniho wrote: Show quote > "Jason Newell" <no@spam.com> wrote in message > news:OwKLCs9FHHA.960@TK2MSFTNGP04.phx.gbl... >> http://www.jasonnewell.net/public/Examples/ClipEMF.zip >> >> Let me know how it goes. > > Thanks! > > Of course, even before I saw this message, I did manage to get my code to > work. For some reason, it looks like I was trying to get the old version > metafile only. Of course, the Metafile constructor requires an enhanced > metafile (at least, that's what it looks like to me from the documentation). > Once I made sure I was getting the right type of metafile from the > clipboard, it worked fine. > > I could've sworn that I had already tried that, but it seems obvious at this > point that I didn't. > > Still, I've downloaded your code and I will be checking it out, to make sure > that I haven't made any other silly mistakes, and just to compare/contrast > with the code I came up with to see if there's anything I left out, or a > different technique that works better. > > And of course, I'm still frustrated that the .NET clipboard access stuff > doesn't work for this. It makes me wonder what other sorts of things it > doesn't work with. Maybe the issues I was having are more some general > issue with the Clipboard class, rather than the Metafile class. > > Thanks again! > Pete > > |
|||||||||||||||||||||||