By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
459,910 Members | 1,625 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 459,910 IT Pros & Developers. It's quick & easy.

.NET, Clipboard, and metafiles

P: n/a
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
Dec 4 '06 #1
Share this Question
Share on Google+
15 Replies


P: n/a
"Peter Duniho" <Np*********@NnOwSlPiAnMk.comwrote:
>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).
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 .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,DrawRando mLines);
}

}
--
Lucian
Dec 4 '06 #2

P: n/a
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)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

Dec 4 '06 #3

P: n/a
"Lucian Wischik" <lu***@wischik.comwrote in message
news:ak********************************@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
Dec 4 '06 #4

P: n/a
"Jason Newell" <no@spam.comwrote in message
news:e0*************@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
Dec 4 '06 #5

P: n/a
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.comwrote in message
news:e0*************@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

Dec 4 '06 #6

P: n/a
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:
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.comwrote in message
news:e0*************@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
Dec 4 '06 #7

P: n/a
"Jason Newell" <no@spam.comwrote in message
news:%2******************@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
Dec 4 '06 #8

P: n/a
"Peter Duniho" <Np*********@NnOwSlPiAnMk.comwrote:
>Are you successful in getting that code to use a metafile put on the
clipboard by some other process?
Yes, I developed my code using Word+Wordpad+Powerpoint as the source
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
metafile using .NET makes a difference (even though the metafile itself was
initialized using the native Win32 function).
No it definitely doesn't.

>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.
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.

>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?
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.

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
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.
Good point. I agree.
Dec 4 '06 #9

P: n/a
"Peter Duniho" <Np*********@NnOwSlPiAnMk.comwrote:
>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.
(1) Create a c# console app, past my code exactly as it appears, and
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
Dec 4 '06 #10

P: n/a
http://www.jasonnewell.net/public/Examples/ClipEMF.zip

Let me know how it goes.

Jason Newell
www.jasonnewell.net

Peter Duniho wrote:
"Jason Newell" <no@spam.comwrote in message
news:%2******************@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

Dec 4 '06 #11

P: n/a
"Lucian Wischik" <lu***@wischik.comwrote in message
news:tj********************************@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.
Yeah, looks like my guess was at least not entirely correct. Jason says
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.
:)
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.
I see what you mean. But I'm not sure why that would be a reason to simply
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
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?
Well, IMHO that's a class hierarchy design error. After all, given that
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.
[...]
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.
I completely agree. :)

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
Dec 5 '06 #12

P: n/a
"Jason Newell" <no@spam.comwrote in message
news:Ow*************@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
Dec 5 '06 #13

P: n/a
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:
"Jason Newell" <no@spam.comwrote in message
news:Ow*************@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

Dec 5 '06 #14

P: n/a
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:
"Lucian Wischik" <lu***@wischik.comwrote in message
news:ak********************************@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
Dec 5 '06 #15

P: n/a
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:
"Lucian Wischik" <lu***@wischik.comwrote in message
news:ak********************************@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
Dec 5 '06 #16

This discussion thread is closed

Replies have been disabled for this discussion.