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;
}

解决方案 »

  1.   

    看看pbData是不是空,看看Getmem返回给pbDATA得是不是内部Buffer得指针,并且指向得内存是不是pINewFree依旧需要使用得。
      

  2.   

    pstr不是new出来
    不要用delete
      

  3.   

    还好是CLSCTX_INPROC_SERVER实例,否则运行到pstr->Format("sss")是就已经死定了,
    引用代理空间的实例一定要基于IUnknown的,释放时用Release。
    想象一下跨进程的引用,从服务进程中返回一个对象的指针,该指针客户进程中指向的地址简直都不知到是什么东西,使用并delete真的无法想象回造成什么后果!
      

  4.   

    pstr->Format("sss"); 是后来测试的时候加上的,肯定是有问题的主要的问题出在 wtl 的CString 的析够函数不是 virtual , 造成 delete 
    析够的出错CLSCTX_INPROC_SERVER && 执行的时候同步的, 所以不会有什么问题???http://blog.csdn.net/newkey007/archive/2004/07/09/38139.aspx
      

  5.   

    西够函数非vurtual会造成出错吗?只是不能正确四放基类对象,出错和这也有关系??
      

  6.   

    回: newkey007(无限天空)
    按你所说我做了一个实例,测试没问题,用的是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 必然会出错!
      

  7.   

    很高兴和大家一起探讨问题我都是用的 WTL,要跨接口操作类,肯定2边都是对应一个类
    只所以要在接口参数中用类指针
    完全是为了简化开发人员的负担,以及体现面向对象的思想
    如果一个稍大一点的软件,全部是用COM来构架的,单纯的用 LPTSTR LPTSTR* CoTaskMemAlloc 的结构内存块这样是很麻烦的,当然我的前题是都在VC中用,并且是进程内组件,同步调用
      

  8.   

    dada9527(傻蛋:用MSDN骗你的分) 因为这是2个dll
      

  9.   

    回: newkey007(无限天空)
    你能告诉我出错的断言是什么吗?
      

  10.   

    用mfc做客户端做了测试eg:
    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;
    }
      

  11.   

    回: newkey007(无限天空)
    你能告诉我出错的断言是什么吗?
    ------------------------------------
    因为在 跨 dll的时候, 如果析构函数不是 虚拟的, 不是在 server端的 heap中释放你可以查一下 虚拟析构函数 的相关知识,就明白了
      

  12.   

    FROM: MSDN
    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!!!
    不要管他是不是虚拟析构函数,就从内存分配和释放的角度来考虑吧,是又何妨,不是又何妨呢!!!
      

  13.   

    内存空间地址不同 传出来的是 com 中分配在测试端不能删除除非 用CString **pstr 从Com在本地分配
      

  14.   

    1:可能你要delete的指针为NULL
    2:可能数组越界
      

  15.   

    我做的dll也遇到了和你同样的问题
    后来用导出一个release函数的方法来解决
    release函数内部调用delete回收内存
      

  16.   

    没有 new 出来的内存  ,我觉得 是不是没有必要 用delete ---因为根本就没有给她分配内存,, 他只是 用别人的 pbData
      

  17.   

    好危险的new和delete啊,在多线程中这就是恶梦。
    还是在组件外面new和delete,把指针传进去COPY一份数据为好。
      

  18.   

    谁分配谁释放。Memory is allocated for passing between objects across interfaces by calling CoTaskMemAlloc. It is freed by calling CoTaskMemFree. You can allocate, pass, and free memory safely between objects created in different programming languages by using a central memory allocator.
      

  19.   

    面向对象ClassCoTaskMemAlloc 只能分配内存块前提都说的很清楚了,
    进程内组件 同步
    --------------------
    不要用COM,就被COM固定死了有些时候,为了简化操作,可以直接写个接口函数,返回个 类指针, 这个类指针 没那些 idl 参数的约束
      

  20.   

    想一想:
    为什么IUnknown接口有个Delete,而不是直接用delete ??因为不能直接delete !
      

  21.   

    iicup(双杯献酒) ( ):请问 “IUnknown接口有个Delete” ,这是什么意思
      

  22.   

    跨模块申请释放内存,必死无疑!!!!一点原则:哪个模块申请内存就由哪个模块释放。
    记住CString的特殊性,修改CString的值实际上也产生了内存的申请释放。对于MFC而言,如果模块都是Debug或者都是Release版,就没有这个问题,
    因为对CString的内存管理都发生在MFC42D.dll或者Mfc42.dll中,实际
    上没有发生跨模块申请释放内存。如果一个模块为Debug而一个为Release,必死无疑!!!!
    因为一个发生在MFC42D.dll而一个发生在Release中,就发生跨模块申请释放内存。
      

  23.   

    因为你的测试程序是在Debug状态,它有自己的Debug heap,它调用delete时,会检查所释放内存是否在它自己的Debug heap内,如果不是,就认为是非法的内存,它无权释放。而你的组件负责分配内存,它也有自己的Debug heap(Debug 状态)或进程Base heap(Release 状态),显然不会到测试程序的Debug heap中去申请内存。
    所以不管你的组件是Debug或Release版本,只要你的测试程序是Debug版本,程序运行就会出错!反之,只要你的测试程序是Release版本,就不会存在检查Debug heap的问题,肯定可以通过。
      

  24.   

    这个问题也许不需要解决,因为你的测试程序和组件最终会是在Release状态下、在同一个进程堆里运行,delete那块内存是合理的,不会存在异常或泄露
      

  25.   

    不同意 jim3000(jim)在VC中, new 调用malloc, 最终调用HeapAlloc的。看一下HeapAlloc的定义就知道,第一个参数hHeap是通过HeapCreate得到的。在MFC程序中,最终在MSVCRTD.dll中调用的HeapAlloc.----对于Release版为MSVCRT.DLL. 这里的hHeap为MSVCRT(D).DLL中的一个参数_crtheap是在MSVCRT.DLL初始化时产生的。同样,delete 调用 free, 最终调用HeapFree函数。假如HeapAlloc的第一个参数和HeapFree的第一个参数不同,肯定会产生问题的。所以,在Debug和Release方式因为最终调用模块不同--MSVCRT.DLL和MSVCRTD.DLL,所以就产生了问题。在一般情况下,如果编译时没有选择MultiThread(DLL)方式,内存的申请和释放都由模块自己管理。显然,每个模块调用的HeapAlloc的第一个参数都是不同的,跨模块进行内存申请和释放当然会有问题。这时,即使都是Debug版本或者Release版本,显然产生了内存泄漏。在MFC的情况下,编译时肯定是MultiThread(DLL)方式,所以, MFC的程序如果都是Debug或者Release版本,进行跨模块申请和释放内存是不会产生问题的。---因为最终调用的是MSVCRTD.DLL或者MSVCRTD.DLL。
      

  26.   

    verybigbug() ,你自己先做个试验看看行不行吧。
    我的试验和资料表明:
        问题的根源不是跨模块申请内存就怎么样,而是因为Debug heap是个局部堆,它附属于进程堆,局部堆只能管自己范围内的内存,每个程序在Debug状态都由VC分派一个局部堆(Debug heap)使用,这个局部堆虽然附属于进程,但管不了局部堆之外的事情。
        本例中,测试程序在Debug状态,它要delete组件分配的内存,而组件中new出来的内存不在测试程序的Debug heap中,肯定会报错
        在debug状态下delete一个指针,VC会调用_CrtIsValidHeapPointer来检查指针的有效性,异常也是在这里产生。
        看看这个函数的说明就明白了:_CrtIsValidHeapPointer
      

  27.   

    verybigbug()老兄,本例中的组件或测试程序可能根本不需要MFC的支持,ATL就足够了
      

  28.   

    我在这里只给出一份测试数据, 你们可以自己copy,编译后测试------------------------------------------------------------
    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.如果有, 一起正常, ------------------------------------------------------------------上面的回答都没法解释这个实际结果继续交流
      

  29.   

    Client 部分
    中的 class CTestClass 
    {
    public:
    CTestClass()
    {
    }

    virtual ~CTestClass()
    {
    } int m_iTest[10000];  // 修正 m_iTest[1000];  
    };
      

  30.   

    编译一下这个程序就知道了。aaa.exe#include <windows.h>void* WINAPI mymalloc(int nSize);int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow)
    {
    void* a = mymalloc(100); free(a);
    return 0;
    }bbb.dll#include <windows.h>void* WINAPI mymalloc(int nSize)
    {
    return malloc(nSize);
    }注意:编译时不要选择MultiThead(DLL)方式。
    肯定出问题。
      

  31.   

    你先把 class CTestClass 
    {
    public:
    CTestClass()
    {
    }

    virtual ~CTestClass()
    {
    } int m_iTest[10000];  // 修正 m_iTest[1000];  
    };放到你的工程中 再说话