微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – CLR主机:使用任意方法签名调用函数?

我需要一个C程序,加载CLR并调用C#库中的函数.我需要调用函数将COM接口作为参数.

我的问题是,CLR托管接口似乎只允许您使用此签名调用方法

int Foo(String arg)

例如,此C代码加载CLR并在“test.exe”中运行P.Test函数

ICLRRuntimeHost *pClrHost = NULL;
HRESULT hrCorBind = CorBindToRuntimeEx(NULL,L"wks",CLSID_CLRRuntimeHost,IID_ICLRRuntimeHost,(PVOID*)&pClrHost);

HRESULT hrStart = pClrHost->Start();

DWORD retVal;
HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(L"C:\\Test.exe",L"P",L"Test",L"",&retVal);

我需要做的是用这个方法签名调用一个函数(注意我拥有C#代码,所以我可以改变它):

void SomeFunction(IFoo interface)

其中IFoo是一个com接口.如果我可以调用这样的函数,我甚至可以做我需要的东西:

IntPtr SomeFunction();

我可以让SomeFunction构造一个正确的委托,然后使用Marshal.GetFunctionPointerForDelegate.但是,我无法弄清楚除了使用int func(string)签名调用函数之外,如何使托管接口执行任何操作.

有谁知道如何从具有不同签名的C代码调用C#函数

(注意我不能使用C/C++LI.我需要使用托管API.)

解决方法

编辑:我答应更新我的答案,包括传递64位值的代码,所以这里是..

>如果有人对32位系统的不太复杂的解决方案感兴趣,我就离开了原来的答案.

注意:由于你使用的是在.net 4.0中已经过时的CorBindToRuntimeEx,我将假设使用旧版Win32 API的.net 2.0兼容解决方案.

因此,为了在C#和C之间传递数据(在我们的例子中是委托的IntPtr),我们将使用两种直接的方法创建一个名为SharedMem的小型Win32 DLL项目.

SharedMem.h

#pragma once

#ifdef SHAREDMEM_EXPORTS
#define SHAREDMEM_API __declspec(dllexport)
#else
#define SHAREDMEM_API __declspec(dllimport)
#endif

#define SHAREDMEM_CALLING_CONV __cdecl

extern "C" {
    SHAREDMEM_API BOOL SHAREDMEM_CALLING_CONV SetSharedMem(ULONGLONG _64bitValue);
    SHAREDMEM_API BOOL SHAREDMEM_CALLING_CONV GetSharedMem(ULONGLONG* p64bitValue);
}

现在为实现文件

SharedMem.cpp

#include "stdafx.h"
#include "SharedMem.h"

HANDLE      hMappedFileObject = NULL;  // handle to mapped file
LPVOID      lpvSharedMem = NULL;       // pointer to shared memory
const int   SHARED_MEM_SIZE = sizeof(ULONGLONG);

BOOL CreateSharedMem()
{
    // Create a named file mapping object
    hMappedFileObject = CreateFileMapping(
                            INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,SHARED_MEM_SIZE,TEXT("shmemfile") // Name of shared mem file
                        );

    if (hMappedFileObject == NULL) 
    {
        return FALSE;
    }

    BOOL bFirstinit = (ERROR_ALREADY_EXISTS != GetLastError());

    // Get a ptr to the shared memory
    lpvSharedMem = MapViewOfFile( hMappedFileObject,FILE_MAP_WRITE,0);

    if (lpvSharedMem == NULL) 
    {
        return FALSE; 
    }

    if (bFirstinit) // First time the shared memory is accessed?
    {
        ZeroMemory(lpvSharedMem,SHARED_MEM_SIZE); 
    }

    return TRUE;
}

BOOL SetSharedMem(ULONGLONG _64bitValue) 
{ 
    BOOL bOK = CreateSharedMem();

    if ( bOK )
    {
        ULONGLONG* pSharedMem = (ULONGLONG*)lpvSharedMem;
        *pSharedMem = _64bitValue;
    }

    return bOK;
}

BOOL GetSharedMem(ULONGLONG* p64bitValue) 
{ 
    if ( p64bitValue == NULL ) return FALSE;

    BOOL bOK = CreateSharedMem();

    if ( bOK )
    {
        ULONGLONG* pSharedMem = (ULONGLONG*)lpvSharedMem;
        *p64bitValue = *pSharedMem;
    }

    return bOK;
}

>请注意,为简单起见,我只是共享一个64位值,但这是在C#和C之间共享内存的一般方法.您可以随意放大SHARED_MEM_SIZE和/或添加功能,以便根据需要共享其他数据类型.

这就是我们将如何使用上述方法:我们将在C#端使用SetSharedMem(),以便将委托的IntPtr设置为64位值(无论代码是在32位还是64位系统上运行) .

C#代码

namespace CSharpCode
{
    delegate void VoidDelegate();

    static public class COMInterfaceClass
    {
        [DllImport( "SharedMem.dll" )]
        static extern bool SetSharedMem( Int64 value );

        static GCHandle gcDelegateHandle;

        public static int EntryPoint(string ignored)
        {
            IntPtr pFunc = IntPtr.Zero;
            Delegate myFuncDelegate = new VoidDelegate( SomeMethod );
            gcDelegateHandle = GCHandle.Alloc( myFuncDelegate );
            pFunc = Marshal.GetFunctionPointerForDelegate( myFuncDelegate );
            bool bSetoK = SetSharedMem( pFunc.ToInt64() );
            return bSetoK ? 1 : 0;
        }

        public static void SomeMethod()
        {
            MessageBox.Show( "Hello from C# SomeMethod!" );
            gcDelegateHandle.Free();
        }
    }
}

>注意使用GCHandle以防止委托被垃圾收集.
>对于好的措施,我们将使用返回值作为成功/失败标志.

在C端,我们将使用GetSharedMem()提取64位值,将其转换为函数指针并调用C#委托.

C代码

#include "SharedMem.h"
typedef void (*VOID_FUNC_PTR)();

void ExecCSharpCode()
{
    ICLRRuntimeHost *pClrHost = NULL;
    HRESULT hrCorBind = CorBindToRuntimeEx(
                                NULL,(PVOID*)&pClrHost
                            );

    HRESULT hrStart = pClrHost->Start();

    DWORD retVal;
    HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(
                                szPathToAssembly,L"CSharpCode.COMInterfaceClass",L"EntryPoint",&retVal // 1 for success,0 is a failure
                            );

    if ( hrExecute == S_OK && retVal == 1 )
    {
        ULONGLONG nSharedMemValue = 0;
        BOOL bGotValue = GetSharedMem(&nSharedMemValue);
        if ( bGotValue )
        {
            VOID_FUNC_PTR CSharpFunc = (VOID_FUNC_PTR)nSharedMemValue;
            CSharpFunc();
        }
    }
}

原始答案 – 适用于32位系统

这是一个基于使用IntPtr.ToInt32()的解决方案,以转换委托func. PTR.到从静态C#EntryPoint方法返回的int.

(*)注意使用GCHandle以防止代理被垃圾收集.

C#代码

namespace CSharpCode
{
    delegate void VoidDelegate();

    public class COMInterfaceClass
    {
        static GCHandle gcDelegateHandle;

        public static int EntryPoint(string ignored)
        {
            IntPtr pFunc = IntPtr.Zero;
            Delegate myFuncDelegate = new VoidDelegate( SomeMethod );
            gcDelegateHandle = GCHandle.Alloc( myFuncDelegate );
            pFunc = Marshal.GetFunctionPointerForDelegate( myFuncDelegate );
            return (int)pFunc.ToInt32();
        }

        public static void SomeMethod()
        {
            MessageBox.Show( "Hello from C# SomeMethod!" );
            gcDelegateHandle.Free();
        }
    }
}

C代码
我们需要将返回的int值转换为函数指针,因此我们首先定义一个void函数ptr.类型:

typedef void (*VOID_FUNC_PTR)();

其余代码看起来非常像原始代码,添加了转换和执行函数ptr.

ICLRRuntimeHost *pClrHost = NULL;
HRESULT hrCorBind = CorBindToRuntimeEx(
                            NULL,(PVOID*)&pClrHost
                        );

HRESULT hrStart = pClrHost->Start();

DWORD retVal;
HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain(
                            szPathToAssembly,&retVal
                        );

if ( hrExecute == S_OK )
{
    VOID_FUNC_PTR func = (VOID_FUNC_PTR)retVal;
    func();
}

一点额外的

您还可以使用字符串输入来选择要执行的方法

public static int EntryPoint( string interfaceName )
{
    IntPtr pFunc = IntPtr.Zero;

    if ( interfaceName == "SomeMethod" )
    {
        Delegate myFuncDelegate = new VoidDelegate( SomeMethod );
        gcDelegateHandle = GCHandle.Alloc( myFuncDelegate );
        pFunc = Marshal.GetFunctionPointerForDelegate( myFuncDelegate );
    }

    return (int)pFunc.ToInt32();
}

>您可以通过使用反射来获得更多通用,以便根据给定的字符串输入找到正确的方法.

原文地址:https://www.jb51.cc/csharp/243354.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