GraphViz C#互操作偶尔会导致AccessViolationException

GraphViz C#互操作偶尔会导致AccessViolationException,c#,graphviz,access-violation,C#,Graphviz,Access Violation,使用我已经把一个经常工作的点文件GraphViz渲染器整合到内存中的图像中 不幸的是,我的版本在IIS 7 ASP.NET web应用程序中以8分之一的执行率失败。我知道点文件数据是一致的,因为我将失败实例与工作实例进行了比较,它们是相同的 由于David的网站似乎暗示博客的未来是不确定的,我将在这里重印interop文章。希望他不介意。失败发生在示例的末尾,在第三个语句集的RenderImage中。我注意到//TODO:….的失败行。。。。失败总是发生在那里(如果它真的发生的话)。通过这一行,

使用我已经把一个经常工作的点文件GraphViz渲染器整合到内存中的图像中

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

由于David的网站似乎暗示博客的未来是不确定的,我将在这里重印interop文章。希望他不介意。失败发生在示例的末尾,在第三个语句集的RenderImage中。我注意到//TODO:….的失败行。。。。失败总是发生在那里(如果它真的发生的话)。通过这一行,g和gvc指针是非零的,布局字符串被正确填充

我真的不希望有人在运行时调试它。相反,我希望对互操作代码进行一些静态分析可以揭示问题所在。我想不出这里有什么高级封送技术可用——两个intptr和一个字符串不需要太多帮助,对吗

谢谢

旁注:我已经看过MSAGL的试用版,但我没有留下深刻印象——微软提供99美元,我希望在节点布局和/或文档方面有更多功能来解释我所缺少的东西。也许我从QuickGraph到AGL的快速端口不公平地影响了我的体验,因为在方法上存在一些根本性的差异(例如,以边缘为中心与以节点为中心)

公共静态类Graphviz
{
public const string LIB_GVC=“GVC.dll”;
public const string LIB_GRAPH=“GRAPH.dll”;
公共const int SUCCESS=0;
///  
///创建新的Graphviz上下文。
///  
[DllImport(LIB_GVC)]
公共静态外部IntPtr gvContext();
///  
///释放上下文的资源。
///  
[DllImport(LIB_GVC)]
公共静态外部int gvFreeContext(IntPtr gvc);
///  
///从字符串读取图形。
///  
[DllImport(LIB_图)]
公共静态外部IntPtr agmemread(字符串数据);
///  
///释放图形使用的资源。
///  
[DllImport(LIB_图)]
公共静态外部无效(IntPtr g);
///  
///使用给定引擎将布局应用于图形。
///  
[DllImport(LIB_GVC)]
公共静态外部int gvLayout(IntPtr gvc、IntPtr g、字符串引擎);
///  
///释放布局使用的资源。
///  
[DllImport(LIB_GVC)]
公共静态外部int gvFreeLayout(IntPtr gvc、IntPtr g);
///  
///将图形渲染为文件。
///  
[DllImport(LIB_GVC)]
公共静态外部int gvRenderFilename(IntPtr gvc、IntPtr g、,
字符串格式,字符串文件名);
///  
///在内存中呈现图形。
///  
[DllImport(LIB_GVC)]
公共静态外部int gvRenderData(IntPtr gvc、IntPtr g、,
字符串格式,输出IntPtr结果,输出int长度);
公共静态图像渲染(字符串源、字符串布局、字符串格式)
{
//创建Graphviz上下文
IntPtr gvc=gvContext();
如果(gvc==IntPtr.Zero)
抛出新异常(“未能创建Graphviz上下文”);
//将点数据加载到图形中
IntPtr g=agmemread(源);
如果(g==IntPtr.Zero)
抛出新异常(“未能从源创建图形。请检查语法错误”);
//应用布局
if(gvLayout(gvc,g,layout)!=SUCCESS)//TODO:修复此处的AccessViolationException
抛出新异常(“布局失败”);
IntPtr结果;
整数长度;
//渲染图形
if(gvRenderData(gvc、g、格式、输出结果、输出长度)!=成功)
抛出新异常(“渲染失败”);
//创建一个数组以保存渲染的图形
字节[]字节=新字节[长度];
//从IntPtr复制图像
Marshal.Copy(结果,字节,0,长度);
//释放资源
全球价值链(gvc,g);
agclose(g);
全球价值链;
使用(内存流=新内存流(字节))
{
返回Image.FromStream(stream);
}
}
}

我记得我在写这篇文章时遇到了这样的问题,并发布了关于这些问题的问题(第二个问题你似乎已经发表了评论;我为没有看到之前的评论而道歉)

第一个问题可能与此没有直接关系,因为我在用C编写测试应用程序,而不是用C#,而且
gvLayout
每次都失败,而不是偶尔失败。无论如何,请确保您的应用程序确实可以访问Graphviz配置文件(将其与可执行文件一起复制,或将Graphviz bin目录放在系统路径中)

第二个问题更相关,除了它适用于
agmemread
而不是
gvLayout
。然而,很可能两者都是由同一问题引起的。我始终无法找到解决方案,因此我派出了Graphviz团队。不幸的是,这个问题还没有解决

Graphviz API非常简单,因此问题不太可能是由互操作代码引起的。我在文章中忽略了一件事:需要释放
结果
指针。我不知道这是否能解决您的问题,但添加它仍然是一个好主意:

[DllImport("msvcrt.dll", SetLastError = true)]
private static extern void free(IntPtr pointer);

// After Marshal.Copy in RenderImage
free(result);

据我所知,这个问题与Graphivz如何从内部错误中恢复有关,因此,在解决错误之前,我不确定您或我能做些什么。但是,我不是互操作专家,所以希望其他人能为您提供更多帮助。

VisualStudio2010添加了一个“PinvokeStackBalancement”检测,我认为它帮助我解决了这个问题。虽然图像仍会生成,但我会多次出现此错误

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

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

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

...
自从进行此更改以来,我没有发生过崩溃,所以现在我将此标记为新的答案。
[DllImport(LIB_GVC, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr gvContext();

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

...