Question about VARIANT deallocation
-
After more than 8 hours trying to fix up a memory leak I need some help :/ I have a ATL COM component, with the following interface: STDMETHODIMP CDCSClient::CallService( VARIANT *p1 ) Inside the method I do the following: VariantClear( p ); VariantInit( p ); p->vt = VT_BSTR | VT_BYREF; BSTR *pBSTR = new BSTR; *pBSTR = SysAllocString( L"abc" ); p->pbstrVal = pBSTR; And my client in VB is like this: For i = 1 To 1000 Dim myInt As Integer myInt = 3 ret = client.CallService(myInt) Next As far as I know the COM engine is creating a VARIANT from my integer (which is received as VT_BYREF | VT_I2), and I change it to VT_BSTR. The VB loop causes a huge memory leak. Am I doing something wrong ? How the COM engine handles the new VARIANT/BSTR deallocation ? Thanks for any help.
GuimaSun www.nexsun.com.br NEXSUN TechZone
-
It seem's you might not correctly release the BSTR accociated with the VARIANT *p1 variable. You might have manually to release variant accoiated with myInt variable.
It was my first guess, but I don't think so...each time I receive a call (from the loop) I receive the same integer VT_BYREF | VT_I2 (which is deallocated by VariantClear). It seems that this BSTR is lost between calls, causing the leak (obviously myInt is an integer and can't reflect changes).
GuimaSun www.nexsun.com.br NEXSUN TechZone
-
It was my first guess, but I don't think so...each time I receive a call (from the loop) I receive the same integer VT_BYREF | VT_I2 (which is deallocated by VariantClear). It seems that this BSTR is lost between calls, causing the leak (obviously myInt is an integer and can't reflect changes).
GuimaSun www.nexsun.com.br NEXSUN TechZone
-
If the BSTR is lost why don't you use BSTR* as variable type instead of VARIANT. Which version of VB are you using?
I can't change that interface, the real code is far more complicated, basically I need to receive a VARIANT* and change their value, and the new value comes from a c++ string. I'm testing using VB 6. Thanks.
GuimaSun www.nexsun.com.br NEXSUN TechZone
-
After more than 8 hours trying to fix up a memory leak I need some help :/ I have a ATL COM component, with the following interface: STDMETHODIMP CDCSClient::CallService( VARIANT *p1 ) Inside the method I do the following: VariantClear( p ); VariantInit( p ); p->vt = VT_BSTR | VT_BYREF; BSTR *pBSTR = new BSTR; *pBSTR = SysAllocString( L"abc" ); p->pbstrVal = pBSTR; And my client in VB is like this: For i = 1 To 1000 Dim myInt As Integer myInt = 3 ret = client.CallService(myInt) Next As far as I know the COM engine is creating a VARIANT from my integer (which is received as VT_BYREF | VT_I2), and I change it to VT_BSTR. The VB loop causes a huge memory leak. Am I doing something wrong ? How the COM engine handles the new VARIANT/BSTR deallocation ? Thanks for any help.
GuimaSun www.nexsun.com.br NEXSUN TechZone
I suspect that rather than 'new BSTR', you should use CoTaskMemAlloc[^] to allocate memory in a COM server:
BSTR *pBSTR = (BSTR*)CoTaskMemAlloc(sizeof(BSTR));
However - one other thing strikes me - why pass the BSTR back by reference? Why not use this?
VariantClear( p );
VariantInit( p );
p->vt = VT_BSTR;
p->pbstrVal = SysAllocString( L"abc" );One less allocation, one less place to leak memory!
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
-
I suspect that rather than 'new BSTR', you should use CoTaskMemAlloc[^] to allocate memory in a COM server:
BSTR *pBSTR = (BSTR*)CoTaskMemAlloc(sizeof(BSTR));
However - one other thing strikes me - why pass the BSTR back by reference? Why not use this?
VariantClear( p );
VariantInit( p );
p->vt = VT_BSTR;
p->pbstrVal = SysAllocString( L"abc" );One less allocation, one less place to leak memory!
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
Hi The same happens with CoTaskMemAlloc, and using VT_BSTR instead of VT_BSTR | VT_BYREF. Thanks anyway.
GuimaSun www.nexsun.com.br NEXSUN TechZone
-
After more than 8 hours trying to fix up a memory leak I need some help :/ I have a ATL COM component, with the following interface: STDMETHODIMP CDCSClient::CallService( VARIANT *p1 ) Inside the method I do the following: VariantClear( p ); VariantInit( p ); p->vt = VT_BSTR | VT_BYREF; BSTR *pBSTR = new BSTR; *pBSTR = SysAllocString( L"abc" ); p->pbstrVal = pBSTR; And my client in VB is like this: For i = 1 To 1000 Dim myInt As Integer myInt = 3 ret = client.CallService(myInt) Next As far as I know the COM engine is creating a VARIANT from my integer (which is received as VT_BYREF | VT_I2), and I change it to VT_BSTR. The VB loop causes a huge memory leak. Am I doing something wrong ? How the COM engine handles the new VARIANT/BSTR deallocation ? Thanks for any help.
GuimaSun www.nexsun.com.br NEXSUN TechZone
How CallService is defined in IDL file? If it's "CallService([in] VARIANT *p1)", then you cannot change the parameter. If it's "CallService([in,out] VARIANT *p1), then you cannot receive the parameter in Integer. IAC, VB passes myInt variable as VT_BYREF | VT_I2 to be possible to change the myInt value. It's made only during call some method. Inside VB don't store variant by this manner. STDMETHODIMP CDCSClient::CallService( VARIANT *p ) { VariantClear( p ); p->vt = VT_BSTR; p->bstrVal = SysAllocString( L"abc" ); return S_OK; } If you desire to pass BSTR by reference, you should keep out this address and make sure to free this BSTR after using. BSTR g_pBSTR = NULL; // global scope, so somewhere should be "if (!g_pBSTR) SysFreeString(g_pBSTR);" STDMETHODIMP CDCSClient::CallService( VARIANT *p ) { VariantClear( p ); p->vt = VT_BSTR | VT_BYREF; if (!g_pBSTR) g_pBSTR = SysAllocString( L"abc" ); p->pbstrVal = &g_pBSTR; return S_OK; }
With best wishes, Vita
-
How CallService is defined in IDL file? If it's "CallService([in] VARIANT *p1)", then you cannot change the parameter. If it's "CallService([in,out] VARIANT *p1), then you cannot receive the parameter in Integer. IAC, VB passes myInt variable as VT_BYREF | VT_I2 to be possible to change the myInt value. It's made only during call some method. Inside VB don't store variant by this manner. STDMETHODIMP CDCSClient::CallService( VARIANT *p ) { VariantClear( p ); p->vt = VT_BSTR; p->bstrVal = SysAllocString( L"abc" ); return S_OK; } If you desire to pass BSTR by reference, you should keep out this address and make sure to free this BSTR after using. BSTR g_pBSTR = NULL; // global scope, so somewhere should be "if (!g_pBSTR) SysFreeString(g_pBSTR);" STDMETHODIMP CDCSClient::CallService( VARIANT *p ) { VariantClear( p ); p->vt = VT_BSTR | VT_BYREF; if (!g_pBSTR) g_pBSTR = SysAllocString( L"abc" ); p->pbstrVal = &g_pBSTR; return S_OK; }
With best wishes, Vita
The parameter is defined as [in,out,optional]. And yes, now I understand, as I suspected, I can't reallocate the VT_BYREF | VT_I2 inside my ATL code. The only thing still not 100% clear which confused me is that I can define a VB procedure receiving a byref variant, do the same thing receiving my int, and changing it to String and no leak happens. That was my first test. But the difference is: there's no COM in this scenario, and the pure VB allocation\dellocation mechanisms are not the same. Thanks a lot !
GuimaSun www.nexsun.com.br NEXSUN TechZone
-
The parameter is defined as [in,out,optional]. And yes, now I understand, as I suspected, I can't reallocate the VT_BYREF | VT_I2 inside my ATL code. The only thing still not 100% clear which confused me is that I can define a VB procedure receiving a byref variant, do the same thing receiving my int, and changing it to String and no leak happens. That was my first test. But the difference is: there's no COM in this scenario, and the pure VB allocation\dellocation mechanisms are not the same. Thanks a lot !
GuimaSun www.nexsun.com.br NEXSUN TechZone
STDMETHODIMP CDCSClient::CallService(/*[in,out]*/ VARIANT *p)
{
if (V_VT(p) == (VT_VARIANT | VT_BYREF))
{
// only here you can change the type of passed variable (see VB example below)
VARIANT *p2 = V_VARIANTREF(p);
p2->vt = VT_BSTR;
p2->bstrVal = SysAllocString( L"abc" );
return S_OK;
}
}Dim v As Variant
v = 1
Debug.Print TypeName(v) & " " & v
Call obj.CallService(v)
Debug.Print TypeName(v) & " " & vWith best wishes, Vita
-
The parameter is defined as [in,out,optional]. And yes, now I understand, as I suspected, I can't reallocate the VT_BYREF | VT_I2 inside my ATL code. The only thing still not 100% clear which confused me is that I can define a VB procedure receiving a byref variant, do the same thing receiving my int, and changing it to String and no leak happens. That was my first test. But the difference is: there's no COM in this scenario, and the pure VB allocation\dellocation mechanisms are not the same. Thanks a lot !
GuimaSun www.nexsun.com.br NEXSUN TechZone
About memory leaks. VB allocation\dellocation mechanisms are the same with COM.
p->vt = VT_BSTR | VT_BYREF;
BSTR *pBSTR = new BSTR;
*pBSTR = SysAllocString( L"abc" );
p->pbstrVal = pBSTR;There are 2 memory allocations in statements "= new BSTR" and "= SysAllocString". The VARIANT with VT_BYREF|VT_BSTR isn't an owner of its values, so freeing of the VARIANT doesn't free neither pbstrVal nor *pbstrVal. And finally you have memory leaks. The VARIANT with VT_BSTR (without VT_BYREF) is an owner of its values, so freeing of the VARIANT frees bstrVal.
p->vt = VT_BSTR;
p->bstrVal = SysAllocString( L"abc" );And there are no memory leaks.
With best wishes, Vita