在过去的两周里,我一直在徒劳地寻找这个问题的答案,但我很困惑。
我正在使用一些代码,这些代码从由元文件构造的 Graphics 对象创建示例图像,所有这些都驻留在内存流中,以避免需要 Windows.Forms(它是一个控制台应用程序),我正在使用函数CopyEnhMetaFile
(从 导入gdi32.dll
),将元文件作为真正的 EMF 保存到磁盘。你可以看这里、这里、这里和这里,了解我如何将这些放在一起的一些基本说明。
当我将它自上而下地编写为简单的 main() 脚本(如codeproject示例中所示)时,它工作正常。但是,当我尝试将元文件/图形对象与方法捆绑到一个类中时,我无法获得MetafileHandle
, 因为GetHenhmetafile()
返回parameter is not valid
异常。
根据这个来源,该异常清楚地表明该方法之前至少被调用过一次。但是看看我的代码。我肯定看不到我在哪里调用了它两次。也许你可以?
无论如何,我强烈怀疑我要么没有完全理解这些对象的使用方式(MemoryStreams、元文件或 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()
在创建元文件本身之后直接将 放入构造函数中。我在一些笔记上这样做了,我发现这些笔记说每个实例只能调用一次此方法(例如,请参见此处)。对于喜欢冒险的人,可以在这里找到整个 repo 。
如果有帮助,这里是损坏代码中的异常详细信息(内部异常为空):
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: