如何调用com组件中包含IntPtr类型参数的函数

背景

  公司的支付平台最近对接了西安移动的支付接口,接口中签名的方法是对方提供了一个com组件,组件中包含了一个签名的方法和一个验签的方法,注册了签名之后,在vs中进行了引用,引用之后,查看组件的定义如下:

using System;
using System.Runtime.InteropServices;

namespace UMPAYLib
{
    [ClassInterface(0)]
    [Guid("E92EB0AA-00CC-4F93-A76D-632BEA94E980")]
    [TypeLibType(2)]
    [ComConversionLoss]
    public class SignClass : ISign, Sign
    {
        public SignClass();

        [DispId(1)]
        public virtual string Sign(string str, string certfile, string keyfile);
        [DispId(2)]
        public virtual int Verify(string str, string sig, IntPtr certfile);
    }
}

  首先先看一下签名的方法:Sign(string str,string certfile,string keyfile);

  三个参数分别是用于签名的串、公钥证书的路径和私钥证书的路径。

  再看验签的方法:Verify(string str,string sig,IntPtr certfile);

  三个参数分别是用于签名的串,要验证的签名值和公钥证书的路径。

  那么问题来了,验签方法的第三个参数,证书的路径怎么是IntPtr类型呢?IntPtr到底是个什么类型呢?我该怎么调用这个方法呢?

解决过程

  首先我问题了接口方,接口放的对接人员倒是挺负责任,帮我看文档,问同事,可接口方看过他们自己的文档之后,也郁闷了,他们也不清楚文档咋和组件里的方法定义不一样,他们说要请示总部,而请示总部要用邮件,而且半天也不见回复,可接口后天就要上线测试了,等回复看来不靠谱,还得靠自己,于是就开始了求助度娘。

  首先我们来看看IntPtr到底是个什么类型?

  MSDN的解释:

用于表示指针或句柄的平台特定类型。
备注:
IntPtr 类型被设计成整数,其大小适用于特定平台。 即是说,此类型的实例在 32 位硬件和操作系统中将是 32 位,在 64 位硬件和操作系统上将是 64 位。
IntPtr 类型可以由支持指针的语言使用,并可作为在支持与不支持指针的语言间引用数据的一种通用方式。
IntPtr 对象也可用于保持句柄。 例如,IntPtr 的实例广泛地用在 System.IO.FileStream 类中来保持文件句柄。
IntPtr 类型符合 CLS,而 UIntPtr 类型却不符合。 只有 IntPtr 类型可用在公共语言运行时中。 UIntPtr 类型大多数是提供来维护与 IntPtr 类型之间的体系结构上的对称性。
此类型实现 ISerializable 接口。

  其中一行是这样说的:

  IntPtr类型可以由支持指针的语言使用,并可作为在支持与不支持指针的语言间引用数据的一种通用方式。

引用数据的通用方式?指针?看到这个之后,我就在度娘里输入了“IntPtr传字符串“几个字,搜索结果中看到了一篇园子里一个仁兄写的博客,http://www.cnblogs.com/jxsoft/archive/2011/07/06/2099061.html,正式这篇博客,让我豁然开朗,找到了问题最终的解决办法,虽然解决的方法不是用的这位仁兄的方法,但思路是从这儿而来,所以还是要谢谢”许明吉博客“了。

  我先是用了这篇博客中的如下这个方法进行测试:

/// <summary>
        /// 根据数据的长度申请非托管空间
        /// </summary>
        /// <param name="strData">要申请非托管空间的数据</param>
        /// <returns>指向非拖管空间的指针</returns>
        private static IntPtr mallocIntptr( string strData )
        {
            //先将字符串转化成字节方式
            Byte[] btData = System.Text.Encoding.Default.GetBytes(strData);  

            //申请非拖管空间
            IntPtr m_ptr = Marshal.AllocHGlobal(btData.Length);  

            //给非拖管空间清0
            Byte[] btZero = new Byte[btData .Length+ 1]; //一定要加1,否则后面是乱码,原因未找到
            Marshal.Copy(btZero, 0, m_ptr, btZero.Length);  

            //给指针指向的空间赋值
            Marshal.Copy(btData, 0, m_ptr, btData.Length);  

            return m_ptr;
        }  

  测试的代码如下:

UMPAYLib.SignClass signClass = new UMPAYLib.SignClass();IntPtr ptrCertFile = mallocIntptr(certFile);
int b = signClass.Verify(prestr, SIGN, ptrCertFile);

运行测试的页面,当执行到signClass.Verify这个com组件验签的方法的时候,报了一个”没有足够的内存来继续运行程序“的异常,组件内部的代码也看不到,所以也不知道里面怎么处理导致了这个内存溢出的异常,好不容易找到了思路,却有出现了这个问题,该怎么办呢?于是我又看了看ptrCertFile的属性和方法,我输出了ptrCertFile的.ToString(),发现得到了一个很大的数字,可还是不知道为什么会内存溢出,我又仔细看了看,mallocIntptr这个方法,方法里有一个分配内存的类引起了我的兴趣,它就是Marshal,我们再来看看Mashal这个类具体是干啥的?有啥方法?

