过去两周我一直在搜索这个答案,但我很难过。
我正在使用一些代码,这些代码从一个元文件构造的Graphics对象创建一个示例图像,所有代码都驻留在内存流中,以避免需要Windows.Forms(它是一个控制台应用程序),而且我m使用函数CopyEnhMetaFile
(从gdi32.dll
导入),将元文件作为真正的EMF保存到磁盘。您可以查看here,here,here和here,了解我如何将其整合在一起的基本说明。
当我将其自上而下编写为简单的main()脚本(如codeproject示例中所示)时,它工作正常。但是当我尝试将图元文件/图形对象捆绑到一个带有方法的类时,我无法获得MetafileHandle
,因为GetHenhmetafile()
会吐出parameter is not valid
异常。
根据this source,该异常是一个明确的指示,表明该方法至少曾被调用过一次。但看看我的代码。我肯定看不到我两次调用它的位置。也许你可以吗?
无论如何,我强烈怀疑我要么不完全理解这些对象的使用方式(MemoryStreams,Metafiles或P / Invoked函数),或者我遗漏了一些基本的东西。 C#班的工作方式,我希望有人可以帮我推动正确的方向。
[编辑后添加回成功代码,并根据建议仅保留代码中的上下文位
以下是有效的代码:
class EmfGenerator
{
static void Main()
{
const int width = 450;
const int height = 325;
Metafile m;
using (var stream = new MemoryStream())
{
Graphics offScreenBufferGraphics; //this is a throw-away object needed for the deviceContext
using (offScreenBufferGraphics = Graphics.FromHwndInternal(IntPtr.Zero))
{
IntPtr deviceContextHandle = offScreenBufferGraphics.GetHdc();
m = new Metafile(
stream,
deviceContextHandle,
new RectangleF(0, 0, width, height),
MetafileFrameUnit.Pixel, //scaling only works properly with integers due to decimal truncation, so use milimeters or pixels here
EmfType.EmfPlusOnly); //force GDI+ mode
offScreenBufferGraphics.ReleaseHdc(); //once we have our metafile, we no longer need the context handle
}
}
using (Graphics g = Graphics.FromImage(m))
{
//draw a picture
g.Clear(Color.White);
//etc...
}
// Save it as a metafile
IntPtr iptrMetafileHandle = m.GetHenhmetafile();
CopyEnhMetaFile(iptrMetafileHandle, @"emf_binary_sample.emf"); //this gives us just the metafile
DeleteEnhMetaFile(iptrMetafileHandle);
}
}
这是不起作用的代码。一个注意事项:我最初使用上面的“使用”结构编写它,并且具有相同的错误。所以,我没有重建它,因为使用包装器过早地破坏了某些东西。无论如何,我都得到了同样的错误。
class MetafileExperiment
{
protected Graphics G; //the working graphics object
protected Metafile M; //the working metafile
protected IntPtr MetafileHandle;
public MetafileExperiment(int startingWidth, int startingHeight)
{
var stream = new MemoryStream();
var bfr = Graphics.FromHwndInternal(IntPtr.Zero);
IntPtr dev = bfr.GetHdc();
M = new Metafile(
stream,
dev,
new RectangleF(0, 0, startingWidth, startingHeight),
MetafileFrameUnit.Pixel, //scaling only works properly with integers due to decimal truncation, so use milimeters or pixels here
EmfType.EmfPlusOnly); //force GDI+ mode
//the handle is needed in order to use the P/Invoke to save out and delete the metafile in memory.
MetafileHandle = M.GetHenhmetafile(); // Parameter is not valid
bfr.ReleaseHdc();
G = Graphics.FromImage(M);
}
}
如您所见,我在创建元文件后直接将GetHenhmetafile()
放在构造函数中。我在一些笔记上做了这个,我发现每个实例只能调用一次这个方法(例如See here)。对于冒险者,可以找到整个回购here。
关闭它的机会很有帮助,这里是破解代码中的异常细节(内部异常为空):
System.ArgumentException was unhandled
_HResult=-2147024809
_message=Parameter is not valid.
HResult=-2147024809
IsTransient=false
Message=Parameter is not valid.
Source=System.Drawing
StackTrace:
at System.Drawing.Imaging.Metafile.GetHenhmetafile()
at SimpleEmfGenerator.MetafileExperiment..ctor(Int32 startingWidth, Int32 startingHeight) in c:\Users\ggauthier\Repositories\Articulate\SimpleEmfGenerator\SimpleEmfGenerator\MetafileExperiment.cs:line 40
at SimpleEmfGenerator.EmfGenerator.Main() in c:\Users\ggauthier\Repositories\Articulate\SimpleEmfGenerator\SimpleEmfGenerator\EmfGenerator.cs:line 108
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
答案 0 :(得分:4)
这是一个初始化问题。核心问题是您从空流创建元文件,并且GDI +似乎会延迟实际创建本机元文件,直到它有充分的理由。有了怪癖(又名bug)GetHenhmetafile()不是一个足够好的理由。
解决方法是强制它这样做,在调用之前放置这行代码:
using (Graphics.FromImage(M)) {}
请注意,在获取本机句柄之后,从元文件创建Graphics对象是不可能的,您将看到代码失败并出现相同的异常。目前尚不清楚为什么要这样做。
答案 1 :(得分:1)
我不确定这是否仅仅因为它已经在.NET 4.0中得到修复,但我可以保存一个图元文件,而不需要使用丑陋的DllImport。
很抱歉这是VB.NET而不是C#,但这就是我使用的......
Public Shared Sub Test()
Dim ms As New IO.MemoryStream()
Dim img As New Bitmap(1000, 1000)
Dim imgGraphics = Graphics.FromImage(img)
Dim hdc = imgGraphics.GetHdc()
Dim mf = New Metafile(ms, hdc, EmfType.EmfPlusOnly)
imgGraphics.ReleaseHdc()
' Important - The above is just a test. In production code, you should eventually dispose of stuff
Using g = Graphics.FromImage(mf)
g.DrawRectangle(Pens.Black, 20, 30, 15, 16)
g.DrawRectangle(Pens.Black, 140, 130, 15, 16)
End Using
' Note: it's important that the Graphics used to draw into the MetaFile is disposed
' or GDI+ won't flush.
' Easy Way
Dim buffer1 = ms.GetBuffer() ' This produces a 444 byte buffer given the example above
' The Hard way
Dim enhMetafileHandle = mf.GetHenhmetafile
Dim bufferSize = GetEnhMetaFileBits(enhMetafileHandle, 0, Nothing)
Dim buffer(bufferSize - 1) As Byte
Dim ret = GetEnhMetaFileBits(enhMetafileHandle, bufferSize, buffer)
End Sub