随笔-23  评论-9  文章-0  trackbacks-0
  2008年5月31日

今天调查内存泄漏,发现一块申请的堆未释放,于是加了一段释放代码
if(!pbuffer)
{
    TaskMemFree(pbuffer);
}

运行程序,发现仍然泄漏,继续检查其余代码,没发现问题,正在郁闷中,发现单步调试不能走到
释放代码内部,一看条件判断,晕!写了个无效代码,pbuffer为空时才会执行释放操作,改为
if(pbuffer)
{
    TaskMemFree(pbuffer);
}

问题解决!
看来写代码时显示判断还是保险些啊,如果当初写成
if(pbuffer != NULL)
{
    TaskMemFree(pbuffer);
}
就不会出现这样的问题了。

另外发现,堆分配时要做好配对,TaskMemAlloc要与TaskMemFree一起用,
New和Delete一起用,尤其在调用方法负责分配空间,调用者负责释放时,要
做好约定,有次用TaskMemAlloc分配的空间,但用Delete释放掉了,造成内
存泄漏,奇怪的是当时用Release版本,编译器不能检查出来,但用Debug版
本就提示出错了。

posted @ 2008-05-31 18:18 XuQ 阅读(57) 评论(0) 编辑
  2008年5月6日

1      使用SafeArray

SafeArrayVB中的数组存储方式。通过SafeArray,可以在VC++VB间相互调用。SafeArray也是Automation中的标准数组存储方式。

1.1     SafeArray处理函数

COM提供了一套API用于处理SafeArray。为了保证程序和SafeArray结构无关[1],程序中建立、读取、更改和释放SafeArray都应该通过这些API进行,而不应该直接读写SafeArray结构。

下面介绍常用的SafeArray处理函数。

1.1.1            建立SafeArray

SAFEARRAY* SafeArrayCreate(

 VARTYPE vt,

 unsigned int cDims,

 SAFEARRRAYBOUND * rgsabound

);

 

SAFEARRAY SafeArrayCreateEx(

 VARTYPE vt,

 unsigned int cDims,

 SAFEARRRAYBOUND * rgsabound

 PVOID pvExtra

);

 

SAFEARRAY* SafeArrayCreateVector(

 VARTYPE vt,

 long lLbound,

 unsigned int cElements

);

 

SAFEARRAY* SafeArrayCreateVectorEx(

 VARTYPE vt,

 long lLbound,

 unsigned int cElements,

 LPVOID pvExtra

);

 

SafeArrayCreate于建立多维普通数组。SafeArrayCreateEx用于建立多维自定义类型或接口指针数组。SafeArrayCreateVector用于建立一维普通数组。SafeArrayCreateVectorEx用于建立一维自定义类型或接口指针数组。

1.1.2            释放数组

 

HRESULT SafeArrayDestroy(

 SAFEARRAY * psa 

);

 

SafeArrayDestroy用于释放创建的SafeArray数组。

1.1.3            访问数据

 

HRESULT SafeArrayAccessData(

 SAFEARRAY * psa,

 void HUGEP ** ppvData

);

 

HRESULT SafeArrayUnaccessData(

 SAFEARRAY * psa

);

 

SafeArrayAccessData函数返回数组的指针。而SafeArrayUnaccessData释放通过SafeArrayAccessData所取得的指针。

1.2     SafeArray相关处理

1.2.1            创建SafeArray数组

创建SafeArray可以使用COM提供的四个创建函数之一。所有的创建函数都返回一个SafeArray指针。通过这个指针可以读写SafeArray中的数据。SafeArray使用完后必须释放。

1. SafeArrayCreateVector

 

SAFEARRAY* SafeArrayCreateVector(

 VARTYPE vt,            

 long lLbound,          

 unsigned int cElements 

);

 

这个函数用来创建简单类型的一维数组。这个函数有三个参数:vt是数组类型、lLbound是数组下界值(最小下标)和数组长度。vt的取值如下表:

vt值

类型

VT_UI1

无符号1字节整数(BYTE)数组

VT_UI2

无符号2字节整数(WORD)数组

VT_UI4

无符号4字节整数(DWORD)数组

VT_UINT

无符号整数(UINT)数组

VT_INT

有符号整数(INT)数组

VT_I1

有符号1字节整数数组

VT_I2

有符号2字节整数数组

VT_I4

有符号4字节整数数组

VT_R4

IEEE 4字节浮点数(float)数组

VT_R8

IEEE 8字节浮点数(double)数组

VT_CY

8字节定点数货币值数组

VT_BSTR

VB字符串数组

VT_DECIMAL

12字节定点数(大数字)数组

