1 经过:
遇到问题:SafeArray无法销毁.
通过测试,SafeArrayUnacess和SafeArrayDestroy的返回值,断定问题出现在SafeArrayDestroy。
上网搜索。网上有篇文章引用MSDN,说, SafeArray销毁的时候, 如果数组中的元素已经销毁了. 则会出现崩溃.
仔细的检查,我这边应该不存在这样的问题。
反复的增加, 删除控件xxx.odl, xxxidl.h文件中新增的接口类型中的字段.
反复的增加, 删除客户端xxx.tlh文件中新增的接口类型中的字段.
反复的编译, 测试.
发现, 如果xxxidl.h xxx.tlh文件中的字段数一致. 只要不destroy就可以执行.
但,不想把这个内存泄露就这样放过。
在多次试验后, 发现无论怎样, 读相关数据的时候都有问题.
于是怀疑是传入的数据结构和解析使用的数据结构不一致导致的.
果然通过, IRecordInfo.GetSize() sizeof(yyy)得到大小不一致. 仔细审查两者使用的结构, 发现IRecordInfo.GetSize() 比 sizeof(yyy) 小了8个字节.
心想, 怎么就少了8个字节了. 突然想起了结构体的字节对齐问题. 然后使用#program pack(4) 使二者一致了.
但又想,应该验证下控件中的结构体和IRecordInfo.GetSize()是否一致。结论也是不一致. 这才意识到, 不是字节对齐的问题. 应该是没有增加字段成功.
然后查看 tlb文件, 生成tlb文件中有新增的字段. 再次失去了线索.
虽然知道工程属性最后到会体现在预编译头命令和其他的Alt+F7的设置中。
但还是对工程属性起了怀疑. 于是自己新建了个简单的工程. 新建的工程是可以的.
然后查注册表, 注册表中只记录了简单的typeid 和 存储路径. 注册表中是不能存储类型信息的.
然后向同事求教, 同事使用OLEView查看了控件内容. 发现控件里确实是没有增加成功. 现在知道了tlb信息是存储在ocx文件中的.
那是什么导致没有增加成功呢? 如果是工程属性的问题, 为什么我第一次可以增加成功了呢. 应该是链接的问题.
最后, 发现在工程根目录下有一个tlb文件. 应该是最初某个编译版本留下的, 删除之, 重新编译. OK.
还发现编译的xxx.odl文件的时候, 没有改变xxxidl.h.
2 结论:
ocx包含了错误的tlb内容。导致,SafeArray创建的时候,使用的record类型与实际的record类型不一致。
最终导致了控件和客户端交互的时候对SafeArray指向内存的操作出现问题。由于实际的record比使用的record类型占的内存要大。
所以写的时候已经出现了内存越界的问题。
而SafeArrayDestroy的时候,由于结构不一致,推测释放结构中BSTR的时候出现了内存的错位读,导致了数据的异常。
3 收获:
odl文件(idl文件),MIDL只需要生成一个tlb文件就可以了.
VC6默认是不生成头文件 和 UUID文件的. 在odl文件的setting中,可以增加output header file 和uuid file来设置.
ocx文件,包含了tlb文件.
在odl文件中定义的类型(UDT),都有对应的TypeInfo. 这个typeInfo也是存储在tlb中的.
GetRecordInfoFromGUID 指定了类型库的ID 通过,类型库ID查注册表,可以找到ocx文件存储目录. 然后就可以获取tlb的信息了.
4 其它:
ActiveX 工程的编译:
/nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_AFXDLL" /D "_MBCS" /D "_USRDLL" /Fp"Debug/test.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c
Link选项
/nologo /subsystem:windows /dll /incremental:yes /pdb:"Debug/test.pdb" /debug /machine:I386 /def:"./test.def" /out:"Debug/test.ocx" /implib:"Debug/test.lib" /pdbtype:sept
可以看到并么有link tlb,不知道是怎么整合进去的。
5 补充(2008-2-29)
导致安全数组释放错误
1 record信息不正确(记录占据空间不一致,容易导致内存误用)
控件和调用者 约定不一致
使用了错误的record GUID
2 先释放了空间
遇到问题:SafeArray无法销毁.
通过测试,SafeArrayUnacess和SafeArrayDestroy的返回值,断定问题出现在SafeArrayDestroy。
上网搜索。网上有篇文章引用MSDN,说, SafeArray销毁的时候, 如果数组中的元素已经销毁了. 则会出现崩溃.
仔细的检查,我这边应该不存在这样的问题。
反复的增加, 删除控件xxx.odl, xxxidl.h文件中新增的接口类型中的字段.
反复的增加, 删除客户端xxx.tlh文件中新增的接口类型中的字段.
反复的编译, 测试.
发现, 如果xxxidl.h xxx.tlh文件中的字段数一致. 只要不destroy就可以执行.
但,不想把这个内存泄露就这样放过。
在多次试验后, 发现无论怎样, 读相关数据的时候都有问题.
于是怀疑是传入的数据结构和解析使用的数据结构不一致导致的.
果然通过, IRecordInfo.GetSize() sizeof(yyy)得到大小不一致. 仔细审查两者使用的结构, 发现IRecordInfo.GetSize() 比 sizeof(yyy) 小了8个字节.
心想, 怎么就少了8个字节了. 突然想起了结构体的字节对齐问题. 然后使用#program pack(4) 使二者一致了.
但又想,应该验证下控件中的结构体和IRecordInfo.GetSize()是否一致。结论也是不一致. 这才意识到, 不是字节对齐的问题. 应该是没有增加字段成功.
然后查看 tlb文件, 生成tlb文件中有新增的字段. 再次失去了线索.
虽然知道工程属性最后到会体现在预编译头命令和其他的Alt+F7的设置中。
但还是对工程属性起了怀疑. 于是自己新建了个简单的工程. 新建的工程是可以的.
然后查注册表, 注册表中只记录了简单的typeid 和 存储路径. 注册表中是不能存储类型信息的.
然后向同事求教, 同事使用OLEView查看了控件内容. 发现控件里确实是没有增加成功. 现在知道了tlb信息是存储在ocx文件中的.
那是什么导致没有增加成功呢? 如果是工程属性的问题, 为什么我第一次可以增加成功了呢. 应该是链接的问题.
最后, 发现在工程根目录下有一个tlb文件. 应该是最初某个编译版本留下的, 删除之, 重新编译. OK.
还发现编译的xxx.odl文件的时候, 没有改变xxxidl.h.
2 结论:
ocx包含了错误的tlb内容。导致,SafeArray创建的时候,使用的record类型与实际的record类型不一致。
最终导致了控件和客户端交互的时候对SafeArray指向内存的操作出现问题。由于实际的record比使用的record类型占的内存要大。
所以写的时候已经出现了内存越界的问题。
而SafeArrayDestroy的时候,由于结构不一致,推测释放结构中BSTR的时候出现了内存的错位读,导致了数据的异常。
3 收获:
odl文件(idl文件),MIDL只需要生成一个tlb文件就可以了.
VC6默认是不生成头文件 和 UUID文件的. 在odl文件的setting中,可以增加output header file 和uuid file来设置.
ocx文件,包含了tlb文件.
在odl文件中定义的类型(UDT),都有对应的TypeInfo. 这个typeInfo也是存储在tlb中的.
GetRecordInfoFromGUID 指定了类型库的ID 通过,类型库ID查注册表,可以找到ocx文件存储目录. 然后就可以获取tlb的信息了.
4 其它:
ActiveX 工程的编译:
/nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_AFXDLL" /D "_MBCS" /D "_USRDLL" /Fp"Debug/test.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c
Link选项
/nologo /subsystem:windows /dll /incremental:yes /pdb:"Debug/test.pdb" /debug /machine:I386 /def:"./test.def" /out:"Debug/test.ocx" /implib:"Debug/test.lib" /pdbtype:sept
可以看到并么有link tlb,不知道是怎么整合进去的。
5 补充(2008-2-29)
导致安全数组释放错误
1 record信息不正确(记录占据空间不一致,容易导致内存误用)
控件和调用者 约定不一致
使用了错误的record GUID
2 先释放了空间