2011-02-02 20 views
7

Sử dụng David Brown's downloadable sample at ImplicitOperator Tôi đã đặt cùng nhau một thường là làm việc GraphViz kết xuất đồ họa của tệp DOT thành hình ảnh trong bộ nhớ.GraphViz C# interop dẫn đến AccessViolationException thỉnh thoảng

Thật không may, phiên bản của tôi không thành công với tỷ lệ 1 trong 8 hành vi được áp dụng từ ứng dụng web IIS 7 ASP.NET mà tôi đã có. Tôi biết rằng dữ liệu tệp DOT phù hợp vì tôi đã so sánh các trường hợp không thành công đối với các cá thể làm việc và chúng giống hệt nhau.

Vì trang web của David dường như gợi ý rằng tương lai của blog không chắc chắn, tôi sẽ in lại các phần tương tác tại đây. Hy vọng anh ta không phiền. Sự thất bại là vào cuối của mẫu, trong RenderImage tại tập lệnh thứ ba. Tôi đã lưu ý dòng thất bại với // TODO: .... Sự thất bại luôn luôn xảy ra ở đó (nếu nó xảy ra ở tất cả). Bởi dòng này, g và con trỏ gvc không khác và chuỗi bố cục được điền chính xác.

Tôi thực sự không mong đợi bất kỳ ai gỡ lỗi cài đặt này khi chạy. Thay vào đó, tôi hy vọng rằng một số phân tích tĩnh của mã interop có thể tiết lộ vấn đề. Tôi không thể nghĩ ra bất kỳ kỹ thuật đầm lầy tiên tiến nào có sẵn ở đây - hai IntPtr và một chuỗi không cần nhiều sự trợ giúp, đúng không?

Cảm ơn!

Lưu ý phụ: Tôi đã xem xét bản dùng thử MSAGL và tôi không ấn tượng - với giá 99 đô la từ Microsoft, tôi mong đợi nhiều tính năng hơn cho bố cục nút và/hoặc tài liệu giải thích những gì tôi thiếu. Có lẽ cổng nhanh của tôi từ QuickGraph đến AGL không công bằng với kinh nghiệm của tôi vì một số khác biệt cơ bản trong các cách tiếp cận (ví dụ như trung tâm so với nút trung tâm).

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

Tôi biết điều này nghe có vẻ ngu ngốc. Nhưng có cần phải chạy dll trực tiếp trong mã của bạn? Có thể tạo ngôn ngữ dấu chấm trong một tệp văn bản và sau đó chạy thực thi dấu chấm độc lập với một lệnh như: "dot -Tpng -o MyFile.png MyFile.dot" bằng cách sử dụng lớp C# Process? Một khi bạn có hình ảnh bạn chỉ có thể tải nó trực tiếp vào bộ nhớ chương trình của bạn. – jluzwick

+0

Tôi đoán đó là một tùy chọn, nhưng việc xử lý các tệp trên đĩa cho thao tác này sẽ chậm và sẽ giới thiệu thêm các điểm thất bại trong chính các cơ chế I/O. Kể từ khi dll được sử dụng một trong hai cách, tôi muốn hiểu làm thế nào để làm cho nó hoạt động đúng trong bộ nhớ. –

+0

Bạn có thể cho biết cách sử dụng lớp học của mình không? –

Trả lời

4

Visual Studio 2010 đã thêm phát hiện "PInvokeStackImbalance" mà tôi nghĩ đã giúp tôi khắc phục sự cố. Mặc dù hình ảnh vẫn sẽ được tạo, tôi sẽ gặp lỗi này nhiều lần.

Bằng cách chỉ định CallingConvention = CallingConvention.Cdecl trên tất cả các sigantures LIBGVC PInvoke, lỗi và sự cố biến mất.

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

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

... 

Tôi không gặp sự cố nào khi thực hiện thay đổi này, vì vậy tôi sẽ đánh dấu đây là câu trả lời mới.

4

