none
动态调用非托管DLL 传址方式参数的问题 RRS feed

  • 问题

  • 我在参考网上的例子后,编写了一个invoke方法,用来动态调用非托管DLL中的函数,但遇到了一个问题. 
    现在的情况是,我需要调用的函数正确的完成了功能,并且返回值正确. 
    但是,在使用传址方式(ByRef)传递参数时,被传递的参数值并没有随之改变. 
    希望有经验的朋友帮忙解决. 
    附invoke代码如下: 
     ''' <summary>
        ''' 调用指定函数
        ''' </summary>
        ''' <param name="farProc">函数入口指针</param>
        ''' <param name="ObjArray_Parameter">实参列表</param>
        ''' <param name="TypeArray_ParameterType">实参类型列表</param>
        ''' <param name="ModePassArray_Parameter">实参传递方式列表</param>
        ''' <param name="Type_Return">返回值类型</param>
        ''' <returns>返回所调用函数的 object</returns>
        ''' <remarks></remarks>
        Public Function Invoke(ByVal farProc As IntPtr, ByRef ObjArray_Parameter As Object(), ByVal TypeArray_ParameterType As Type(), _
                                ByVal ModePassArray_Parameter As ModePass(), ByVal Type_Return As Type) As Object
            If Me.hModule = IntPtr.Zero Then
                Throw New Exception("函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")
            End If
            If farProc = IntPtr.Zero Then
                Throw New Exception(" 函数指针为空 , 请确保已进行 LoadFun 操作 ! ")
            End If
            If ObjArray_Parameter.Length <> ModePassArray_Parameter.Length Then
                Throw New Exception("参数个数及其传递方式的个数不匹配 ")
            End If
            '创建 MyAssemblyName 对象并设置其 Name 属性
            Dim MyAssemblyName As New AssemblyName
            MyAssemblyName.Name = "InvokeFun"
    
            '生成单模块配件
            Dim MyAssemblyBuilder As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(MyAssemblyName, _
                                                                                                     AssemblyBuilderAccess.Run)
    
            Dim MyModuleBuilder As ModuleBuilder = MyAssemblyBuilder.DefineDynamicModule("InvokeDll")
    
            '定义要调用的方法 , 方法名为“ MyFun ”,返回类型是“ Type_Return ”参数类型是“ TypeArray_ParameterType ” 
            Dim MyMethodBuilder As MethodBuilder = MyModuleBuilder.DefineGlobalMethod("MyFun", _
                                MethodAttributes.Public Or MethodAttributes.Static, Type_Return, TypeArray_ParameterType)
            Dim IL As ILGenerator = MyMethodBuilder.GetILGenerator
            For i As Integer = 0 To ObjArray_Parameter.Length - 1
                Select Case ModePassArray_Parameter(i)
                    Case ModePass.ByValue
                        IL.Emit(OpCodes.Ldarg, i)
                    Case ModePass.ByRefe
                        IL.Emit(OpCodes.Ldarga, i)
                    Case Else
                        Throw New Exception("第 " & i.ToString() & " 个参数没有给定正确的传递方式 .")
                End Select
            Next
    
            If IntPtr.Size = 4 Then
                IL.Emit(OpCodes.Ldc_I4, farProc.ToInt32)
            ElseIf IntPtr.Size = 8 Then
                IL.Emit(OpCodes.Ldc_I8, farProc.ToInt64)
            Else
                Throw New PlatformNotSupportedException
            End If
    
            IL.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, Type_Return, TypeArray_ParameterType)
    
            IL.Emit(OpCodes.Ret) ' 返回值 ]
    
    
            MyModuleBuilder.CreateGlobalFunctions()
            '取得方法信息 
    
            Dim MyMethodInfo As MethodInfo = MyModuleBuilder.GetMethod("MyFun")
    
            Return MyMethodInfo.Invoke(Nothing, ObjArray_Parameter) ' 调用方法,并返回其值 
        End Function
    
    

    2009年8月4日 5:55

