Взаимодействие GraphViz C # иногда приводит к AccessViolationException - PullRequest
8 голосов
/ 02 февраля 2011

Использование загружаемого примера Дэвида Брауна из ImplicitOperator Я собрал часто рабочий GraphViz-рендерер файла DOT в образ в памяти.

К сожалениюмоя версия дает сбой с предполагаемой скоростью выполнения 1 из 8 при использовании веб-приложения IIS 7 ASP.NET, в котором она у меня есть. Я знаю, что данные файла DOT согласованы, потому что я сравнил сбойные экземпляры с работающимиэкземпляры и они идентичны.

Поскольку сайт Дэвида, похоже, предполагает, что будущее блога неопределенное, я перепечатал здесь фрагменты взаимодействия.Надеюсь, он не против.Ошибка в конце примера, в RenderImage в третьем наборе операторов.Я отметил ошибочную строку с помощью // TODO: .... Ошибка всегда происходит там (если это вообще происходит).По этой строке указатели g и gvc отличны от нуля, а строка макета заполнена правильно.

Я не ожидаю, что кто-то отладит это во время выполнения.Скорее, я надеюсь, что некоторый статический анализ кода взаимодействия может выявить проблему.Я не могу вспомнить какие-либо продвинутые методы маршалинга, доступные здесь - два IntPtrs и строка не должны нуждаться в большой помощи, верно?

Спасибо!

Примечание: я посмотрелна испытании MSAGL, и я не впечатлен - за 99 долларов от Microsoft я бы ожидал больше возможностей для разметки узлов и / или документации, объясняющей, что мне не хватает.Может быть, мой быстрый переход от QuickGraph к AGL несправедливо искажает мой опыт из-за некоторых принципиальных различий в подходах (например, ориентированных на края по сравнению с ориентированными на узлы).

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);
    }
  }
}

Ответы [ 3 ]

5 голосов
/ 17 апреля 2012

В Visual Studio 2010 добавлено обнаружение «PInvokeStackImbalance», которое, как мне кажется, помогло мне решить проблему.Хотя изображение все еще генерируется, я получаю эту ошибку несколько раз.

При указании CallingConvention = CallingConvention.Cdecl на всех сигнатурах LIBGVC PInvoke ошибки и сбои исчезают.

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

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

...

У меня не было сбоев с момента внесения этого изменения, поэтому я отмечу этокак новый ответ, пока.

4 голосов
/ 02 февраля 2011

Я помню, как сталкивался с такими проблемами, когда работал над статьей и писал о них вопросы здесь и здесь (вторая из которых, по-видимому, вы прокомментировали; мой извиняюсь за то, что не видел комментарий ранее).

Первый вопрос, вероятно, не имеет прямого отношения к этому, потому что я писал тестовое приложение на C, а не на C #, и gvLayout терпел неудачу каждый раз, а не время от времени. В любом случае, убедитесь, что ваше приложение имеет доступ к файлу конфигурации Graphviz (скопируйте его вместе с исполняемым файлом или поместите каталог bin Graphviz в системную переменную PATH).

Второй вопрос более актуален, за исключением того, что он относится к agmemread, а не gvLayout. Однако вполне возможно, что оба они вызваны одной и той же проблемой. Я так и не смог найти решение, поэтому отправил команде Graphviz сообщение об ошибке . К сожалению, это не было решено.

API Graphviz очень прост, поэтому маловероятно, что проблема вызвана кодом взаимодействия. В статье я не упомянул одну вещь: указатель result необходимо освободить. Я не знаю, решит ли это вашу проблему, но все равно стоит добавить ее:

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

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

Насколько я знаю, эта проблема связана с тем, как Graphivz восстанавливается после внутренних ошибок, поэтому, пока ошибка не устранена, я не уверен, что вы или я можем что-либо сделать. Но я не эксперт по взаимодействию, так что, надеюсь, кто-то еще может помочь вам немного больше.

0 голосов
/ 23 июля 2012

изменение соглашения о вызовах НЕ ПОМОГАЕТ !!

Да, это работает, когда простой (на самом деле не такой простой) точечный источник, но ВСЕГДА вылетает, если точечный источник содержит символ юникода (упрощенный китайский).

...