DLL management

rambovn
rambovn used Ask the Experts™
on
Hi all,
I created a DLL in Visual 2008. in one of the methods of the DLL, a Cstring pointer is returned. When the program goes out of scope. it trys to delete this pointer and it throws a memory exception. I know why this exception happens. I think this is a common problem in DLL. I would like know whether there is a standard way to solve this problem.

Here is my code:
Code in C++

if ( wms.Connect( strURL, NormalizePath( m_strVectorPath ), strUserName, strPassword, bLog ? strWMSLogFile : (const char*)NULL ) )
{
      CString *sPN = NULL;
      sPN = wms.DownloadLayer(); // this is a pointer returned by DLL
        if ( sPN )
      {
            for (int k = 0; k < 10; k++)
            {
                  strLayer = sPN[k];
                  CString strFileName = NormalizePath( m_strVectorPath ) + strLayer + strImageFormat;
                  CVisuObject* pVisu = m_pras->LoadRaster( strFileName );
                  if ( pVisu )
                  {
                        AddVisuLayer( pVisu );
                        pVisu->SetTag( strLayer );
                  }
      }                                                
      wms.Disconnect(); // this function will call CHTTPImageDownloader::~CHTTPImageDownloader()
}
When the program exits from this if, memory exception is thrown.

Code in DLL
CString* CHTTPImageDownloader::DownloadLayer()
{
      if ( !m_DownloadInfo.m_bValid )
            return NULL;

      CString strLayers = m_DownloadInfo.m_strLayer;
      strLayers.MakeUpper();
      bool bMultiLayers = strLayers.Find(_T(','), 0) != -1;
      
      int iLayer = 0;
      bool bIsSuccessful = true;
      while (iLayer < m_arrWMSLayers.GetSize())
      {
            CString strName;
            for ( ; iLayer < m_arrWMSLayers.GetSize(); iLayer++ )
            {
                  CWMSLayer* p = m_arrWMSLayers[iLayer];
                  if (!bMultiLayers)
                  {
                        CString strNameTmp = p->m_strName;
                        strName = strNameTmp;
                        CString strLayerTmp = m_DownloadInfo.m_strLayer;
                        strNameTmp.MakeUpper();
                        strLayerTmp.MakeUpper();
                        int nLength = strLayerTmp.GetLength();
                        CString temp;
                        temp = strNameTmp.Mid(-1, nLength);
                        if (temp.CompareNoCase(strLayerTmp) == 0)            // two strings are identical
                        {
                              bool bIsAdded = true;
                              for (int i = 0; i < m_nArrSize; i++)
                              {
                                    if (m_pPathNames[i].CompareNoCase(strNameTmp) == 0)
                                          bIsAdded = false;
                              }
                              if (bIsAdded)            
                                    m_pPathNames[m_nArrSize++] = strNameTmp;
                              
                               break;
                        }
                  }
                  else
                  {
                        CString strName = p->m_strName;
                        strName.MakeUpper();
                        if (m_DownloadInfo.m_strLayer.Find(p->m_strName) != -1)
                              break;
                  }
            }

      return m_pPathNames;
}

CHTTPImageDownloader::~CHTTPImageDownloader()
{      
      if (m_pPathNames)
      {
            delete[] m_pPathNames;
            m_pPathNames = NULL;
      }
}

Any suggestions?
Thanks,
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Hi rambovn,

where and how is 'm_pPathNames' created? Is it a member of CHTTPImageDownloader? I don't really see an error here.

BTW: Maybe it would be better not to use a C-style array. If you'd use some container class like i.e. STL-vector or MFC's CStringArray you won't have to new/delete anything and you could return a reference rather than a pointer ...

ZOPPO

Commented:
You said you know why it happens. So the easiest way to fix it without touching the source code is to change the C Run-Time Library settings in your projects: http://msdn.microsoft.com/en-us/library/abx4dbyh(VS.80).aspx
Here are more explanations:
http://www.rtsoft.com/forums/archive/index.php/t-1607.html
http://www.codeguru.com/forum/archive/index.php/t-313913.html
Your CString object was created in one heap (DLL), and the app tries to release the memory in the own heap - that was happens in the most cases like you described.
If all your project will have the same setting - C Run-Time library as Multi-threaded DLL, and both Unicode, or, :(,  MultiByte - probably you did it it will not crash.
 Even with the STL it will crash: http://www.codeguru.com/forum/archive/index.php/t-229782.html
If you are ready to modify the source code, this link can be interesting for you:  http://www.codeproject.com/KB/DLL/BSTR_CString_conversion.aspx?display=PrintAll&fid=16351&df=90&mpp=25&noise=3&sort=Position&view=Quick&select=572038
And in this thread a developer past all stages of this well-known problem: http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg=microsoft.public.vc.mfc&tid=8075ff5c-3d3e-40fc-b0b3-04b563436e69&cat=&lang=&cr=&sloc=&p=1
 

Commented:
Now I'm not that sure. Sorry.
Because of that:
        CWMSLayer* p = m_arrWMSLayers[iLayer];
...
        CString strNameTmp = p->m_strName;

 and then you clean m_pPathNames
Is it possible that you delete the CString objects twice?
 
Announcing the Winners!

The results are in for the 15th Annual Expert Awards! Congratulations to the winners, and thank you to everyone who participated in the nominations. We are so grateful for the valuable contributions experts make on a daily basis. Click to read more about this year’s recipients!

Author

Commented:
Thank you for yr answers.
@Zoppo: yes, you're right, when looking at my code, it seems nothing wrong, but whenever the program goes out of scope, i.e. go out the if clause, then it crashes. Here is what I get from stack when it crashed
_CrtIsValidHeapPointer(const void * 0x066912a8) line 1606
_free_dbg_lk(void * 0x066912a8, int 1) line 1011 + 9 bytes
_free_dbg(void * 0x066912a8, int 1) line 970 + 13 bytes
operator delete(void * 0x066912a8) line 351 + 12 bytes
CString::FreeData() line 146 + 15 bytes
CString::~CString() line 213

m_pPathNames is a member variable of class CHTTPImageDownloader
Cstring * m_pPathNames;
m_pPathNames = new Cstring[10];

First I also used CStringArray, but I had the same problem, the program tried to delete it automatically, and it crashed.
In both case (CString* and CstringArray) I didn't delete anything, the program did it automatically for me even though I don't want it :-(

@pgnatyuk: Sorry, I could not get what you meant :-(. I have to read yr comment again.
Hi rambovn,

could it be that you somehow mix binaries from different VS-versions? In MFC of VS2008 there's no function CString::FreeData, so it looks to me as if it comes from an older VC++ version.

ZOPPO

Author

Commented:
yes, you're right, my DLL used VS2008, and my app used VC6 :-(, and for this I can not change it.
So do you have any suggestion?
ok, IMO this means the CStrings are created using VS 2008 implementation but deleted using VC6 implementation.

I think you have to ensure somehow CStrings are created and deleted within the same module using the same implementation.

I would suggest to implement two (none-inline) functions in your 'CHTTPImageDownloader', i.e. 'InitStringArray' and 'ReleaseStringArray' i.e. like this:

void CHTTPImageDownloader::InitStringArray()
{
 m_pPathNames = new Cstring[10];
}

void CHTTPImageDownloader::ReleaseStringArray()
{
 delete [] m_pPathNames;
 m_pPathNames  = NULL;
}

Call 'CHTTPImageDownloader::InitStringArray' where up to now you called 'm_pPathNames = new Cstring[10]', call 'CHTTPImageDownloader::ReleaseStringArray()' from '~CHTTPImageDownloader'.

Hope that helps,

ZOPPO
BTW, I think you may have to wrap the code which accesses the 'm_pPathNames' from the VC6 app to call functions within the VS 2008 DLL, i.e.:

Instead of 'if (m_pPathNames[i].CompareNoCase(strNameTmp) == 0)' implement a function in 'CHTTPImageDownloader' which compares a passed string with the n'th entry of 'm_pPathNames' (where 'n' needs to be passed to the function too).

Instead of 'm_pPathNames[m_nArrSize++] = strNameTmp' implement a function in 'CHTTPImageDownloader' like 'AppendPathName'.

Implement both of them as none-inline functions.

Author

Commented:
Thank you very much, Zoppo. But it didn't work, it still crashed at the same place with same error :-(
Hm - after some thinking about this I come to the conclusion that using CString in the header where 'CHTTPImageDownloader' is declared is a general problem since if you include that header in the VC6 code the compiler 'sees' a different CString-class than the VS 2008 compiler.

You could try to move all code which has to do with that CString-array into an own class which is wrapped by 'CHTTPImageDownloader' via a pimpl idiom, i.e. somehow like this:


// in header
> class CHTTPImageDownloaderImpl; // forward declaration
>
> class CHTTPImageDownloader // this class doesn't use CString at all
> {
>  CHTTPImageDownloaderImpl* m_pImpl;
> public:
>  CHTTPImageDownloader();
>  void AppendPathName( LPCTSTR pszPath );
>  ... // further functions which need to access the string-array
> };

// in cpp-file
> ...
> class CHTTPImageDownloaderImpl
> {
>  CString* m_pPathNames;
>  int m_nArrSize;
> public:
>  CHTTPImageDownloaderImpl() { m_pPathNames = new CString[10]; m_nArrSize = 0 }
>  ~CHTTPImageDownloaderImpl() { delete [] m_pPathNames; }
>  void AddPathName( LPCTSTR pszPath ) { m_pPathNames[ m_nArrSize++ ] = pszPath;
>  ... // further functions to be called by 'CHTTPImageDownloader'
> };
>
> void CHTTPImageDownloader::AddPathName( LPCTSTR pszPath )
> {
>  m_pImpl->AddPathName( pszPath );
> }
> ...

With this you encapsulate all CString handling within the class 'CHTTPImageDownloaderImpl' which is not known by the calling VC6 application.

Hope that helps,

ZOPPO

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial