Mentioned
In
|
 |
 |
 |
 |
Microsoft Knowledge Base Article
This article contents is Microsoft Copyrighted material.
©2005-©2007 Microsoft Corporation. All rights reserved. Terms
of Use |
Trademarks
Article ID: 242527 - Last Review: January 6, 2004 - Revision: 4.0 PRB: #import Wrapper Methods May Cause Access ViolationThis article was previously published under Q242527
If a COM object has a method that returns an interface pointer (retval), and #import is used to generate a wrapper for the object, a smart pointer is returned. If a raw pointer is used for the assignment, an access violation may occur on the first attempt to use the interface.
The returned smart pointer object frees itself at the end of the wrapper function call. This releases the last reference on the object (though there was no corresponding AddRef), making the left hand side of the assignment an invalid reference. In Visual C++ 5, code generated from #import for this type of method simply returned the raw pointer.
If a Compiler COM support class is used for the assignment, it performs a QueryInterface() on the returned interface, retaining a reference count on the object. Thus, the result will be valid and the interface can be used as intended. The call could be made like the following:
IDispatchPtr spDisp = spServer->GetOtherInterface();
Although this would be the preferred solution, an LPDISPATCH variable could still be used if the raw interface is detached from the smart pointer at the end of the function call. This could be done in the following manner:
LPDISPATCH pDisp = spServer->GetOtherInterface().Detach();
The _com_ptr_t Detach() method hands back the raw interface pointer and sets the smart pointer's interface member to NULL so that Release() is not called.
Please note that, although the example earlier is for an IDispatch pointer, this information pertains to wrapper methods that return any kind of interface pointer.
Microsoft Visual C++ 6 more clearly demarcates the presence or absence of the raw_native_types attribute for #import. To return the raw pointer in Visual C++ 6, apply this attribute to the #import statement.
Steps to Reproduce Behavior- Create an Automation Server project. Call it "ImportServer". The steps provided here represent an ATL In-Proc Automation Server, so for the ATL COM AppWizard simply accept the defaults and click Finish.
- Insert a new ATL object and select a Simple Object. Name it Server1.
- Insert a second ATL object and select Simple Object again. Name it Server2.
- Add a member variable to Server1 for Server2 as in the following:
CComObject<CServer2>* m_pServer2;
Don't forget to include the header file for Server2, and to initialize the pointer in Server1's constructor, or use an initializer.
- Override Server1's FinalConstruct() and FinalRelease() methods to create an instance of Server2 when Server1 is created, and destroy the instance when it is destroyed.
HRESULT FinalConstruct()
{
HRESULT hr = CComObject<CServer2>::CreateInstance(&m_pServer2);
if (SUCCEEDED(hr))
m_pServer2->AddRef();
return hr;
}
void FinalRelease()
{
if (m_pServer2)
m_pServer2->Release();
}
- Add a method to Server1 which returns an LPDISPATCH pointer for Server2. The method could be prototyped as follows:
STDMETHOD(ReturnServer2)(/*[out,retval]*/ LPDISPATCH* ppDisp);
The implementation simply queries Server2 for its IDispatch interface:
STDMETHODIMP CServer1::ReturnServer2(LPDISPATCH* ppDisp)
{
return m_pServer2->QueryInterface(IID_IDispatch, (void**)ppDisp);
}
- Add a method to Server2 so that it can be tested. For this example, add a method which returns a string value, such as:
STDMETHOD(GetString)(/*[out,retval]*/ BSTR* pstr);
Here is a sample implementation:
STDMETHODIMP CServer2::GetString(BSTR* pstr)
{
*pstr = SysAllocString(L"Hello from Server2");
return S_OK;
}
- Create a Win32 Console app to serve as the client. Here is some sample test code:
#include "stdafx.h"
int main(int argc, char* argv[])
{
CoInitialize(NULL);
{
IServer1Ptr spServer1;
spServer1.CreateInstance(__uuidof(Server1)); // for Visual C++ 6.0
spServer1.CreateInstance(__uuidof(CServer1)); // for Visual C++.NET
TCHAR szTemp[50];
for (int i = 0; i < 5; i++)
{
LPDISPATCH pDisp = spServer1->ReturnServer2();
IServer2Ptr spServer2(pDisp);
BSTR str = spServer2->GetString();
int nRet = WideCharToMultiByte(0, 0, str,
SysStringLen(str)+1, szTemp, 50,
NULL, NULL);
if (nRet)
{
lstrcat(szTemp, "\n");
OutputDebugString(szTemp);
}
SysFreeString(str);
pDisp->Release();
}
}
CoUninitialize();
return 0;
}
- In stdafx.h, import the type library for the server. For example:
#import "..\ImportServer.tlb" no_namespace // For Visual C++ 6.0
or
#import "..\ImportServer.tlb" no namespace // For Visual C++ .NET
-
Compile the server and the client. When the client is executed, an error should occur on creation of spServer2, since pDisp is not valid.
To correct the problem, change the line which calls ReturnServer2 as follows:
IDispatchPtr spDisp = spServer1->ReturnServer2();
Also, the call to Release() the dispatch pointer should now be removed.
As mentioned previously, the other solution could be to detach the raw dispatch pointer from the smart pointer wrapper:
LPDISPATCH pDisp = spServer1->ReturnServer2().Detach();
If this method is used, then Release() would still need to be called for the Dispatch interface.
For more information on #import, see the Visual C++ Programmer's Guide in MSDN, under Preprocessor Reference\The Preprocessor\Preprocessor Directives.
Smart pointers are implemented in the _com_ptr_t class, and documented in the Reference section of the Visual C++ documentation, under C/C++ Language and C++ Libraries\C++ Language Reference\Compiler COM Support Classes.
The ATL classes CComPtr and CComQIPtr are also smart pointers, and could be used successfully in place of IDispatchPtr. For more information, see the ATL Class Reference.
APPLIES TO- Microsoft Visual C++ 6.0 Enterprise Edition
- Microsoft Visual C++ 6.0 Professional Edition
- Microsoft Visual C++, 32-bit Learning Edition 6.0
- Microsoft Visual C++ .NET 2002 Standard Edition
| kbautomation kbprb kbsmartptr kbpending KB242527 |
Community Feedback System
Very often, it takes hours to solve a problem. Very often, you've looked high
and low, and have tried a lot of solutions. When you finally found it, chances
are, it was because someone else helped you. Here's your chance to give back.
Use our community feedback tool to let others know what worked for you and what
didn't.
Please also understand that the community feedback system is not warranted to be
correct, it's simply a system that we've built to let people try and help each
other. If something in a feedback response doesn't make sense to you, or you're
not comfortable making changes that the feedback talks about (like registry
edits), please consult a professional.
Thank you for using kbAlertz.com Feedback System.
-- Scott Cate
Be the first to leave feedback, to help others about this knowledge base
article.
(Optional) Name
(Optional)
Public URL Or Email
Comments
No
HTML -- Text Only Please
|
 |
 |
 |
 |
 |
 |
 |
| |