VT_ERROR

标准错误编号数组

VT_BOOL

布尔值数组

VT_DATE

日期型数组

VT_VARIANT

VB Variant类型数组

lLbound是数组的最小下标,可以是取负数。cElements是数组的长度。数组的最大下标的值是最小下标加上数组长度减一。

SafeArrayCreateVector函数返回SafeArray结构的指针。

2. SafeArrayCreateVectorEx

SAFEARRAY* SafeArrayCreateVectorEx(

 VARTYPE vt,            

 long lLbound,          

 unsigned int cElements, 

 LPVOID pvExtra 

);

 

这个函数用于创建自定义类型或COM对象的SafeArray数组。和SafeArrayCreateVector类似,SafeArrayCreateVector也有类型、下界和长度的三个参数。SafeArrayCreateVectorEx还增加了一个参数pvExtra

pvExtra的含义和vt的取值有关。当vt的取值在上表中的时候,pvExtra的取值没有作用。当vt取值VT_RECORD时,SafeArrayCreateVectorEx返回一个自定义类型(结构structure或联合union)的数组。这时,pvExtra必须是一个指向IRecordInfo的指针。

vt取值是VT_UNKNOWNVT_DISPATCH时。pvExtra是一个指向IID(接口GUID)的指针。在目前的COM规范中,pvExtra只能是IID_IUnknownIID_IDispatch。并且必须和vt的取值一致。

a.   创建自定义类型数组

vtVT_RECORD时。pvExtra必须是一个IRecordInfo指针。绝大多数情况下,我们从TLB中取得自定义类型的IRecordInfo指针。以下是取得IRecordInfo的代码:

 

IRecordInfo * pRecordInfo;

hr = GetRecordInfoFromGuids(

LibID,

MajorVer,

MinorVer,

LOCALE_USER_DEFAULT,

    TypeGUID,

&pRecordInfo);

 

上述代码中,LibID是所TLBGUIDMajorVerMinorVer分别是TLB的主、次版本号,TypeGUID是自定义结构的GUID

函数返回的是IRecordInfo接口的指针。

b.   创建COM对象数组

当需要创建COM数组时,可以使用IUnknown指针,也可以用IDispatch指针。如果需要使用其它指针类型,应该使用QueryInterface方法取得,而不能直接在数组中保存。因为SafeArray数组的序列化程序只能处理IUnknownIDispatch两种指针类型,如果在数组中放其它接口类型的指针,可能在跨套间使用中会出现问题。

1.2.2            读取和写入SafeArray数组。

读写SafeArray数组时。应该使用COM提供的标准APICOM提供了大量函数用于SafeArray数组的操作,本文中仅使用其中的两个函数,SafeArrayAccessDataSafeArrayUnaccessData,和一些辅助用的函数。实际上是用这两个函数就可以进行所有的数组操作了。其它的函数用于对单个元素的操作,由于使用不多,而且效率也不高,所以本文中不进行说明。

1. SafeArrayAccessData

这个函数用于获取SafeArray的数据指针,并锁定SafeArray数组的数据。在取得了数据指针之后,就可以直接访问SafeArray数组中的数据了。

如果数组类型是Type,那么所取得的数据指针实际上就是Type类型的数组的地址。在多维数组的情况下,必须把多个维度的下标转换成一维下标进行访问。

2. SafeArrayUnaccessData

这个函数的作用是对SafeArray数据解锁,解锁后,就不应该继续对数据指针进行读写访问。如果要访问,必须重新获取并锁定数据。

3. 确定数组结构

在访问数组之前,必须知道数组中数据的类型,、维数以及每个维度的下界和长度。COM提供了取得这些数组参数的函数。

取得类型,返回“VT_”开头的类型枚举值:

 

HRESULT SafeArrayGetVartype (

    SAFEARRAY * pSA,

    VARTYPE * pVarType);

 

取得维数,返回数组的维数:

 

UINT SafeArrayGetDim (

    SAFEARRAY * pSA);

 

取得每个维度的属性,返回指定维数(nDim)的上界和下界(nDim1开始):

 

HRESULT SafeArrayGetLBound (

    SAFEARRAY * pSA,

    UINT nDim,

    long * pLBound);

 

HRESULT SafeArrayGetUBound (

    SAFEARRAY * pSA,

    UINT nDim,

    long * pUBound);

 

取得自定义类型接口,对于自定义结构数组,返回自定义结构类型数据的指针:

 

HRESULT SafeArrayGetRecordInfo (

    SAFEARRAY * pSA,

    IRecordInfo ** ppRecordInfo);

 

4. 访问普通一维数组

