Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1767
  • Last Modified:

Calling librsvg functions from C#

So i'm trying to make a C# wrapper around librsvg for a web app where we need to rasterize svgs on the server. Below is some simple test code I'm trying to get to working, but there's a few problems:
1.) After the call to rsvg_pixbuf_from_file_at_size, both the IntPtrs are still 0. One of them should be getting set to something, whether that is a GdkPixbuf* or a GError*, right?

2.) The call to gdk_pixbuf_save causes an AccessViolationException ("Attempted to read or write protected memory. This is often an indication that other memory is corrupt."). I would expect the function call to fail given that i'm not passing it a legit handle, but I'm still not sure why i'm getting that exception. The weird thing is that if I allocate some memory to that error IntPtr with Marshal.AllocCoTaskMem(), it stops this exception from being thrown, but the prototype of the function makes it look like it would be writing to that error pointer directly.

Any ideas?

Here's the prototype for rsvg_pixbuf_from_file_at_size():
GdkPixbuf*  rsvg_pixbuf_from_file_at_size   (const gchar *file_name,
                                             gint width,
                                             gint height,
                                             GError **error);

And here's the prototype for gdk_pixbuf_save():
gboolean            gdk_pixbuf_save                     (GdkPixbuf *pixbuf,
                                                         const char *filename,
                                                         const char *type,
                                                         GError **error,
                                                         ...);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool SetDllDirectory(string pathname);
 
        [DllImport("libgobject-2.0-0.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern void g_type_init(); 
 
        [DllImport("librsvg-2-2.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern IntPtr rsvg_pixbuf_from_file_at_size(string file_name, int width, int height, out IntPtr error);
 
        [DllImport("librsvg-2-2.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern IntPtr rsvg_handle_new();
 
        [DllImport("librsvg-2-2.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool rsvg_handle_write(IntPtr handle, byte[] buf, int count, out IntPtr error);
 
        [DllImport("libgdk_pixbuf-2.0-0.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool gdk_pixbuf_save(IntPtr pixbuf, string filename, string type, out IntPtr error);  
 
 
        public static void RasterizeSvg(string fileName)
        {
            bool callSuccessful = SetDllDirectory("C:\\Program Files\\GIMP-2.0\\bin");
            if (!callSuccessful)
            {
                throw new Exception("Could not set DLL directory");
            }
            g_type_init();
            IntPtr error;
            IntPtr result = rsvg_pixbuf_from_file_at_size(fileName, 100, 100, out error);
            if (error != IntPtr.Zero)
            {
                throw new Exception(Marshal.ReadInt32(error).ToString());
            }
            callSuccessful = gdk_pixbuf_save(result,@"C:\test.png", "png", out error);
            if (!callSuccessful)
            {
                throw new Exception(Marshal.ReadInt32(error).ToString());
            }
        }

Open in new window

0
cacliffo
Asked:
cacliffo
  • 2
1 Solution
 
williamcampbellCommented:
1) IntPtr may not be the correct type what is the true definition of this function?
2) This error can be misleading it means an uncaught exception is being thrown ... turn on Exceptions  under "Debug /Exceptions" in the main menu and see what the exception is  ...
0
 
cacliffoAuthor Commented:
1.) The prototype is in my first post, the actual function just passes the error argument along to g_set_error() (a glib function) if there are any errors. Here's the documentation for that function:

void                g_set_error                         (GError **err,
                                                         GQuark domain,
                                                         gint code,
                                                         const gchar *format,
                                                         ...);

Does nothing if err is NULL; if err is non-NULL, then *err must be NULL. A new GError is created and assigned to *err.

So since i'm passing an IntPtr by reference, err should be non null and that IntPtr should be getting assigned to the address of a GError object (not that I can really do anything with that in C#).

2.) It was already set to break on all exceptions. I'm still getting the same one (AccessViolationException).
0
 
AgariciCommented:
most probably your pInvoke definitions and the way you pass parameters between C# and unmanaged have some bugs

please have a look at this site:http://pinvoke.net/
they have a custom visual studio add-in

also this one may be helpfull: http://www.codeplex.com/clrinterop/Release/ProjectReleases.aspx?ReleaseId=14120

hth,
A.
0
 
cacliffoAuthor Commented:
Fixed. Basically I had to remove the CharSet attribute from the DllImport statement for rsvg_pixbuf_from_file_at_size (don't know why, though), and use C#'s undocumented __arglist keyword: http://community.bartdesmet.net/blogs/bart/archive/2006/09/28/4473.aspx

Working code attached.
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool SetDllDirectory(string pathname);
 
        [DllImport("libgobject-2.0-0.dll", SetLastError = true)]
        static extern void g_type_init(); 
 
        [DllImport("librsvg-2-2.dll", SetLastError = true)]
        static extern IntPtr rsvg_pixbuf_from_file_at_size(string file_name, int width, int height, out IntPtr error);
 
        [DllImport("libgdk_pixbuf-2.0-0.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        static extern bool gdk_pixbuf_save(IntPtr pixbuf, string filename, string type, out IntPtr error, __arglist);
 
 
 
        public static void RasterizeSvg(string fileName)
        {
            bool callSuccessful = SetDllDirectory("C:\\Program Files\\GIMP-2.0\\bin");
            if (!callSuccessful)
            {
                throw new Exception("Could not set DLL directory");
            }
            g_type_init();
            IntPtr error;
            IntPtr result = rsvg_pixbuf_from_file_at_size(fileName, -1, -1, out error);
            if (error != IntPtr.Zero)
            {
                throw new Exception(Marshal.ReadInt32(error).ToString());
            }
            callSuccessful = gdk_pixbuf_save(result,@"C:\test.png", "png", out error, __arglist(null));
            if (!callSuccessful)
            {
                throw new Exception(error.ToInt32().ToString());
            }
        }

Open in new window

0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now