過去 2 週間、これに対する答えを無駄に探しましたが、困惑しています。
私はメタファイルから構築された Graphics オブジェクトからサンプル画像を作成するいくつかのコードを使用しており、Windows.Forms (コンソール アプリ) の必要性を避けるためにすべてメモリ ストリームに常駐しています。関数CopyEnhMetaFile
(からインポートgdi32.dll
) を使用して、メタファイルを実際の EMF としてディスクに保存します。ここ、ここ、ここ、およびここを参照して、これをどのようにまとめたかについての基本的なメモを確認してください。
単純な main() スクリプト ( codeprojectの例に見られるように) としてトップダウンで記述すると、問題なく動作します。しかし、メソッドを使用してメタファイル/グラフィックス オブジェクトをクラスにバンドルしようとすると、例外が返されるMetafileHandle
ため、 を取得できません。GetHenhmetafile()
parameter is not valid
このソースによると、その例外は、メソッドが以前に少なくとも 1 回呼び出されたことを明確に示しています。しかし、私のコードを見てください。2回呼び出した場所は確かにわかりません。たぶんあなたはできる?
いずれにせよ、これらのオブジェクトを使用する方法 (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);
}
}
そして、これが機能しないコードです。1 つの注記: 私は元々、上記の "using" 構造を使用して記述しましたが、同じエラーが発生しました。そのため、使用しているラッパーが何かを破壊するのが早すぎる可能性があるため、それを使用せずに再構築しました。どちらの方法でも同じエラーが発生しました。
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()
、メタファイル自体を作成した直後に、コンストラクターに を入れています。このメソッドは、インスタンスごとに 1 回しか呼び出すことができないというメモでこれを行いました (たとえば、こちらを参照してください)。冒険好きの方は、リポジトリ全体をここで見つけることができます。
参考になれば、壊れたコードの例外の詳細を次に示します (内部例外は null です)。
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: