views:

111

answers:

4

For some reason, whenever my C# .NET 2.0 application makes a call to GetProcAddress it always returns zero.

public class MyClass
{
    internal static class UnsafeNativeMethods
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern IntPtr LoadLibrary(string lpFileName);

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

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
    }

    private void MyFunc()
    {
        IntPtr _dllHandle;
        IntPtr _fptr;
        string _fullPath = ".\\mydll.dll";
        string _procName = "MyDllFunc";

        _dllHandle = UnsafeNativeMethods.LoadLibrary(_fullPath);
        _fptr = UnsafeNativeMethods.GetProcAddress(_dllHandle, _procName); // <-- Always returns zero.
    }
}

I'm sure the function name is spelled correctly, and _fullPath is presumably correct because _dllHandle is always assigned a non-zero value. Any insight you may be able to provide is appreciated. Thanks.

+1  A: 

You have not shown how you export the function from the DLL, but I suspect the problem is that the exported name is not what you thing it is. You can run dumpbin /exports mydll.dll to view the exports of the dll to verify the name.

If you show the a code snippet of the export, I could provide more direct advice. You can try decorate the exported function with extern "C" to eliminate the name mangling as a test.

Chris Taylor
+2  A: 

You really need to add some error checking. At least verify if _dllHandle != IntPtr.Zero. Also, depending on the current working directory is dangerous, use Assembly.GetEntryAssembly().Location to get a full path name.

The function name is probably wrong. Exports tends to be decorated, like _MyDllFunc or _MyDllFunc@4. More wildly if it was compiled by a C++ compiler. Use Dumpbin.exe /exports on your DLL to see the real names.

Back to error handling, use SetLastWin32Error in the [DllImport] attribute. Throw Win32Exception if the function returns false or IntPtr.Zero.


Edit: I see the real problem. Using CharSet.Auto for GetProcAddress() is wrong. Very unlucky, it is just about the only Windows API function that only has an ANSI version. You have to use CharSet.Ansi. A good place to get proper [DllImport] declarations is pinvoke.net

Hans Passant
Vote for wrong name. exported function names usually looks very weird.
Sergey Mirvoda
I removed the error-handler code to make the code snippet simpler.
Jim Fell
@Jim: did you actually remove SetLastWin32Error before you posted? I know, smart-ass remark :)
Hans Passant
A: 

Does your export in the .DEF file for the DLL match the input here? You can use dumpbin to find out what's exported, per other replies here.

What is the underlying Win32 error on GetProcAddress(), per GetLastError()?

You could try this in native code to work out the correct inputs first without the additional baggage of P/Invoke.

Steve Townsend
BTW you can get the last error by throwing a Win32Exception: `throw new Win32Exception();` The parameterless constructor calls `Marshal.GetLastWin32Error()` and gives a detailed error message.
dtb
+4  A: 

GetProcAddress only comes in an ANSI flavor, hence we help the runtime by telling it to always use ANSI when marshalling the string parameter. We also prevent the runtime looking for a non-existent GetProcAddressA, because the default for C# is to set ExactSpelling to false.

http://www.pinvoke.net/default.aspx/kernel32.getprocaddress

Snoopy
+1, you got that before I saw it.
Hans Passant
That did the trick! Thank you!
Jim Fell