Search code examples
c#dllpinvokemarshallingunmanaged

Dynamically invoke unmanaged code from C#


In general I need to be able to invoke any unmanaged function from any DLL which I don't know at the compile time.

All the articles I seen (like this https://blogs.msdn.microsoft.com/jonathanswift/2006/10/03/dynamically-calling-an-unmanaged-dll-from-net-c/) suggest using delegates but I don't know at the compile time which function I gonna invoke and even which and how many parameters it requires.

Basically I have a user input like: call "Kernel32.dll" function "DeleteFile" arguments ["C:\testfile.txt"].

Could you please advice at least how to google it? The word "dynamically" doesn't help..

The task itself is a bit crazy just because actually it's a university project. Not sure it can be useful in real life..

var dll = "kernel32.dll";
var fun = "DeleteFile";
var args = new object[] { "C:\\dev\\test.txt" };

IntPtr pDll = NativeMethods.LoadLibrary(dll);

IntPtr pFun = NativeMethods.GetProcAddress(pDll, fun);

// How can I call it in a different way, without having a delegate?
Marshal.GetDelegateForFunctionPointer(pFun, typeof(?????));

Solution

  • I also agree with idea of Roslyn, but when I see "Dynamic" and "P/Invoke", good old System.Reflection.Emit comes to mind:

    var asmName = new AssemblyName("Win32");
    var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
    var modBuilder = asmBuilder.DefineDynamicModule("Win32", emitSymbolInfo: false);
    var typeBuilder = modBuilder.DefineType("Win32.User32", TypeAttributes.Class | TypeAttributes.Public);
    
    // Optional: Use if you need to set properties on DllImportAttribute
    var dllImportCtor = typeof(DllImportAttribute).GetConstructor(new Type[] { typeof(string) });
    var dllImportBuilder = new CustomAttributeBuilder(dllImportCtor, new object[] { "user32.dll" });
    
    var pinvokeBuilder = typeBuilder.DefinePInvokeMethod(
        name:              "ShowMessageBox",
        dllName:           "user32.dll",
        entryName:         "MessageBoxW",
        attributes:        MethodAttributes.Static | MethodAttributes.Public,
        callingConvention: CallingConventions.Standard,
        returnType:        typeof(int),  // typeof(void) if there is no return value.
        // TODO: Construct this array from user input somehow:
        parameterTypes:    new Type[] { typeof(IntPtr), typeof(string), typeof(string), typeof(uint) },
        nativeCallConv:    CallingConvention.Winapi,
        nativeCharSet:     CharSet.Unicode);
    
    pinvokeBuilder.SetCustomAttribute(dllImportBuilder);
    
    Type user32Type = typeBuilder.CreateType();
    
    const uint MB_YESNOCANCEL = 3;
    
    user32Type
        .GetMethod("ShowMessageBox", BindingFlags.Static | BindingFlags.Public)
        // TODO: User input goes here:
        .Invoke(null, new object[] { IntPtr.Zero, "Message Text", "Message Caption", MB_YESNOCANCEL });
    

    Not pretty, I know. Just my $0.02.

    WARNING: If this code is going to be called multiple times in a long running application, consider creating a new AppDoman each time, and disposing it when call is done; because this is the only way to unload generated dynamic assembly.