描述:
大概思路是这样的:
我们的用户鉴权功能是由C#写的一个Web Service提供,为了方便VC的客户端使用,打算将对WebService的引用封装在一个类库中。
但是偶用vc写的一个自测程序去调用封装后的类库,在退出时就会出现“内存不能为读”的错误提示;
偶试了两种情况:
1、如果直接在自测程序中添加Web引用,然后直接调用自动生成的WebService的ATL代理类,退出时就不会出现这样的错误;
2、在类库中调用这个ATL代理类时,如果输出参数传递一个NULL,退出时也不会出现这个错误。
所以,初步确定应该是输出参数的释放有问题,但偶试了好久,也还是有“内存不能为读”的错误提示,不知道错在哪里了?大家帮忙看看,谢谢
封装的类库中的部分代码如下:
其中,m_pWS是atl代理类指针,在类库构造时创建,析构时释放;
// ***************************************************************
// 函数名称:CheckUser
// 功能描述:验证用户登录
// 访问的表:无
// 修改的表:无
// 输入参数:const CString& sUserid - 用户名称
// const CString& sUserpwd - 用户密码
// BYTE nSysfuncid - 是否能登录该子系统的系统功能编号
// 输出参数:bool* bRtn - 执行是否成功
// 返回值:CString - 输出信息
// 其它说明:无
// ***************************************************************
CString CAuthority::CheckUser(const CString& sUserid, const CString& sUserpwd , BYTE nSysfuncid, bool* bRtn)
{
HRESULT hr = S_OK;
CComBSTR bstrUserid = sUserid;
CComBSTR bstrUserpwd = sUserpwd;
CComBSTR bstrMsg = NULL;//static_cast< LPCOLESTR>(NULL);
bstrMsg.Empty();
hr = m_pWS->CheckUser(bstrUserid, bstrUserpwd, nSysfuncid, bRtn, &bstrMsg);
// 获取返回值
CString sMsg(bstrMsg == NULL ? L"" : bstrMsg);
return sMsg;
}
解决方案1:
我不确定你通过new来生成代理对象是合理的。不过根据他在delete(就是说被析构)时调用Release()方法实现,说明它是一个COM智能指针类型。对COM中的东西都不该直接调用delete,应该通过调用Release()让他自己析构,智能指针也可以通过附NULL值间接调用Release()。
我给你分析一下违规内存访问的发生原因:
m_pWS是个智能指针(可参考CComPtr地实现,很可能他根本就是个CComPtr)。它内部维护着真正的代理对象的指针,就是你上面Release()方法里面的p呀。因为智能指针类型有个operator T()实现,所以你可以拿他当它内部的p用。你调用delete m_pWS;其实是调用的delete p,如此真正的对象就析构了(永远不要对智能指针调用delete),但智能指针m_pWS不知道。然后你有调用了m_pWS = NULL;你可以参考CComPtr的operator = (null)方法,他会去调用Release(),就是你最后列出来的void Release() throw(){}。可见,这时候pTemp是无效的内存,所以报了错误。
前面你讲的字符串问题是偶然的。就算你传空的输出参数结果不出错,只能说明微软现在的系统做得比较安全了。你可以尝试拿你的代码去Win98上跑,即使传空的输出参数怕也会报错。XP估计也会报错。
无论如何,你delete m_pWS;是绝对错误的操作。
确定你的m_pWS在delete之前是有效的吗?
还有你说到的输出参数是在调用程序还是DLL中分配、释放?
看看是不是析构函数里面调用了虚函数
解决方案4: 一般来说在退出时报错,是由内存泄漏引起的
你的类库在处理,宽字符是否有问题,缓冲是否够用。
MultiByteToWideChar 不自动加大利\0 这个要注意