class CNewFree :
public INewFree,
public CComObjectRoot,
public CComCoClass<CNewFree,&CLSID_NewFree>
{
public:
CNewFree() {}
BEGIN_COM_MAP(CNewFree)
COM_INTERFACE_ENTRY(INewFree)
END_COM_MAP()
DECLARE_NOT_AGGREGATABLE(CNewFree) DECLARE_REGISTRY_RESOURCEID(IDR_NewFree)// INewFree
public:
STDMETHOD(GetMem)(/*[in]*/BYTE** pbMem);
};
-------------------------------
#include <atlapp.h>
#include <atlmisc.h>STDMETHODIMP CNewFree::GetMem(BYTE **pbMem)
{
CString *pstr = new CString("1111111111111");
*pbMem = (BYTE*)pstr; return S_OK;
}
===============================
测试端代码
LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
INewFree *pINewFree = NULL;
CoCreateInstance(__uuidof(NewFree), NULL, CLSCTX_INPROC_SERVER,
__uuidof(INewFree), (void**)&pINewFree);
BYTE* pbData = NULL;
pINewFree->GetMem(&pbData);
CString * pstr = NULL;
pstr = (CString*)pbData;
pstr->Format("sss");
delete pstr; // 一运行这里就出错!!! why???
return 0;
}
public INewFree,
public CComObjectRoot,
public CComCoClass<CNewFree,&CLSID_NewFree>
{
public:
CNewFree() {}
BEGIN_COM_MAP(CNewFree)
COM_INTERFACE_ENTRY(INewFree)
END_COM_MAP()
DECLARE_NOT_AGGREGATABLE(CNewFree) DECLARE_REGISTRY_RESOURCEID(IDR_NewFree)// INewFree
public:
STDMETHOD(GetMem)(/*[in]*/BYTE** pbMem);
};
-------------------------------
#include <atlapp.h>
#include <atlmisc.h>STDMETHODIMP CNewFree::GetMem(BYTE **pbMem)
{
CString *pstr = new CString("1111111111111");
*pbMem = (BYTE*)pstr; return S_OK;
}
===============================
测试端代码
LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
INewFree *pINewFree = NULL;
CoCreateInstance(__uuidof(NewFree), NULL, CLSCTX_INPROC_SERVER,
__uuidof(INewFree), (void**)&pINewFree);
BYTE* pbData = NULL;
pINewFree->GetMem(&pbData);
CString * pstr = NULL;
pstr = (CString*)pbData;
pstr->Format("sss");
delete pstr; // 一运行这里就出错!!! why???
return 0;
}
不要用delete
引用代理空间的实例一定要基于IUnknown的,释放时用Release。
想象一下跨进程的引用,从服务进程中返回一个对象的指针,该指针客户进程中指向的地址简直都不知到是什么东西,使用并delete真的无法想象回造成什么后果!
析够的出错CLSCTX_INPROC_SERVER && 执行的时候同步的, 所以不会有什么问题???http://blog.csdn.net/newkey007/archive/2004/07/09/38139.aspx
按你所说我做了一个实例,测试没问题,用的是ATL+MFC,但我想说明几点问题:
1。跨越COM接口直接引用服务进程内的对象指针,是非常危险的做法,对与CLSCTX_INPROC_SERVER,由于客户进程和服务进程同属于一进程,在同一进程内引用和释放对象也许不至于回引发错误,但对于CLSCTX_LOCAL_SERVER,CLSCTX_REMOTE_SERVER情况就截然不同了,前者是本地不同进程的服务,后者是远程进程的服务,pstr在服务进程内被创建,然后跨越COM接口,客户端获得的对象指针,在客户进程中的指向是毫无意义的,与之交互并释放,那是不可想象的。
2。按你的说明,在服务进程中创建的pstr应是WTL的CString对象,但我还不知道在客户进程中的pstr是否是MFC的CString指针,如果是MFC的CString指针,那么两者是不可以强制转换的,delete 必然会出错!
只所以要在接口参数中用类指针
完全是为了简化开发人员的负担,以及体现面向对象的思想
如果一个稍大一点的软件,全部是用COM来构架的,单纯的用 LPTSTR LPTSTR* CoTaskMemAlloc 的结构内存块这样是很麻烦的,当然我的前题是都在VC中用,并且是进程内组件,同步调用
你能告诉我出错的断言是什么吗?
void CtestClientDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
::CoInitialize(NULL);
CComPtr<IXing> pXing = NULL;
HRESULT hr = pXing.CoCreateInstance(__uuidof(CXing));
BYTE* pData = NULL;
pXing->GetStr(&pData);
CString *str = (CString*)pData; //mfc的CString
if(str)
{
delete str;
}
::CoUninitialize();}
没有出现问题!
接口实现.cpp中:
STDMETHODIMP CXing::GetStr(BYTE** ppdata)
{
// TODO: 在此添加实现代码
CString *pstr = new CString("1111111111111"); //atl的CString
*ppdata = (BYTE*)pstr; return S_OK;
}
你能告诉我出错的断言是什么吗?
------------------------------------
因为在 跨 dll的时候, 如果析构函数不是 虚拟的, 不是在 server端的 heap中释放你可以查一下 虚拟析构函数 的相关知识,就明白了
The DLL uses the stack of the calling thread and the virtual address space of the calling process. The DLL allocates memory from the virtual address space of the calling process. CLSCTX_INPROC_SERVER
The code that creates and manages objects of this class runs in the same process as the caller of the function specifying the class context.
在CLSCTX_INPROC_SERVER中Server端的Heap就是你Client端的Heap!!!
不要管他是不是虚拟析构函数,就从内存分配和释放的角度来考虑吧,是又何妨,不是又何妨呢!!!
2:可能数组越界
后来用导出一个release函数的方法来解决
release函数内部调用delete回收内存
还是在组件外面new和delete,把指针传进去COPY一份数据为好。
进程内组件 同步
--------------------
不要用COM,就被COM固定死了有些时候,为了简化操作,可以直接写个接口函数,返回个 类指针, 这个类指针 没那些 idl 参数的约束
为什么IUnknown接口有个Delete,而不是直接用delete ??因为不能直接delete !
记住CString的特殊性,修改CString的值实际上也产生了内存的申请释放。对于MFC而言,如果模块都是Debug或者都是Release版,就没有这个问题,
因为对CString的内存管理都发生在MFC42D.dll或者Mfc42.dll中,实际
上没有发生跨模块申请释放内存。如果一个模块为Debug而一个为Release,必死无疑!!!!
因为一个发生在MFC42D.dll而一个发生在Release中,就发生跨模块申请释放内存。
所以不管你的组件是Debug或Release版本,只要你的测试程序是Debug版本,程序运行就会出错!反之,只要你的测试程序是Release版本,就不会存在检查Debug heap的问题,肯定可以通过。
我的试验和资料表明:
问题的根源不是跨模块申请内存就怎么样,而是因为Debug heap是个局部堆,它附属于进程堆,局部堆只能管自己范围内的内存,每个程序在Debug状态都由VC分派一个局部堆(Debug heap)使用,这个局部堆虽然附属于进程,但管不了局部堆之外的事情。
本例中,测试程序在Debug状态,它要delete组件分配的内存,而组件中new出来的内存不在测试程序的Debug heap中,肯定会报错
在debug状态下delete一个指针,VC会调用_CrtIsValidHeapPointer来检查指针的有效性,异常也是在这里产生。
看看这个函数的说明就明白了:_CrtIsValidHeapPointer
COM 部分
------------------------------------------------------------
-------------------------------------------------------------
// NewFree.h: Definition of the CNewFree class
//
//////////////////////////////////////////////////////////////////////#if !defined(AFX_NEWFREE_H__DF9612B0_7619_44AE_AB91_B9958D51D358__INCLUDED_)
#define AFX_NEWFREE_H__DF9612B0_7619_44AE_AB91_B9958D51D358__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000#include "resource.h" // main symbols
class CTestClass
{
public:
CTestClass()
{
} virtual ~CTestClass()
{
}
int m_iTest[1000];
};
/////////////////////////////////////////////////////////////////////////////
// CNewFreeclass CNewFree :
public INewFree,
public CComObjectRoot,
public CComCoClass<CNewFree,&CLSID_NewFree>
{
public:
CNewFree() {}
BEGIN_COM_MAP(CNewFree)
COM_INTERFACE_ENTRY(INewFree)
END_COM_MAP()
DECLARE_NOT_AGGREGATABLE(CNewFree) DECLARE_REGISTRY_RESOURCEID(IDR_NewFree)// INewFree
public:
STDMETHOD(GetMem)(/*[out]*/BYTE** pbMem);
};#endif // !defined(AFX_NEWFREE_H__DF9612B0_7619_44AE_AB91_B9958D51D358__INCLUDED_)---------------------------------------------------------------------------------
// NewFree.cpp : Implementation of CHeapTestApp and DLL registration.#include "stdafx.h"
#include "HeapTest.h"
#include "NewFree.h"/////////////////////////////////////////////////////////////////////////////
//
STDMETHODIMP CNewFree::GetMem(BYTE **pbMem)
{
// TODO: Add your implementation code here
*pbMem = (BYTE*)new CTestClass();
return S_OK;
}
--------------------------------------------------------------------------------------------------------------------------------------------
Client 部分
------------------------------------------------------------
-------------------------------------------------------------
// maindlg.h : interface of the CMainDlg class
//
/////////////////////////////////////////////////////////////////////////////#if !defined(AFX_MAINDLG_H__93023046_B71B_4954_BFF1_EC4D0D1B4FC1__INCLUDED_)
#define AFX_MAINDLG_H__93023046_B71B_4954_BFF1_EC4D0D1B4FC1__INCLUDED_#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000class CTestClass
{
public:
CTestClass()
{
}
virtual ~CTestClass()
{
} int m_iTest[10000];
};#include "..\HeapTest.h"class CMainDlg : public CDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,
public CMessageFilter, public CIdleHandler
{
public:
enum { IDD = IDD_MAINDLG }; virtual BOOL PreTranslateMessage(MSG* pMsg)
{
return IsDialogMessage(pMsg);
} virtual BOOL OnIdle()
{
return FALSE;
} BEGIN_UPDATE_UI_MAP(CMainDlg)
END_UPDATE_UI_MAP() BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
COMMAND_ID_HANDLER(IDOK, OnOK)
COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
END_MSG_MAP()// Handler prototypes (uncomment arguments if needed):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
// center the dialog on the screen
CenterWindow(); // set icons
HICON hIcon = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME),
IMAGE_ICON, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
SetIcon(hIcon, TRUE);
HICON hIconSmall = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME),
IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
SetIcon(hIconSmall, FALSE); // register object for message filtering and idle updates
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->AddMessageFilter(this);
pLoop->AddIdleHandler(this); UIAddChildWindowContainer(m_hWnd); CoCreateInstance(__uuidof(NewFree), NULL, CLSCTX_INPROC_SERVER,
__uuidof(INewFree), (void**)&m_pINewFree); return TRUE;
} LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
CAboutDlg dlg;
dlg.DoModal();
return 0;
} LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
CTestClass *pTestClass = NULL;
m_pINewFree->GetMem((BYTE**)&pTestClass);
delete pTestClass; // 一运行这里就出错!!! why???
// TODO: Add validation code
//CloseDialog(wID);
return 0;
} LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
CloseDialog(wID);
return 0;
} void CloseDialog(int nVal)
{
DestroyWindow();
::PostQuitMessage(nVal);
} LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
m_pINewFree->Release();
m_pINewFree = NULL; // TODO : Add Code for message handler. Call DefWindowProc if necessary.
return 0;
} INewFree *m_pINewFree;
};
///////////////////////////////////////////////////////////////////////////////{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.#endif // !defined(AFX_MAINDLG_H__93023046_B71B_4954_BFF1_EC4D0D1B4FC1__INCLUDED_)
--------------------------------------------------------------------------------
com 部分是 atl dll 工程
client 是 WTL 对话框工程
----------------------------------------------------------------------------1. 如果 没有
virtual ~CTestClass()
{
}ClientTest.exe debug 和 Release 在编译器下运行, 有 gpf,
Release 直接运行, 没有 gpf, 但是直接打开资源管理, 观察内存(虚拟内存也同步增加)变化, 每次加 4k2.如果有, 一起正常, ------------------------------------------------------------------上面的回答都没法解释这个实际结果继续交流
中的 class CTestClass
{
public:
CTestClass()
{
}
virtual ~CTestClass()
{
} int m_iTest[10000]; // 修正 m_iTest[1000];
};
{
void* a = mymalloc(100); free(a);
return 0;
}bbb.dll#include <windows.h>void* WINAPI mymalloc(int nSize)
{
return malloc(nSize);
}注意:编译时不要选择MultiThead(DLL)方式。
肯定出问题。
{
public:
CTestClass()
{
}
virtual ~CTestClass()
{
} int m_iTest[10000]; // 修正 m_iTest[1000];
};放到你的工程中 再说话