Solved

Why NO memory leak with AllocSysString() in COM object???

Posted on 2008-09-29
11
1,593 Views
Last Modified: 2013-11-17
I have build a COM wrapper for an underlying C++ DLL in order to access it with VB6 and .NET, but I am puzzled by why I'm NOT having a memory leak for one of the methods I wrote.

I have run the VB snippet below with 100,000 random strings of length 10,000 (a total of 1GB of strings allocated via AllocSysString(). Whether the FreeString() method is called or not, there is NO difference in the memory footprint of the running process (either memory usage or VM size) -- in both cases, over the entire run, the memory usage is 7.2MBytes and the VM size is 2.8MBytes.

As I understand it, it should be necessary for the VB program to free the memory allocated in the wrapper by AllocSysString(). But it looks like (somehow) that memory is being deallocated without my having to do so.

Here's my question -- can someone explain whether either (1) this test is flawed such that there really isn't a bunch of memory allocated, or (2) some part of VB6 or the COM mechanism is deallocating the memory for me, or (3) something else even more mysterious is happening such that I am not seeing 1GB of memory leak.

Thanks in advance.

COM Wrapper code:
 

STDMETHODIMP CETModule::SetInString(LONG VarNum, BSTR Value)

{

   // Get the specified Input variable

   IVariable *pVar = pModule->GetInVariable(VarNum);
 

   USES_CONVERSION;

   if (pVar == NULL)

      // Invalid VarNum

      return E_INVALIDARG;

   else

      pVar->SetString(W2A(OLE2T(Value)));
 

   return S_OK;

}
 

STDMETHODIMP CETModule::GetOutString(LONG VarNum, BSTR* Result)

{

   // Get the specified Output variable

   IVariable *pVar = pModule->GetOutVariable(VarNum)
 

   if (pVar == NULL)

      // Invalid VarNum

      return E_INVALIDARG;

   else

      // MUST BE DEALLOCATED BY CETModule::FreeString()

      *Result = CString(pVar->GetString()).AllocSysString();
 

   return S_OK;

}
 

STDMETHODIMP CETModule::FreeString(BSTR Value)

{

   try

   {

      SysFreeString(Value);

   }

   catch(...)

   {}
 

   return S_OK;

}
 
 

VB6 Code:
 

   ...

   Dim index as Long

   Dim out As String

   For index = 1 To NumLoops

      ' push a random string into the COM object

      obj.SetInString 0, RandomString(StrSize)

      ' just transfer the input var to the output var

      obj.Calculate

      ' retrieve the random input string

      out = obj.GetOutString(0)

      If chkDealloc = 1 Then obj.FreeString (out)

   Next index
 

   MsgBox "Done.", vbOKOnly

   ...

Open in new window

0
Comment
Question by:sklein
  • 6
  • 5
11 Comments
 
LVL 24

Expert Comment

by:fridom
Comment Utility
That's wrong COM programming, COM is base on Reference Counting, and it is not allowed that the client can tamper with the Server data structures. You expose an Interface which does exactly that. If you return a BSTR it is abolutly in the responsibility of the client to free that memory,  it "owns" the String. You must prepare in  Release for freeing the Server BSTR. There must not be any link left from Client data structures to Server data structures.

Regards
Friedrich
0
 

Author Comment

by:sklein
Comment Utility
Friedrich--

What I am looking for is primarily an understanding of why I am NOT seeing a memory leak. As far as I can tell, the code sample I posted should leak. But it doesn't.

In this situation, reference counting is not an option. My example illustrates the sort of thing I need to do. The strings returned are dynamically created and there are potentially an unlimited number of them created during the COM object's lifetime. But they have a very short lifespan. Strict allocate/deallocate works better (I believe) than reference counting.

My question is still why it doesn't look like I need to do the deallocate!
0
 
LVL 24

Expert Comment

by:fridom
Comment Utility
You have not read my mail properly. You should not give the client access to  the intenal structures  of your server. You always generate fresh BSTR as return value.

Get a decent book about COM and check it. I suggest Essential COM....

You always have reference counting in COM that's the base of IUnknown...

Regards
Friedrich
0
 

Author Comment

by:sklein
Comment Utility
Friedrich--

That is not the issue here. I have implemented a COM object only because the underlying DLL is not easily accessible to VB6 or C#. It is strictly for internal consumption. I don't have the option of rewriting the underlying DLL. So, COM it is.

Second, this COM object will create (and return) an UNLIMITED number of dynamically generated strings. I have no control over that. Reference counting only adds overhead in tracking them.

Third, my question is why I'm NOT seeing memory leaks. Allocate/deallocate is a valid C/C++ style whether or not it is recommended for COM. In this case, it *appears* that the deallocate is not necessary. Does VB6 automatically deallocate [out, retval] BSTRs when they go out of scope or is something else happening here?
0
 
LVL 24

Expert Comment

by:fridom
Comment Utility
If you COM server obey the  rules than you do not have to worry about memory handling in Visual Basic. So if you have the typelibrary at hand VB runtime takes care of "getting" rid of the garbage. You however provided a facility to free a Servers allocated String to Client code and this is a bug.

Friedrich
0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 

Author Comment

by:sklein
Comment Utility
Friedrich--

I added the FreeString call because the sources I consulted indicated that every AllocSysString() call must be matched by an explicit SysFreeString() call. If that is not actually necessary in this case, I'd be happy to delete the (unnecessary) FreeString() method.  I am trying to determine if I really don't need to do an explicit deallocation, because none of the documentation I've seen makes that claim.

Normally, the VB runtime deallocates strings that it allocates. I have not seen the claim that it deallocates strings that are passed to it thru a COM interface. Perhaps that is what is happening, because (as I've indicated) I don't see any memory leaks.

So, are you making the claim that the combination of the VB Runtime and the COM interface result in the strings being automatically deallocated? If so, does that also apply to .NET + COM (per my original question)?
0
 

Author Comment

by:sklein
Comment Utility
Friedrich--

As an alternative strategy, if I specify that the string returned by a GetOutString() call must be copied before the next GetOutString() call, then the protocol can be that GetOutString() deallocates the *previous* allocated string before allocating the next string. Then, there is never more than one string allocated. The destructor also knows what to clean up. Ugly, but guaranteed not to leak. Thoughts?
0
 
LVL 24

Expert Comment

by:fridom
Comment Utility
The COM rule are strict and they are known in Visual Basic too. What you have to do is keep track  of the memory in  C/C++ code. So indeed ifyou get back a BSTR as out you know that it's your respnsibility to free it.
The typelibrary informs Visual Basic about that and so you do not have to call the SysFreeString in Visual Basic
If you like you can help the runtime with  setting the returned value to nil.

You still have you memory leak  if you do  not free the allocates memory on the server  side, and this is much more serious.  You generte  COM object and every time you set the string you are  leaking memory, for  that  you have to keep track of the reference count and provide a clean up in the Release Element of  the  IUnknown interface.

That's absolutly necessary  to get  right if you do  use COM...

COM and net have interoperability laysers which should be handled in .NET as in the visual basic runtime. But you  still have to provide the freein of  required resources in the Server. That's what I wrote, Clien and Server must no hamper with the data of the other side. This just can go wrong

For a book  about COM and net check .NET and COM the Complete Interoperability Guide

Regards
Friedrich

Regards
Friedrich
0
 

Author Comment

by:sklein
Comment Utility
Friedrich--

Microsoft doesn't agree with you. Please see the MSDN article: "Allocating and Releasing Memory for a BSTR" (http://msdn.microsoft.com/en-us/library/xda6xzx7(VS.71).aspx)

The intro section says:

When you create BSTRs and pass them between COM objects, you must take care in treating the memory they use in order to avoid memory leaks. When a BSTR stays within an interface, you must free its memory when you are done with it. However, when a BSTR passes out of an interface, the receiving object takes responsibility for its memory management.

The last sentence says that it is the client who is responsible for memory management in this specific case.

So, the model I am following is specifically per Microsoft recommendations. And back to my original question (which is still unanswered), why am I not seeing a memory leak when I don't do the deallocate? Since my code is consistent with Microsoft recommendations, can we stop discussing the need to free the memory on the server side?

Following is the specific Microsoft example from the above MSDN article which looks exactly like my GetOutString().


When you implement a function that returns a BSTR, allocate the string but do not free it. The receiving function releases the memory. For example:
 

// Example shows using MFC's 

// CString::AllocSysString
 

//...

HRESULT CMyClass::get_StatusText( BSTR * pbstr )

{
 

   try

   {

      //m_str is a CString in your class

      *pbstr = m_str.AllocSysString( );

      }

   catch (...)

   {

      return E_OUTOFMEMORY;

   }
 

// The client is now responsible for freeing pbstr.

return( S_OK );

}

//...

Open in new window

0
 
LVL 24

Accepted Solution

by:
fridom earned 500 total points
Comment Utility
I wrote it more than once. Of course the client must take care of the SysAlloced String, I haven't claimed it beeing different.  If you are in C/C++ you must call
SysFreeString on  a returned BSTR, but Visual Basic does not have this need. The Runtime takes care of it. I suggest you check the  Visual basic runtime docs and their memory handling.  In this  regard the client is not your program but the visual  basic runtime. Just check any other code with BSTR as return value and see how it's used in Visual  Basic

I also wrote you still have a memory leak on the  COM Server side and this  is  written in C/C++ you have  to handle the aquired memory in the Release Method. That's a  thing I  pointed out to be a bug in your code.

The code you posted is C or C++ code and yes you're responsible on the Client side to free this SysStringAlloced string and not you do not have to look after that in Visual Basic clients.

Regards
Friedrich





0
 

Author Comment

by:sklein
Comment Utility
Friedrich--
I think I agree with your analysis that the VB6 runtime is doing the deallocate call. That would be consistent with behavior for strings allocated in VB itself (it doesn't matter whether the string is allocated within VB or an arbitrary COM object).
This does explain why I see no memory leaks even though allocating more than 1GB of strings. Thanks.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Introduction: Ownerdraw of the grid button.  A singleton class implentation and usage. Continuing from the fifth article about sudoku.   Open the project in visual studio. Go to the class view – CGridButton should be visible as a class.  R…
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

7 Experts available now in Live!

Get 1:1 Help Now