全部回复

  • 你好,本来我刚才打算帮写出来的,但是由于好久没用VB了差不多都忘记了,用一些转换工具从VB转换到C#转换过来也不能编译成功
    所以我只能在这里给你说下方法了
    由于之前我也做过类似这样的东西,所以对这方面比较熟悉,你可以参考一下这篇文章,不过是C#版本的
    http://www.manuelabadia.com/blog/PermaLink,guid,dc72b235-1381-4c91-8706-e36216f49b94.aspx

    其中的代码段





    // iterates through the parameters updating the parameters passed by ref

                for (int i = 0; i < paramTypes.Length; i++) {

                    if (parameters[i].ParameterType.IsByRef) {

                        ilGenerator.Emit(OpCodes.Ldarg_1);

                        EmitFastInt(ilGenerator, i);

                        ilGenerator.Emit(OpCodes.Ldloc, locals[i]);

                        if (locals[i].LocalType.IsValueType)

                            ilGenerator.Emit(OpCodes.Box, locals[i].LocalType);

                        ilGenerator.Emit(OpCodes.Stelem_Ref);

                    }

                }

    这里就是将Ref参数的值保存

    paramTypes是方法参数类型的集合
    parameters是方法参数ParameterInfo类型的集合 ParameterInfo[] parameters = methodInfo.GetParameters();

    locals是定义的本地局部变量的集合

    通过这段代码赋值的
    LocalBuilder[] locals = new LocalBuilder[paramTypes.Length];

     

                // generates a local variable for each parameter

                for (int i = 0; i < paramTypes.Length; i++) {

                    locals[i] = ilGenerator.DeclareLocal(paramTypes[i], true);

                }

     

                // creates code to copy the parameters to the local variables

                for (int i = 0; i < paramTypes.Length; i++) {

                    ilGenerator.Emit(OpCodes.Ldarg_1);

                    EmitFastInt(ilGenerator, i);

                    ilGenerator.Emit(OpCodes.Ldelem_Ref);

                    EmitCastToReference(ilGenerator, paramTypes[i]);

                    ilGenerator.Emit(OpCodes.Stloc, locals[i]);

                }

     


    它的大体思路是 先将买个参数保存到局部变量中,然后通过引用的方式保存局部变量,不知道我这句话表述得合不合适


    Wenn ich dich hab’,gibt es nichts, was unerträglich ist.坚持不懈!http://hi.baidu.com/1987raymond
    • 已标记为答案 Kira Qian 2009年8月10日 8:26
    • 取消答案标记 米沙 2009年8月12日 1:46
    2009年8月4日 9:09
    版主
  • 谢谢版主的解答
    有几个疑问
    1.我如何获得MethodInfo?我在加载时,已经获取了函数的句柄,但我不清楚如何才能获取MethodInfo.
    2.如何执行? 例子里是通过动态方法的委托实现的.而我现在还不清楚如何建立这个动态方法.
    3.当我建立LocalBuilder,并将这个传递到MethodInfo.Invoke时,程序报类型不匹配的错误."Uint16(我动态库的函数中使用的数据类型)不能转换为LocalBuilder"
    2009年8月5日 5:35
  • 你好你可以通过Type.GetMethod获得那个MethodInfo
    Wenn ich dich hab’,gibt es nichts, was unerträglich ist.坚持不懈!http://hi.baidu.com/1987raymond
    • 已标记为答案 Kira Qian 2009年8月10日 8:26
    • 取消答案标记 米沙 2009年8月12日 1:46
    2009年8月5日 6:24
    版主
  • 你好 你可以继续关注 http://social.microsoft.com/Forums/zh-CN/visualcpluszhchs/thread/f40ddde8-d57c-4b3d-9d87-e0555b349373

    Wenn ich dich hab’,gibt es nichts, was unerträglich ist.坚持不懈!http://hi.baidu.com/1987raymond
    2009年8月5日 7:03
    版主
  • 我是通过VB.net调用windows API来获取的函数句柄.
    代码如下
        ''' <summary>
        ''' 获得函数指针
        ''' </summary>
        ''' <param name="lpFileName"> 包含需调用函数的 DLL 文件名</param>
        ''' <param name="lpProcName">调用函数的名称</param>
        ''' <remarks></remarks>
        Public Function LoadFunc(ByVal lpFileName As String, ByVal lpProcName As String) As IntPtr
            Me.hModule = LoadDll(lpFileName)
            If Me.hModule = IntPtr.Zero Then
                Throw New Exception("函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")
            End If
    
            Dim farProc As IntPtr = GetProcAddress(hModule, lpProcName)
    
            If farProc = IntPtr.Zero Then
                Throw New Exception(" 没有找到 :" & lpProcName & " 这个函数的入口点 ")
            Else
                Return farProc
            End If
        End Function
    2009年8月5日 8:15
  • 没有看太明白你的意思,

    你的意思应该是 ObjArray_Parameter 的值在最后调用
     Return MyMethodInfo.Invoke(Nothing, ObjArray_Parameter) ' 调用方法,并返回其值 
    

    的时候没有改变吧?

    你一直都没有重新给 ObjArray_Parameter 赋值,它肯定不会改变了.

    没了解你的意思.将仔细些,如果有问题,发消息给我
    I am Rayman Zhai
    2009年8月5日 12:00
  • 没有看太明白你的意思,

    你的意思应该是 ObjArray_Parameter 的值在最后调用
     Return MyMethodInfo.Invoke(Nothing, ObjArray_Parameter) ' 调用方法,并返回其值 
    
    

    的时候没有改变吧?

    你一直都没有重新给 ObjArray_Parameter 赋值,它肯定不会改变了.

    没了解你的意思.将仔细些,如果有问题,发消息给我
    I am Rayman Zhai
    是的,我应该如何赋值?
    或者说我应该将什么值赋给ObjArray_Parameter
    我的联系方式 QQ 8621024  MSN linlin@tcps.com.cn 
    2009年8月6日 0:45
  • 没有看太明白你的意思,

    你的意思应该是 ObjArray_Parameter 的值在最后调用
     Return MyMethodInfo.Invoke(Nothing, ObjArray_Parameter) ' 调用方法,并返回其值 
    
    
    
    

    的时候没有改变吧?

    你一直都没有重新给 ObjArray_Parameter 赋值,它肯定不会改变了.

    没了解你的意思.将仔细些,如果有问题,发消息给我
    I am Rayman Zhai

    他的意思是说比如FunctionName(ref int i)
    {
    i=100;
    }
    当这段代码执行后i的值没有改变
    Wenn ich dich hab’,gibt es nichts, was unerträglich ist.坚持不懈!http://hi.baidu.com/1987raymond
    2009年8月6日 2:29
    版主
  • 是的 就如1987raymond版主所说的
    我的DLL中的函数会对ref方式传入的参数赋值. 但在这个方法中,我传入的参数并没有获得这个值.
    版主提供的办法,我感觉应该是对托管类进行处理.
    2009年8月6日 2:53
    • 已标记为答案 Kira Qian 2009年8月10日 8:26
    • 取消答案标记 米沙 2009年8月12日 1:46
    2009年8月7日 0:46
    版主
  • MethodBuilder.DefineParameter 方法
    http://msdn.microsoft.com/zh-cn/architecture/system.reflection.emit.methodbuilder.defineparameter(VS.85).aspx
    http://feiyun0112.cnblogs.com/
    因为一些事情,我有一段时间没能回来看帖子.
    这个方法查不到例子.我只能简单的认为这是在MethodBuilder建立后,完善方法的参数.包括方法的返回值和参数的传递,我只知道,传值的参数应该标注为"in".传址的参数是不是标注为"out"?
    另外即使我定义了一个完整的MethodBuilder,但问题还是没有解决.我该如何得到我的传址参数的返回值?
    2009年8月12日 1:48