C# GetProcAddress Returns Zero
Asked Answered
C

4

15

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.

Cirillo answered 20/9, 2010 at 18:24 Comment(0)
C
19

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

Cardboard answered 20/9, 2010 at 18:38 Comment(2)
+1 I've been hinting my head against the wall for hours until google sent me here! After all it was only a matter of adding CharSet = CharSet.Ansi to the DllImport attribute's named parameters to make it work.Glans
Auto Marshal in C# is ill advised. Manually use "Encoding.ASCII.GetBytes" and unsafe code. Its far more portable between runtimes and platforms.Sidwel
C
2

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

Chalutz answered 20/9, 2010 at 18:34 Comment(3)
Vote for wrong name. exported function names usually looks very weird.Tolerable
I removed the error-handler code to make the code snippet simpler.Cirillo
@Jim: did you actually remove SetLastWin32Error before you posted? I know, smart-ass remark :)Chalutz
E
1

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.

Eous answered 20/9, 2010 at 18:33 Comment(0)
L
0

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.

Lynettalynette answered 20/9, 2010 at 18:36 Comment(1)
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.Swore

© 2022 - 2024 — McMap. All rights reserved.