问题描述
我获得的方法是使用C#非托管指针的方法来实现的,因为VB.NET中不允许直接操作非托管指针或内存,所以必须使用Marshal来实现。=========================================C++的类库原型:DLLEXPORT_APIint__stdcallSetOsdDisplayModeEx(HANDLEhChannelHandle,intcolor,BOOLTranslucent,intparam,intnLineCount,USHORT**Format);
C++中的调用方法片段:USHORTFormat3[8][44]={{2,2,'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4',''},{2,25,'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4',''},{2,50,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9',''},{2,75,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9',''},{2,100,_OSD_YEAR4,'-',_OSD_MONTH2,'-',_OSD_DAY,'-',_OSD_HOUR24,':',_OSD_MINUTE,':',_OSD_SECOND,'-',_OSD_MONTH3,'-',_OSD_WEEK3,'星','期',_OSD_CWEEK1,_OSD_HOUR12,''},{2,125,'h','a','n','g','z','h','o','u','','h','i','k','v','i','s','i','o','n',''},{2,150,'杭','州','海','康','威','视',''},{2,175,'D','S','-','4','0','x','x','H','C',''}};USHORT*Format4[8];for(i=0;i<8;i++){Format4[i]=Format3[i];}...for(i=0;i<GetTotalDSPs();i++){SetOsdDisplayMode(ChannelHandle[i],255,TRUE,1,Format1,Format2);//showosdwith8rows//SetOsdDisplayModeEx(ChannelHandle[i],255,TRUE,1,8,Format4);SetOsd(ChannelHandle[i],TRUE);}
转换成C#的封装类:[DllImport("DS40xxSDK.dll")]publicstaticexternunsafeintSetOsdDisplayModeEx(IntPtrhChannelHandle,intBrightness,boolTranslucent,intparam,intnLineCount,ushort*[]Format);
C#中的调用方法片段:...ushort[,]Format={{48,16,'A',''},{48,32,'B',''}};this.UsePoints(Format);...intUsePoints(ushort[,]Format){introw=Format.GetUpperBound(0)+1;intcol=Format.GetUpperBound(1)+1;unsafe{fixed(ushort*fp=Format){ushort*[]farr=newushort*[row];for(inti=0;i<row;i++){farr[i]=fp+i*col;}returnDS40xxSDK.HikVisionSDK.SetOsdDisplayModeEx(ChannelHandle,255,true,0,2,farr);}}}
欲转换成在VB.NET中的封装:<DllImport("DS40xxSDK.dll")>_PublicSharedFunctionSetOsdDisplayModeEx(ByValhChannelHandleAsIntPtr,ByValBrightnessAsInteger,ByValTranslucentAsBoolean,ByValparamAsInteger,ByValnLineCountAsInteger,ByValiFormatAsIntPtr)AsIntegerEndFunction
下面就是请教该如何转换成使用Marshal类来实现的方法了。有哪位大虾知道,帮一下忙!
解决方案
解决方案二:
自己写了一个,但还是遇到问题!程序出错:System.Runtime.InteropServices.COMException(0x80070006):句柄无效。(ExceptionfromHRESULT:0x80070006(E_HANDLE))封装:<DllImport("DS40xxSDK.dll")>_PublicSharedFunctionSetOsdDisplayModeEx(ByValhChannelHandleAsIntPtr,ByValBrightnessAsInteger,ByValTranslucentAsBoolean,ByValparamAsInteger,ByValnLineCountAsInteger,ByValiFormatAsIntPtr)AsIntegerEndFunction
调用:PublicFunctionSetOSD(ByValTagAsString,ByValRowCountAsInteger)AsBooleanDimFormat(,)AsUShort=NewUShort(,)_{_{16,16,Asc("F"),0},_{16,32,Asc("L"),0}_}DimrowAsInteger=Format.GetUpperBound(0)+1DimcolAsInteger=Format.GetUpperBound(1)+1DimptAsNewIntPtrDimptr(row-1)AsIntPtr'申请内存pt=Marshal.AllocHGlobal(Marshal.SizeOf(GetType(IntPtr))*row)ForiAsInteger=0Torow-1'ptr(i)=Marshal.AllocHGlobal(Marshal.SizeOf(GetType(UShort))*col)ptr(i)=Marshal.UnsafeAddrOfPinnedArrayElement(Format,i)NextMarshal.Copy(ptr,0,pt,row)Dimret1AsInteger=HKDSSDK.HikVisionSDK.SetOsdDisplayModeEx(ChannelHandle,255,False,&H10200,2,pt)Dimret2AsInteger=-1DimretvalAsBooleanIfret1=0Thenret2=HKDSSDK.HikVisionSDK.SetOsd(ChannelHandle,True)Ifret2=0Thenretval=TrueElseretval=FalseEndIfElseretval=FalseEndIfForiAsInteger=0Torow-1Marshal.FreeHGlobal(ptr(i))NextMarshal.FreeHGlobal(pt)ReturnretvalEndFunction
调用成功一半,数组一部分能够能够成功封送,随后出现System.Runtime.InteropServices.COMException(0x80070006):句柄无效。(ExceptionfromHRESULT:0x80070006(E_HANDLE))
解决方案三:
根据你上面的C#用机器翻译的:PublicClassHikVisionSDK<DllImport("DS40xxSDK.dll")>_PublicSharedFunctionSetOsdDisplayModeEx(hChannelHandleAsIntPtr,BrightnessAsInteger,TranslucentAsBoolean,paramAsInteger,nLineCountAsInteger,FormatAsPointer(OfUShort)())AsIntegerEndFunctionEndClassDimFormatAsUShort(,)={{48,16,"A"C,ControlChars.NullChar},{48,32,"B"C,ControlChars.NullChar}}Me.UsePoints(Format)PrivateFunctionUsePoints(FormatAsUShort(,))AsIntegerDimrowAsInteger=Format.GetUpperBound(0)+1DimcolAsInteger=Format.GetUpperBound(1)+1DimfarrAsPointer(OfUShort)()=NewPointer(OfUShort)(row-1){}ForiAsInteger=0Torow-1farr(i)=fp+i*colNextReturnDS40xxSDK.HikVisionSDK.SetOsdDisplayModeEx(ChannelHandle,255,True,0,2,farr)EndFunction
解决方案四:
引用2楼的回复:
根据你上面的C#用机器翻译的:VB.NETcodePublicClassHikVisionSDK<DllImport("DS40xxSDK.dll")>_PublicSharedFunctionSetOsdDisplayModeEx(hChannelHandleAsIntPtr,BrightnessAsInteger,Translu……
VB.NET中是不允许直接操作非托管的指针和内存,所以Pointer类的与指针操作相关的Box和UnBox方法都是无法使用的,所以不能像C#那样很自由的用Unsafe来操作指针和内存。
解决方案五:
似乎又有了些进展,终于知道为什么会产生:System.Runtime.InteropServices.COMException(0x80070006):句柄无效。(ExceptionfromHRESULT:0x80070006(E_HANDLE))的原因!跟踪了一下错误:atSystem.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32errorCode,IntPtrerrorInfo)atSystem.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32errorCode)atSystem.Runtime.InteropServices.Marshal.FreeHGlobal(IntPtrhglobal)其中FreeHGlobal(IntPtrhglobal)我初时考虑,是不是申请的内存被Free了,于是就注释了所有的内存资源释放语句。结果仍旧出现FreeHGlobal(IntPtrhglobal),百思不得其解。。。直到突然“垃圾回收”,是不是invoke自动回收调用了这个语句。。。结合问题出现的现象,调用似乎刚开始是正常的,后来抛出无效句柄的异常。。。看来是应该这么回事。。。但如何不让他自动回收,查了下资料。。。以下是我找到的一篇资料的片段:三、如何保证使用托管对象的平台调用成功?如果在调用平台invoke后的任何位置都未引用托管对象,则垃圾回收器可能将完成该托管对象。这将释放资源并使句柄无效,从而导致平台invoke调用失败。用HandleRef包装句柄可保证在平台invoke调用完成前,不对托管对象进行垃圾回收。例如下面:FileStreamfs=newFileStream("a.txt",FileMode.Open);StringBuilderbuffer=newStringBuilder(5);intread=0;ReadFile(fs.Handle,buffer,5,outread,0);//调用WinAPI中的ReadFile函数由于fs是托管对象,所以有可能在平台调用还未完成时候被垃圾回收站回收。将文件流的句柄用HandleRef包装后,就能避免被垃圾站回收:[DllImport("Kernel32.dll")]publicstaticexternboolReadFile(HandleRefhndRef,StringBuilderbuffer,intnumberOfBytesToRead,outintnumberOfBytesRead,refOverlappedflag);............FileStreamfs=newFileStream("HandleRef.txt",FileMode.Open);HandleRefhr=newHandleRef(fs,fs.Handle);StringBuilderbuffer=newStringBuilder(5);intread=0;//platforminvokewillholdreferencetoHandleRefuntilcallendsReadFile(hr,buffer,5,outread,0);
关键是要利用HandleRef来包装一个托管对象,该对象保存使用平台invoke(调用)传递给非托管代码的资源句柄。唉,又涨知识了,可是,怎么着手。。。当然封送的参数类型又要修改。。。<DllImport("DS40xxSDK.dll")>_PublicSharedFunctionSetOsdDisplayModeEx(ByValhChannelHandleAsIntPtr,ByValBrightnessAsInteger,ByValTranslucentAsBoolean,ByValparamAsInteger,ByValnLineCountAsInteger,ByRefiFormatAsHandleRef)AsIntegerEndFunction
但如何调用。。。还在摸索。。。又或者。。。弱弱的希望又一个大虾的出现。。。
解决方案六:
前阵子做过的给你参考下,主要是定义为指针,并分配一块内存,使用时根据指针,及长度,读出到数组中即可。祝你成功。DllImport("epcAuthorizationVb.dll",CharSet:=CharSet.Unicode)>_PublicFunctionVbepcAuthorizeEncode(ByVallpszPassword()AsByte,ByValpEncodeOutDataAsIntPtr,ByRefpOutDataLenAsUInt32)AsBooleanEndFunction'======调用例ConstOutDataBufferLenAsInteger=64'缓冲区大小,>=16且<=__BLOCK_DATA_SIZEDimpassword()AsByte=System.Text.Encoding.UTF8.GetBytes(TextBoxSend.Text)DimbufferEncodeOutDataAsIntPtr=Marshal.AllocCoTaskMem(OutDataBufferLen)'分配内存DimoutDataLenAsUInt32=OutDataBufferLen'传入缓冲区大小DimbSuccessAsBoolean=VbepcAuthorizeEncode(password,bufferEncodeOutData,outDataLen)IfbSuccessThenConsole.WriteLine("Success!outDataLenAfterCall:{0}",outDataLen)'outDataLen输出已加密字节长度DimarrEncodeOutData()AsByte=NewByte(outDataLen){}'储存输出的已加密字节i=0Whilei<outDataLenarrEncodeOutData(i)=Marshal.ReadByte(bufferEncodeOutData,i)Console.Write("{0}",arrEncodeOutData(i))System.Math.Max(System.Threading.Interlocked.Increment(i),i-1)EndWhileElseConsole.WriteLine("Fail!outDataLenAfterCall:{0}",outDataLen)EndIfMarshal.FreeCoTaskMem(bufferEncodeOutData)'释放内存'======
解决方案七:
用完后要记得,释放内存
解决方案八:
谢谢5楼的这位朋友,这个方法我已经知道了,我在前面已经尝试过类似的方法。目前的问题是,封送过去后,出现调用COM句柄无效的问题,可能是跟GC有关,正在头疼中。。。