Utilizzo di David Brown's downloadable sample at ImplicitOperator Ho creato un spesso Renderer GraphViz funzionante di un file DOT in un'immagine in memoria.GraphViz C# interop risultante in AccessViolationException occasionalmente
Sfortunatamente, la mia versione non riesce ad una velocità guestimated di 1 su 8 esecuzioni con l'applicazione Web IIS 7 ASP.NET in cui sono presente. So che i dati del file DOT sono coerenti perché ho confrontato il fallendo istanze contro le istanze di lavoro e sono identici.
Poiché il sito di David sembra suggerire che il futuro del blog è incerto, ristamperò qui i pezzi di interopzione. Spero che non gli dispiaccia. L'errore è verso la fine dell'esempio, all'interno di RenderImage alla terza serie di istruzioni. Ho notato la linea in errore con // TODO: .... L'errore si verifica sempre lì (se succede a tutti). Con questa linea, i puntatori g e gvc sono diversi da zero e la stringa di layout è correttamente compilata.
Non mi aspetto davvero che qualcuno esegua il debug di questo in fase di esecuzione. Piuttosto, spero che qualche analisi statica del codice di interoperabilità possa rivelare il problema. Non riesco a pensare a tecniche di marshalling avanzate disponibili qui: due IntPtr e una stringa non dovrebbero richiedere molto aiuto, giusto?
Grazie!
Nota a margine: ho esaminato una versione di prova di MSAGL e non mi entusiasma: per $ 99 di Microsoft, mi aspetto più funzionalità per il layout e/o la documentazione del nodo che spiegano cosa mi manca. Forse il mio rapido passaggio da QuickGraph a AGL distorce ingiustamente la mia esperienza a causa di alcune differenze fondamentali negli approcci (edge-centric vs node-centric, ad esempio).
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);
}
}
}
So che questo potrebbe sembrare stupido. Ma c'è bisogno di eseguire la DLL direttamente nel tuo codice? Sarebbe possibile generare la lingua dei punti in un file di testo e quindi eseguire l'eseguibile con punti standalone con un comando come: "dot -Tpng -o MyFile.png MyFile.dot" usando la classe C# Process? Una volta che hai l'immagine, puoi caricarla direttamente nella memoria del tuo programma. – jluzwick
Immagino che sia un'opzione, ma trattare i file su disco per questa operazione sarebbe lento e introdurrebbe ulteriori punti di errore nei meccanismi di I/O stessi. Dal momento che la dll è impiegata in entrambi i casi, preferirei capire come farlo funzionare correttamente nella memoria. –
Puoi mostrare l'uso della tua classe? –