Calling unmanaged DLL functions from C#
December 28, 2007 10:00 PM   Subscribe

How can I work with an unmanaged C++ API from within C#?

I have C++ functions with the following prototypes:
extern "C" __declspec(dllexport) HFSUniStr255 GetFolderList(char *path);
extern "C" __declspec(dllexport) void GetFile(char *path, UInt64* offset, UInt64* length);


The HFSUniStr255 typedef is the following struct:
struct HFSUniStr255 {
    UInt16  length;
    UInt16 unicode[255];
};


Where a UInt16 is an unsigned 16-bit integer. The two functions take a pointer to a char array and the former returns the above struct and the latter writes 64-bit integer values to the locations indicated by the parameter pointers offset and length.

My question is this: how on Earth do I work with these in managed C# code? I'm trying to follow examples like this to no avail. In C# I have defined the following:
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct HFSUniStr255
        {
            [MarshalAs(UnmanagedType.U2)]
            public UInt16 length;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst=255)]
            public UInt16[] unicode;
        }

        [DllImport("llio2.dll")]
        public static extern void GetFile([MarshalAs(UnmanagedType.LPStr)] char[] path, [MarshalAs(UnmanagedType.U8)] UIntPtr offset, [MarshalAs(UnmanagedType.U8)] UIntPtr length);
        [DllImport("llio2.dll")] 
        [return : MarshalAs(UnmanagedType.Struct)]
        public static extern HFSUniStr255 GetFolderList([MarshalAs(UnmanagedType.LPStr)] char[] path);


Despite this, I get errors telling me that I've tried reading or writing protected memory and that the method's signature is not compatible with the PInvoke type. Any gurus in this area?
posted by PuGZ to Computers & Internet (6 answers total)
 
I doubt this is the only problem you'll have, but you probably want to marshal offset and length as ref UInt instead of UIntPtr. The MSDN forums might be a better place to ask.
posted by 0xFCAF at 10:06 PM on December 28, 2007


Yeah, I suppose you are right. I really know absolutely nothing of C# nor managed code and to hope that everything will just work without doing any research is a bit wishful thinking. Ah well.
posted by PuGZ at 10:21 PM on December 28, 2007


C++/.NET interop is one of the few places where C++.NET has any use. MSDN has some good examples of writing C++.NET wrappers for native -> .NET things.. if you haven't looked at those, it'd be a good place to start. Once you get it into .NET, you can go back to using C#.
posted by devilsbrigade at 11:44 PM on December 28, 2007


extern "C" __declspec(dllexport) void GetFile(char *path, UInt64* offset, UInt64* length);

"functions take a pointer to a char array ... the latter writes 64-bit integer values to the locations indicated by the parameter pointers offset and length."


Where are you getting the 64 bit integer values you want to write from? The signature and description you gave were unclear about this. From the signature it looks like you provide the file path, offset into that file and the length of data you are requesting. It appears to be a read operation rather than a write.

Make sure you can call the functions OK in C++ native code before attempting to do the C# interop code. It might be that my brain has switched to holiday mode, but it isn't clear from your description which are input and output parameters.
posted by CaveFrog at 4:04 AM on December 29, 2007


In my experience this is something that requires a fair amount of trial and error to get right. One thing that stands out in your code is that you really use MarshalAs a lot. A lot of the time, you can just declare things using the equivalent C# type, and everything will work out fine.

If you wanted to post your code that actually calls it, that might help. It's been a while since I've done any interop work, but if I see what you're actually doing it might be easier. One thing that comes to mind is that you may need to use Marshal.AllocHGlobal to get C# to allocate memory that your C code can write to. You then use something like Marshal.ReadByte to read the data into something managed code is cool with.

For example, I have the following code which uses IOControl to read data from a USB port:
IntPtr outBuff = Marshal.AllocHGlobal((int)USBPacketSize);

packets.Clear();

while (true)
{
	int bytesRead = 0;
	int success = USBSharp.USBSharp.DeviceIoControl((IntPtr)handle,
 	        USBSharp.USBSharp.IOCTL_ASYNC_IN, 
		IntPtr.Zero, 
		0, 
		outBuff, 
		(int)USBPacketSize, 
		ref bytesRead, 
		IntPtr.Zero);

	for(int i = 0; i < bytesRead; i++)
	{
		byte yoFace = Marshal.ReadByte(outBuff, i);  
		buffer.Add(yoFace);
	}

	if (success == 0)
	{
		int error = Marshal.GetLastWin32Error();
		if (error != 0)
			throw new Exception(error.ToString());
	}

	
	if (bytesRead != ASYNC_DATA_SIZE)
	{
		p = Packet.FromBytes(buffer.ToArray());
		packets.Enqueue(p);
		Marshal.FreeHGlobal(outBuff);
		break;
	}
}
If you'd like I can post that full project and you can peruse it. Feel free to contact me via email or AIM (both in the profile) with questions. This stuff is really fun to do, so even it feels like you're totally clueless, I encourage you to keep at it. pinvoke.net is the place to go to for stuff like this, by the way. It's basically a giant wiki with method signatures and things people have used to get this stuff to work.
posted by !Jim at 2:45 PM on December 29, 2007


I'm in a bit of a rush to get to the airport, but I can't leave you hanging after you've gone out of your way to help me:

CaveFrog: Sorry for not being clear enough. I am attempting to implement an interface for HFS+ (Mac) formatted disks under Windows. GetFile is a function which takes path as path to a file and returns offset which is the physical offset on-disk to the file and length is the length of the file. Thus, path is an input and offset and length are output parameters.

!Jim: That looks like it might actually be right down my alley! While I'm going to be gone for a while, if I can't figure out my issue I might just take you up on that offer afterwards. :-)

Thanks again, Mefi!
posted by PuGZ at 4:12 PM on December 29, 2007


« Older How does new value created by industry and economy...   |   Free (or Very Cheap) US Nationwide Dialup? Newer »
This thread is closed to new comments.