SafeArrayAccessData返回的指针实际上就是C语言中的一维数组地址。在VC++中可以像访问普通数组一样读写这个数组。

需要注意的是,在C语言中,所有的数组下标都是从0开始的。而在SafeArray中,数组下标可以从任何数字开始。所以在访问前必须进行转换。转换方法就是从SafeArray的下标中减去数组的下界,就可以得到C语言中数组的下标了。

如下:

 

Type * pData;

long LBound;

SafeArrayAccessData(pSA, (void HUGEP **) &pData);

SafeArrayGetLBound(pSA, 1, &LBound);

Type Item = pData[n – LBound];

 

5. 访问多维数组

访问多维数组和访问一维数组类似,只是要把多维下标转换成一维下标。把多维下标转换成一维下标的方法和在数组指针中介绍的是相似的。

设:有n个维度,每个维度的长度(上界减去下界加一)分别是L1L2Ln。要转换的下标是X1X2Xn。可以根据下述公式转换成一维数组的下标。

X1+X2*L1+X3*(L1*L2)+X4*(L1*L2*L3)+…+Xn*(L1*L2*…*L(n-1))

6. 访问自定义结构数组

访问自定义结构数组的时候,可以使用#iimport自动生成或者IDL编译产生的类型定义。如果没有办法取得自定义结构的声明,可以使用IRecordInfo接口中的方法间接访问自定义结构。

首先需要取得自定义结构的长度,这可以通过IRecordInfo::GetSize方法取得。

访问自定义结构中的字段内容,通过IRecordInfo::GetFieldIRecordInfo::PutField方法实现。

通过IRecordInfo中的其它方法还可以取得每个字段的属性内容。大家可以参考相关文档。

1.2.3            释放SafeArray数组

释放SafeArray数组应该通过COM的支持函数:

 

HRESULT SafeArrayDestroy(SAFEARRAY * pSA);

 

1.3     使用SafeArray的IDL定义

每个接口都要通过IDL生成代理和占位程序代码。为了使代理和占位程序能够正确地对参数进行序列化,必须正确的书写IDL定义。

MIDL工具直接支持SafeArray类型数据的传递。但是,在传递SafeArray数据的时候,必须通过SAFEARRAY的指针进行。困难在于,VC++ 6.0的添加方法和添加属性的工具不能够正确的处理SafeArray数组的情况。

 

IDL中,数组必须指定类型,如下:

 

[id(10)] HRESULT Foo([in] SAFEARRAY(LONG) pParam);

 

在实现的函数声明中,要使用相应的指针类型:

 

HRESULT Foo(SAFEARRAY * pParam);

 

输出和输入输出类型的数组参数,在IDL中必须使用指针参数,而在函数声明中则是双重指针。

 

[id(11)] HRESULT Foo2([out] SAFEARRAY(LONG) * ppParam);

 

函数声明如下:

 

HRESULT Foo2(SAFEARRAY ** ppParam);

 

1.4     VARIANT和SafeArray

VB的接口中,经常通过VARIANT传递数组参数。这里简述一下使用VARIANT参数传递数组中需要注意的地方。

1.4.1            输入数组

对于输入数组,可以使用VARIANT指针,也可以使用VARIANT类型参数。在这两种情况下,VARIANT中的类型是不同的。

当使用VARIANT指针时,输入的VARIANT参数的类型(vt参数的值)VT_ARRAY | VT_BYREF | VT_xxx。此时,使用VARIANT参数的pparray字段取得SafeArray指针。

如果参数是VARIANT,输入的VARIANT参数的类型(vt参数的值)是VT_ARRAY | VT_xxx。使用VARIANT参数的parray字段取得SafeArray指针。

必须注意这两种情况下,VARIANT的类型不同,所以代码也会有区别。

1.4.2            输出数组

输出和输入输出数组,必须使用VARIANT指针,这时,VARIANT类型是VT_ARRYA | VT_BYREF | VT_xxx

1.5     SafeArray内存管理

使用COM专用的创建和销毁API函数处理SafeArray

对于输入型的SafeArray,调用方负责创建和销毁SafeArray;对于输出型的SafeArray,由被调用方创建,调用方销毁;输入输出型SafeArray,调用方创建,被调用方可以销毁并重新创建,最终由调用方销毁。

 


[1]这样就可以在微软修改SafeArray结构时,保证程序兼容性。

转自:http://dev.csdn.net/article/19/19696.shtm

posted @ 2008-05-06 09:59 XuQ 阅读(1924) 评论(0) 编辑
  2008年5月4日
代码如下:

LONGLONG llValue;
BYTE* pbValue;