MSDN的解释:

提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。

从解释中可以看出,该类主要是用于分配非托管内存和在托管类型和非托管类型之间进行转换。

于是我就浏览了一下Mashal类的成员,发现了一个方法:

而这个方法也本身就能实现上面mallocIntptr这个方法的功能,于是我就把代码修改为如下:

UMPAYLib.SignClass signClass = new SignClass();
string certPath = MobileWapPayConfig.CertFile;
IntPtr ptrCertFile = Marshal.StringToBSTR(certPath);
int result = signClass.Verify(prestr, sign, ptrCertFile);
Marshal.FreeBSTR(ptrCertFile);
return result == 0;

重新运行测试页面,一切正常。

至此,最初遇到的IntPtr不知如何调用的问题已经解决了,但遗留了一个小问题,那就是为什么mallocIntptr这个方法会导致内存溢出,望了解的朋友不吝赐教!

总结

对于一些com组件的方法参数IntPtr类型的,可以使用Marshal类的相关方法来处理。

时间: 2024-10-11 23:04:58

如何调用com组件中包含IntPtr类型参数的函数的相关文章

父组件调用子组件中的方法- this.$refs.xxx.子组件方法();

子组件中有一个说的方法 在父组件中去调用当你点击的时候 去调用子组件中的方法 fu.vue 在父组件的方法中调用子组件的方法,很重要 this.$refs.mychild.parentHandleclick(); { <template> <div> <button @click="clickParent">点击 调用子组件</button> <child ref="mychild"></child&

微信小程序自定义组件的使用以及调用自定义组件中的方法

在写小程序的时候,有时候页面的内容过多,逻辑比较复杂,如果全部都写在一个页面的话,会比较繁杂,代码可读性比较差,也不易于后期代码维护,这时候可以把里面某部分功能抽出来,单独封装为一个组件,也就是通常说的自定义组件,自定义组件类似于页面,它有wxml 模版.wxss 样式和js文件,然后在页面中使用该自定义组件即可. 例如,我的自定义组件代码结构是这样的: myComponent文件就是我所创的自定义组件,myComponent.wxml文件代码为: <view class="inner&q

深入理解--VUE组件中数据的存放以及为什么组件中的data必需是函数

1.组件中数据的存放 ***(重点)组件是一个单独模块的封装:这个模块有自己的HTML模板,也有data属性. 只是这个data属性必需是一个函数,而这个函数返回一个对象,这个对象里面存放着组件的数据. <template id="MyCpn"> <div> <h2>组件数据的存放 </h2> <p>{{title}}</p> </div> </template> <script>

time.h文件中包含的几个函数使用时须注意事项

time.h头文件中包含以下函数 char* asctime(const struct tm *tm); char* asctime_r(const struct tm *tm,char *buf); char* ctime(const time_t *timep); char* ctime_r(const time_t *timep,char *buf); struct tm *gmtime(const time_t *timep); struct tm *gmtime_r(const tim

组件中 data 为什么是一个函数?

如果两个实例引用同一个对象,当其中一个实例的属性发生改变时,另一个实例属性也随之改变,对象没有自己的作用域,只有当两个实例拥有自己的作用域时,才不会相互干扰. 这是因为JavaScript的特性所导致,在component中,data必须以函数的形式存在,不可以是对象 组件中的data写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱. 而单纯的写成对象形式,就是所有的组件实

react:在一个组件中调用别的组件中的方法

先介绍一下要解决的问题:react中一个组件A和一个组件B,其中B是被connect(connect是redux中的方法)包装过的组件,包装成BContainer,A和BContainer的关系是兄弟关系,在同一个父元素下渲染.现在我们要在点击A的时候调用B中的方法 解决思路:主要是用到ref获取BContainer组件挂载之后的实例 render(){ var b = null return(<BContainer ref={(node) => b = node}/>) } ref中的

react 父组件调用子组件中的事件

import React, {Component} from 'react'; export default class Parent extends Component { render() { return( <div> <Child onRef={this.onRef} /> <button onClick={this.click} >click</button> </div> ) } onRef = (ref) => { this.

Vue组件中的Data为什么是函数。

简单点说,组件是要复用的,在很多地方都会调用.   如果data不是函数,而是属性,就又可能会发生多个地方的相同组件操作同一个Data属性,导致数据混乱. 而如果是函数,因为组件data函数的返回值是{属性:属性名},所以每次返回都会在堆空间创建一个新的空间,所以各个组件不会发生操作同一个数据,防止数据混乱. 原文地址:https://www.cnblogs.com/itrencaoqi/p/12175420.html

vue组件之间的通信以及如何在父组件中调用子组件的方法和属性

在Vue中组件实例之间的作用域是孤立的,以为不能直接在子组件上引用父组件的数据,同时父组件也不能直接使用子组件的数据 一.父组件利用props往子组件传输数据 父组件: <div> <child v-bind:my-message="parentMsg"></child>//注意传递参数时要用-代替驼峰命名,HTML不区分大小写 </div> 子组件: Vue.component('child', { // camelCase in Ja