Tôi nhớ chạy vào các vấn đề như thời gian này, tôi đã làm việc trên bài viết và gửi câu hỏi về họ herehere (thứ hai trong số đó bạn dường như đã nhận xét về; lời xin lỗi của tôi vì đã không nhìn thấy những nhận xét trước đó) .

Câu hỏi đầu tiên có lẽ không liên quan trực tiếp đến điều này vì tôi đã viết một ứng dụng thử nghiệm trong C, chứ không phải C#, và gvLayout không hoạt động mỗi lần thay vì chỉ mỗi lần. Bất kể, hãy chắc chắn rằng ứng dụng của bạn không có quyền truy cập vào tệp cấu hình Graphviz (sao chép nó cùng với tệp thực thi của bạn hoặc đặt thư mục thùng rác Graphviz trong hệ thống PATH của bạn).

Câu hỏi thứ hai có liên quan hơn, ngoại trừ câu hỏi thứ hai áp dụng cho agmemread và không phải gvLayout. Tuy nhiên, rất có thể cả hai đều do cùng một vấn đề. Tôi đã không bao giờ có thể tìm ra một giải pháp, vì vậy tôi đã gửi nhóm Graphviz một bug report. Thật không may, nó đã không được giải quyết.

API Graphviz rất đơn giản, do đó không chắc vấn đề là do mã interop gây ra. Có một điều mà tôi bỏ qua đề cập đến trong bài viết: con trỏ result cần được giải phóng. Tôi không biết nếu điều này sẽ khắc phục vấn đề của bạn, nhưng nó vẫn là một ý tưởng tốt để thêm nó anyway:

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

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

Theo như tôi biết, vấn đề này liên quan đến cách Graphivz hồi phục từ lỗi nội bộ, vì vậy cho đến khi lỗi được giải quyết, tôi không chắc chắn có bất cứ điều gì bạn hoặc tôi có thể làm. Nhưng, tôi không phải là một chuyên gia interop, vì vậy hy vọng ai đó khác có thể giúp bạn nhiều hơn một chút.

+0

Xin chào David. Cảm ơn bạn đã phản hồi và thay đổi mã. Tôi đã cố gắng áp dụng nó và nó bây giờ bị treo luôn luôn miễn phí (kết quả); Bất kỳ ý tưởng tại sao? Có lẽ mã khác của tôi không đồng bộ với bạn? –

+0

Tôi chỉ chạy mẫu với mã 'miễn phí' một lần nữa và nó cũng bị hỏng cho tôi. Không chắc chắn tại sao nó không làm điều đó một năm trước đây, mặc dù. Đây là một báo cáo lỗi về nó (không được giải quyết, thật không may): http://www.graphviz.org/bugs/b1775.html –

+0

Rất tiếc. GraphViz có công cụ bố trí nút tốt nhất và các tùy chọn tổng thể, nhưng có hai lỗi chưa được giải quyết mà chúng tôi đã thảo luận. Và tôi tìm thấy cả hai trong những gì tôi sẽ xem xét một ứng dụng khá cơ bản, vì vậy họ đang gần bề mặt. Ngoài ra, không có phân phối x64. Tôi cảm thấy như tôi biết làm thế nào để viết lại tất cả mọi thứ cách lý tưởng của tôi trong C# ngoại trừ các thuật toán định tuyến cạnh. –

0

thay đổi quy ước gọi điện KHÔNG ĐƯỢC trợ giúp !!

Có, nó hoạt động khi một nguồn đơn giản (thực sự không đơn giản), nhưng nó luôn luôn sụp đổ nếu nguồn dấu chấm chứa ký tự unicode (tiếng Trung giản thể).

+1

Có lẽ đó là một vấn đề khác mà CSONG đổ vỡ? Tôi không biết rằng Graphviz hỗ trợ unicode, phải không? [Đây là liên kết đề cập đến việc sử dụng mã hóa html của các ký tự unicode cho một số nội dung.] (Http://www.graphviz.org/doc/info/lang.html) –

+0

Có, tôi có thể xác nhận rằng Graphviz hỗ trợ unicode. Tôi có thể tạo biểu đồ chính xác bằng tệp dot viết tay. – Alsan