GraphViz C# 互操作偶尔会导致 AccessViolationException [英] GraphViz C# interop resulting in AccessViolationException occasionally

查看:19
本文介绍了GraphViz C# 互操作偶尔会导致 AccessViolationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 David Brown 在 ImplicitOperator 上的可下载示例 我已经将 DOT 文件的 经常 工作 GraphViz 渲染器组合到内存中图像.

Using David Brown's downloadable sample at ImplicitOperator I've put together an often working GraphViz renderer of a DOT file to an in-memory image.

不幸的是,我的版本在 IIS 7 ASP.NET Web 应用程序中以八分之一的执行失败率失败.我知道 DOT 文件数据是一致的,因为我比较了失败的实例对工作实例,它们是相同的.

Unfortunately, my version fails at a guestimated rate of 1 in 8 executions from with the IIS 7 ASP.NET web application I've got it in. I know that the DOT file data is consistent because I've compared the failing instances against the working instances and they are identical.

由于 David 的网站似乎暗示该博客的未来不确定,因此我将在此处重印互操作文章.希望他不会介意.失败发生在示例的末尾,在第三个语句集的 RenderImage 中.我用//TODO: .... 失败总是发生在那里(如果它发生了).通过这一行,g 和 gvc 指针不为零,并且布局字符串已正确填充.

As David's site seems to suggest that the blog's future is uncertain, I'll reprint the interop pieces here. Hope he doesn't mind. The failure is toward the end of the sample, within RenderImage at the third statement set. I've noted the failing line with // TODO: .... The failure always happens there (if it happens at all). By this line, g and gvc pointers are non-zero and the layout string is correctly populated.

我真的不希望有人在运行时调试它.相反,我希望对互操作代码的一些静态分析可能会揭示问题.我想不出这里可用的任何高级封送处理技术 - 两个 IntPtrs 和一个字符串应该不需要太多帮助,对吧?

I don't really expect anyone to debug this at runtime. Rather, I hope that some static analysis of the interop code might reveal the problem. I can't think of any advanced marshaling techniques available here - two IntPtrs and a string shouldn't need a lot of help, right?

谢谢!

旁注:我看过 MSAGL 的试用版,并没有给我留下深刻印象 - 微软 99 美元,我希望节点布局和/或文档有更多功能来解释我所缺少的东西.也许我从 QuickGraph 到 AGL 的快速移植由于方法的一些根本差异(例如,以边缘为中心与以节点为中心)不公平地影响了我的体验.

Side note: I've looked at a trial of MSAGL and I'm not impressed - for $99 from Microsoft, I'd expect more features for node layout and/or documentation explaining what I'm missing. Maybe my rapid port from QuickGraph to AGL unfairly biases my experience because of some fundamental differences in the approaches (edge-centric vs node-centric, for example).

public static class Graphviz
{
  public const string LIB_GVC = "gvc.dll";
  public const string LIB_GRAPH = "graph.dll";
  public const int SUCCESS = 0;

  /// <summary> 
  /// Creates a new Graphviz context. 
  /// </summary> 
  [DllImport(LIB_GVC)]
  public static extern IntPtr gvContext();

  /// <summary> 
  /// Releases a context's resources. 
  /// </summary> 
  [DllImport(LIB_GVC)]
  public static extern int gvFreeContext(IntPtr gvc);

  /// <summary> 
  /// Reads a graph from a string. 
  /// </summary> 
  [DllImport(LIB_GRAPH)]
  public static extern IntPtr agmemread(string data);

  /// <summary> 
  /// Releases the resources used by a graph. 
  /// </summary> 
  [DllImport(LIB_GRAPH)]
  public static extern void agclose(IntPtr g);

  /// <summary> 
  /// Applies a layout to a graph using the given engine. 
  /// </summary> 
  [DllImport(LIB_GVC)]
  public static extern int gvLayout(IntPtr gvc, IntPtr g, string engine);

  /// <summary> 
  /// Releases the resources used by a layout. 
  /// </summary> 
  [DllImport(LIB_GVC)]
  public static extern int gvFreeLayout(IntPtr gvc, IntPtr g);

  /// <summary> 
  /// Renders a graph to a file. 
  /// </summary> 
  [DllImport(LIB_GVC)]
  public static extern int gvRenderFilename(IntPtr gvc, IntPtr g,
    string format, string fileName);

  /// <summary> 
  /// Renders a graph in memory. 
  /// </summary> 
  [DllImport(LIB_GVC)]
  public static extern int gvRenderData(IntPtr gvc, IntPtr g,
    string format, out IntPtr result, out int length);

  public static Image RenderImage(string source, string layout, string format)
  {
    // Create a Graphviz context 
    IntPtr gvc = gvContext();
    if (gvc == IntPtr.Zero)
      throw new Exception("Failed to create Graphviz context.");

    // Load the DOT data into a graph 
    IntPtr g = agmemread(source);
    if (g == IntPtr.Zero)
      throw new Exception("Failed to create graph from source. Check for syntax errors.");

    // Apply a layout 
    if (gvLayout(gvc, g, layout) != SUCCESS) // TODO: Fix AccessViolationException here
      throw new Exception("Layout failed.");

    IntPtr result;
    int length;

    // Render the graph 
    if (gvRenderData(gvc, g, format, out result, out length) != SUCCESS)
      throw new Exception("Render failed.");

    // Create an array to hold the rendered graph
    byte[] bytes = new byte[length];

    // Copy the image from the IntPtr 
    Marshal.Copy(result, bytes, 0, length);

    // Free up the resources 
    gvFreeLayout(gvc, g);
    agclose(g);
    gvFreeContext(gvc);

    using (MemoryStream stream = new MemoryStream(bytes))
    {
      return Image.FromStream(stream);
    }
  }
}

推荐答案

Visual Studio 2010 添加了PInvokeStackImbalance"检测,我认为这有助于我解决问题.虽然仍然会生成图像,但我会多次收到此错误.

Visual Studio 2010 added a "PInvokeStackImbalance" detection that I think helped me fix the problem. While the image would still get generated, I would get this error several times.

通过在所有 LIBGVC PInvoke 签名上指定 CallingConvention = CallingConvention.Cdecl,错误和崩溃消失了.

By specifying CallingConvention = CallingConvention.Cdecl on all the LIBGVC PInvoke sigantures, the error and crashes disappear.

[DllImport(LIB_GVC, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr gvContext();

[DllImport(LIB_GVC, CallingConvention = CallingConvention.Cdecl)]
public static extern int gvFreeContext(IntPtr gvc);

...

自从进行此更改后,我没有发生任何崩溃,因此我现在将其标记为新答案.

I've had no crashes since making this change, so I'll mark this as the new answer, for now.

这篇关于GraphViz C# 互操作偶尔会导致 AccessViolationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