Question

I'm working on some code that involves using P/Invoke to call unmanaged functions from a few C++ DLLs. I'd like to be able to build the application as either 32 or 64 bit.

Currently, it only works as x86.

I have 32 and 64 bit copies of each of the referenced C++ DLLs and am using the following code to change the DllDirectory depending on whether the app is built as x86 or x64 (/lib/x64 holds the 64-bit dlls, /lib/x86 holds the 32-bit ones):

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern bool SetDllDirectory(string lpPathName);

string libPath = Path.Combine(Environment.CurrentDirectory, "lib", (Environment.Is64BitProcess == true ? "x64" : "x86"));
SetDllDirectory(libPath);  

The rest of my unmanaged C++ functions are defined as follows:

[DllImport("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void g_type_init();
[DllImport("libgobject-2.0-0.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void g_object_unref(IntPtr pixbuf);
[DllImport("librsvg-2-2.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
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);

The code that actually uses these functions looks similar to this:

g_type_init();
IntPtr ptrError;
IntPtr ptrPixbuf = rsvg_pixbuf_from_file_at_size(filePath, width, height, out ptrError);
if (ptrError == IntPtr.Zero)
{
    bool isSaved = gdk_pixbuf_save(ptrPixbuf, outputPath, outputFormat, out ptrError, __arglist(null));  //this line fails when compiled as x64!
    if (isSaved && File.Exists(outputPath))
    {
        return outputPath;
    }
}
g_object_unref(ptrPixbuf);

As I mentioned, everything works fine when running the application as x86 on my local machine (Windows 7 x64). However, when I compile it as an x64 application, I get an "AccessViolationException" at the call to gdk_pixbuf_save().

Any ideas? I'm relatively new to interop code, but I think it might have something to do with how the IntPtr variables are sent to/from the unmanaged code? But why is it different from x86 to x64?

Était-ce utile?

La solution

Many thanks to all who commented -- you sent me down the right path, and helped me solve an underlying problem.

Originally, I wanted to have an x64 build just in case it was necessary...and it ended up being just that.

As it turns out, these comments are correct. On x64 builds, the undocumented ___arglist keyword does not function as intended.

I can't comment on what, specifically, goes wrong. The comment I linked mentions the possibility of the calling convention not being set correctly. I'm not sure how this works...doesn't x64 only have one calling convention, anyway?

Whatever, back to the point:

I changed my DllImport for gdk_pixbuf_save to look like this:

[DllImport("libgdk_pixbuf-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
static extern bool gdk_pixbuf_save(UIntPtr pixbuf, string filename, string type, out UIntPtr error, UIntPtr arglist);

The key here is that I'm passing in the final parameter, arglist, as IntPtr instead of ___arglist.

Actually, I'm passing it in as UIntPtr because I switched all of my original IntPtr objects to UIntPtr.

That being said, when I call the function, it looks like this:

bool isSaved = gdk_pixbuf_save(ptrPixbuf, outputFilePath, outputFileFormat, out ptrError, UIntPtr.Zero);

As my ___arglist is empty (there are other, optional, parameters that could be specified), the documentation tells me it should be null terminated. To accomplish this, I pass in IntPtr.Zero (or UIntPtr.Zero, in my case).

Now, my code compiles, runs and I (more importantly) have access to 64-bits' worth of memory.

Thanks again to those who commented on my post - without your pointers toward the ___arglist parameter, I'd have been completely clueless.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top