HRESULT hr = GetValue("Value", &pbValue);
if (SUCCESSED(hr))
{
    llValue = (LONGLONG)(*pbValue);
}

执行之后发现获得的llValue值比理论值小很多,经过分析发现犯了个弱智的错误:
———对指针pbValue先取值后转换类型
pbValue是BYTE型指针,取值后将pbValue指向的内存中一字节数据取出,然后类型转换时实际是将该一字节数据通过补0填充成LONGLONG类型(即获得的llValue中只有一字节是内存中的数据,其余都是填充的0)。

正确的赋值方式应该是:
llValue = *(LONGLONG*)pbValue;
即先类型转换,再赋值。只有这样才能将LONGLONG类型对应的内存中的数据完整取出来。

posted @ 2008-05-04 22:00 XuQ 阅读(290) 评论(0) 编辑
  2007年9月29日
摘要: /*********************************************************查找进程操作系统最终调用native api ZwQuerySystemInformation,因此拦截此函数可以达到隐藏进程的目的。*********************************************************//****************...阅读全文
posted @ 2007-09-29 19:54 XuQ 阅读(416) 评论(0) 编辑
摘要: /*********************************************************查找目录/文件操作系统最终调用native api ZwQueryDirectoryFile,因此拦截此函数可以达到隐藏文件/目录的目的。*********************************************************//**************...阅读全文
posted @ 2007-09-29 19:53 XuQ 阅读(187) 评论(0) 编辑
摘要: Windows下网络数据报的监听和拦截技术是一个比较古老的话题,应用也很广泛,例如防火墙等等。这篇小文只是对该技术的一个总结,没有新技术,高手免看:) 要监听和拦截Windows下的数据报,基本可以在两个层次进行,一个是用户态(user-mode),一个是核心态(kernel-mode)。 在用户态下,从高到低大概有四种方法。 1、原是套结字(Raw Socket)。Winsock2以后提供了原始...阅读全文
posted @ 2007-09-29 19:50 XuQ 阅读(711) 评论(0) 编辑
摘要: 由于三键是winlogon在启动时注册的,在原始输入线程(raw input thread)中处理的,所以很难屏蔽但是在2k下屏蔽三键的方法还是有不少,常见的是1、替换GINA2、进程注入到winlogon这两种方法在网上已经很普遍了,源代码也可以找到,缺点是容易闪屏,而且处理比较麻烦理论上还有不少屏蔽方法,在系统消息的各个处理界面将其屏蔽掉即可我这里介绍的是在kernel mode下处理的方法在...阅读全文
posted @ 2007-09-29 19:48 XuQ 阅读(771) 评论(0) 编辑
  2007年9月27日
摘要: 在Microsoft Windows 98中,键盘和鼠标是两个标准的使用者输入来源,在一些连贯操作中常产生互补作用。当然,鼠标在今天的应用程序中比十年前使用得更为广泛。甚至在一些应用程序中,我们更习惯于使用鼠标,例如在游戏、画图程序、音乐程序以及Web浏览器等程序中就是这样。然而,我们可以不使用鼠标,但绝对不能从一般的PC中把键盘拆掉。相对于个人计算机的其它组件,键盘有非常久远的历史,它起源于18...阅读全文
posted @ 2007-09-27 13:17 XuQ 阅读(1225) 评论(0) 编辑
  2007年9月25日
摘要: 获取当前程序文件的路径也算是一个比较常见的需求,比如我有一个文件conn.xml位于.exe的同一目录,在运行时需要从中读取一些设置信息。      进程对象在.NET中表现为System.Diagnostics.Process类,通过调用Process.GetCurrentProcess().MainModule.FileName可获得当前执行的exe的文件名。但是这个方法得到的仅仅是文件名,如...阅读全文
posted @ 2007-09-25 13:55 XuQ 阅读(168) 评论(0) 编辑
摘要: 一、Remoting基础 什么是Remoting,简而言之,我们可以将其看作是一种分布式处理方式。从微软的产品角度来看,可以说Remoting就是DCOM的一种升级,它改善了很多功能,并极好的融合到.Net平台下。Microsoft®.NETRemoting提供了一种允许对象通过应用程序域与另一对象进行交互的框架。这也正是我们使用Remoting的原因。为什么呢?在Windows操作系统...阅读全文
posted @ 2007-09-25 13:51 XuQ 阅读(45) 评论(0) 编辑
昵称:XuQ
园龄:4年5个月
粉丝:0
关注:1
<2012年2月>
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

搜索

 
 

常用链接

随笔档案

最新评论

阅读排行榜

评论排行榜

推荐排行榜