Call C function inside C# via function pointer

前端 未结 1 1481
南旧
南旧 2020-12-17 04:43

I have a static function in my C program which address is transmitted to a C# .NET program. The address is correct, but is it possible to call this function within C#?

相关标签:
1条回答
  • 2020-12-17 04:58

    You'll want to use Marshal.GetDelegateForFunctionPointer. You won't even need to use unsafe code.

    delegate void TestCallbackDelegate(); //must match the signature of test_callback()
    
    public static void callCallback(int ptr)
    {
        IntPtr nativePtr = new IntPtr( ptr );
    
        var callback = Marshal.GetDelegateForFunctionPointer<TestCallbackDelegate>( nativePtr );
    
        callback();
    }
    
    public static int test(string param)
    {
        char[] ptrChar = param.ToCharArray();
        int ptrInt = 0;
    
        ptrInt = ( ((int)(0xFF00 & (int)ptrChar[1]) | (0x00FF & (int)ptrChar[1])) << 16 ) |
                    (int)(0xFF00 & (int)ptrChar[0]) | (0x00FF & (int)ptrChar[0]);
    
        callCallback(ptrInt);
    }
    

    Although a much simpler way would be to just pass a void* to the C# method, and it will automatically get marshalled to IntPtr. Here's a minimal example of that:

    C++

    //invoke.cpp
    //compile with: cl /EHsc /LD /nologo invoke.cpp
    #include <stdio.h>
    
    static void test_callback() 
    {
        printf("test_callback called\n");
    }
    
    extern "C" __declspec( dllexport ) void* getPointer()
    {
        return (void*)&test_callback; //Return a raw pointer to the test_callback function.
    }
    

    C#

    //invoke.cs
    //compile with: csc /nologo invoke.cs
    using System;
    using System.Runtime.InteropServices;
    
    class Program
    {
        [DllImport( "invoke.dll" )]
        private static extern IntPtr getPointer();
    
        private delegate void TestCallbackDelegate(); //Delegate that matches the signature of test_callback
    
        static void main()
        {
            IntPtr ptr = getPointer(); //Fetch the native void pointer.
            TestCallbackDelegate test_callback = Marshal.GetDelegateForFunctionPointer<TestCallbackDelegate>( ptr ); //Marshal the void pointer to a delegate.
            test_callback(); //Invoke the native C function.
        }
    }
    

    I used the DllImport attribute to avoid having to invoke the CLR as you're doing, but it's the same idea.


    EDIT: Because I realized the above doesn't apply to what the OP was asking, I'll include an updated and proper sample. The above will remain for posterity.

    C

    #define COBJMACROS
    
    #include <stdio.h>
    #include <windows.h>
    #include <mscoree.h>
    
    static void test_callback()
    {
        printf( "test_callback has been called.\n" );
    }
    
    int main( void )
    {
        HRESULT status;
        ICLRRuntimeHost *Host;
        BOOL Started;
        DWORD Result;
    
        Host = NULL;
        Started = FALSE;
    
        status = CorBindToRuntimeEx( NULL, NULL, 0, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (void**)&Host );
    
        if( FAILED( status ) )
            goto cleanup;
    
        status = ICLRRuntimeHost_Start( Host );
    
        if( FAILED( status ) )
            goto cleanup;
    
        Started = TRUE;
    
        int ptr = (int)&test_callback;
        printf( "test_callback is at 0x%X\n", ptr );
    
        char param[5];
        param[0] = 0xFF & ( ptr >> 0 );
        param[1] = 0xFF & ( ptr >> 8 );
        param[2] = 0xFF & ( ptr >> 16 );
        param[3] = 0xFF & ( ptr >> 24 );
        param[4] = '\0';
    
        status = ICLRRuntimeHost_ExecuteInDefaultAppDomain( Host, L"invoke.dll", L"InteropTesting.Invoker", L"InvokeCallback", (LPCWSTR)param, &Result );
    
        if( FAILED( status ) )
            goto cleanup;
    
    cleanup:
        if( Started )
            ICLRRuntimeHost_Stop( Host );
    
        if( Host != NULL )
            ICLRRuntimeHost_Release( Host );
    
        return SUCCEEDED( status ) ? 0 : 1;
    }
    

    C#

    using System;
    using System.Runtime.InteropServices;
    
    namespace InteropTesting
    {
        public static class Invoker
        {
            private delegate void TestCallbackDelegate();
    
            public static int InvokeCallback( string param )
            {
                //C# has a built-in means of turning byte arrays into integers
                //so we'll use BitConverter instead of using the bitwise operators.
                char[] chars = param.ToCharArray();
                int ptr = BitConverter.ToInt32( Array.ConvertAll( chars, c => (byte)c ), 0 );
    
                var test_callback = (TestCallbackDelegate)Marshal.GetDelegateForFunctionPointer( new IntPtr( ptr ), typeof( TestCallbackDelegate ) );
                test_callback();
    
                return 0;
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题