|
>"Non-creatable" option presented a real problem for me while writing this wizard
All you had to do was to skip adding macro DECLARE_REGISTRY_RESOURCEID to the class declaration. There's nothing else to it.
As for the whole stuff in general, i have doubts, because implementation of ATL collection for simple types and interfaces are quite different from each other, i.e. it is one thing writing a COM collection of simple types, and totally different writing that for COM interfaces, i.e. the latter is way more complex/tricky.
|
|
|
|
|
<br />
[!if=(FreeThreadedMarshaler, "TRUE")]<br />
[!set(NeedsGetControllingUnknown, "TRUE")]<br />
[!endif]<br />
[!crlf]<br />
<br />
[!if=(FileExists, "FALSE")]<br />
#ifndef __[!UpperShortName]_H_<br />
#define __[!UpperShortName]_H_<br />
[!crlf]<br />
#include "resource.h"
[!else]<br />
[!AddIncludeFile(TargetFile, "resource.h")]<br />
[!endif]<br />
[!crlf]<br />
#include "[!COLLECTION_BASE][!COLLECTION_IMPL]Impl.h"<br />
[!crlf]<br />
<br />
typedef ICollectionOnSTLImpl<idispatchimpl><[!interfacename], &iid_[!interfacename]="">,<br />
[!COLLECTION_IMPL]< CAdapt< CComPtr<[!COLLECTION_BASE]> > >,<br />
[!COLLECTION_BASE]*,<br />
_CopyItfFromAdaptItf<[!COLLECTION_BASE]>,<br />
CComEnum[!COLLECTION_BASE]VariantOnSTL[!COLLECTION_IMPL]><br />
[!InterfaceName]Impl;<br />
<br />
<br />
class ATL_NO_VTABLE [!ClassName] : <br />
[!if=(ThreadingModel, "Single")]<br />
public CComObjectRootEx<ccomsinglethreadmodel>,<br />
[!endif]<br />
[!if=(ThreadingModel, "Apartment")]<br />
public CComObjectRootEx<ccomsinglethreadmodel>,<br />
[!endif]<br />
[!if=(ThreadingModel, "Both")]<br />
public CComObjectRootEx<ccommultithreadmodel>,<br />
[!endif]<br />
[!if=(ThreadingModel, "Free")]<br />
public CComObjectRootEx<ccommultithreadmodel>,<br />
[!endif]<br />
public CComCoClass<[!ClassName], &CLSID_[!CoClassName]>,<br />
[!if=(ErrorInfoEnabled, "TRUE")]<br />
public ISupportErrorInfo,<br />
[!endif]<br />
[!if=(ConnectionPointsEnabled, "TRUE")]<br />
public IConnectionPointContainerImpl<[!ClassName]>,<br />
[!endif]<br />
public [!InterfaceName]Impl<br />
{<br />
public:<br />
[!ClassName]()<br />
{<br />
[!if=(FreeThreadedMarshaler, "TRUE")]<br />
m_pUnkMarshaler = NULL;<br />
[!endif]<br />
}<br />
<br />
[!crlf]<br />
[!if=(COLLECTION_UNREGISTERED,"TRUE")]<br />
DECLARE_NO_REGISTRY()<br />
[!else]<br />
DECLARE_REGISTRY_RESOURCEID([!IDR_REGISTRYID])<br />
[!endif]<br />
<br />
[!if=(Aggregatable, "NO")]<br />
DECLARE_NOT_AGGREGATABLE([!ClassName])<br />
[!endif]<br />
[!if=(Aggregatable, "ONLY")]<br />
DECLARE_ONLY_AGGREGATABLE([!ClassName])<br />
[!endif]<br />
[!crlf]<br />
DECLARE_PROTECT_FINAL_CONSTRUCT()<br />
[!crlf]<br />
BEGIN_COM_MAP([!ClassName])<br />
COM_INTERFACE_ENTRY([!InterfaceName])<br />
[!if=(Dual, "TRUE")]<br />
COM_INTERFACE_ENTRY(IDispatch)<br />
[!endif]<br />
[!if=(ConnectionPointsEnabled, "TRUE")]<br />
COM_INTERFACE_ENTRY(IConnectionPointContainer)<br />
[!endif]<br />
[!if=(FreeThreadedMarshaler, "TRUE")]<br />
COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.p)<br />
[!endif]<br />
[!if=(ErrorInfoEnabled, "TRUE")]<br />
COM_INTERFACE_ENTRY(ISupportErrorInfo)<br />
[!endif]<br />
END_COM_MAP()<br />
[!if=(ConnectionPointsEnabled, "TRUE")]<br />
[!crlf]<br />
BEGIN_CONNECTION_POINT_MAP([!ClassName])<br />
END_CONNECTION_POINT_MAP()<br />
[!endif]<br />
<br />
[!if=(FreeThreadedMarshaler, "TRUE")]<br />
[!crlf]<br />
HRESULT FinalConstruct()<br />
{<br />
return CoCreateFreeThreadedMarshaler(<br />
GetControllingUnknown(), &m_pUnkMarshaler.p);<br />
}<br />
<br />
[!crlf]<br />
void FinalRelease()<br />
{<br />
m_pUnkMarshaler.Release();<br />
}<br />
<br />
[!crlf]<br />
CComPtr<iunknown> m_pUnkMarshaler;<br />
[!endif]<br />
<br />
[!if=(ErrorInfoEnabled, "TRUE")]<br />
[!crlf]<br />
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);<br />
[!endif]<br />
[!crlf]<br />
<br />
public:<br />
[!if=(COLLECTION_ADD,"TRUE")]<br />
STDMETHOD(Add)([!COLLECTION_BASE]* NewVal)<br />
{<br />
if(NewVal == NULL)<br />
{<br />
return E_POINTER;<br />
}<br />
<br />
m_coll.push_back(CComPtr<[!COLLECTION_BASE]>(NewVal));<br />
return S_OK;<br />
}<br />
[!endif]<br />
<br />
[!if=(COLLECTION_REMOVE,"TRUE")]<br />
STDMETHOD(Remove)(long Index, [!COLLECTION_BASE]** ppVal)<br />
{<br />
if(ppVal == NULL || *ppVal != NULL)<br />
{<br />
return E_POINTER;<br />
}<br />
if (Index < 1 || Index > m_coll.size())<br />
{<br />
return S_FALSE;<br />
}<br />
[!if=(COLLECTION_IMPL,"vector")]<br />
HRESULT hRes = m_coll[Index - 1].m_T.QueryInterface(ppVal);<br />
if(hRes != S_OK)<br />
{<br />
return E_NOINTERFACE;<br />
}<br />
<br />
m_coll.erase(m_coll.begin() + Index - 1);<br />
return S_OK;<br />
[!endif]<br />
[!if=(COLLECTION_IMPL,"list")]<br />
<br />
typedef [!COLLECTION_IMPL]< CAdapt< CComPtr<[!COLLECTION_BASE]> > >::iterator it;<br />
long Count(0);<br />
for ( it i = m_coll.begin(); i != m_coll.end(); i++,Count++ )<br />
{<br />
if ( Count == (Index - 1) )<br />
{<br />
HRESULT hRes = (*i).m_T.QueryInterface(ppVal);<br />
if(hRes != S_OK)<br />
{<br />
return E_NOINTERFACE;<br />
}<br />
<br />
m_coll.erase(i);<br />
return S_OK;<br />
}<br />
}<br />
return E_INVALIDARG;<br />
[!endif]<br />
}<br />
[!endif]<br />
};<br />
<br />
[!crlf]<br />
[!if=(FileExists, "FALSE")]<br />
#endif //__[!UpperShortName]_H_<br />
[!endif]<br />
<br />
</iunknown></ccommultithreadmodel></ccommultithreadmodel></ccomsinglethreadmodel></ccomsinglethreadmodel></[!interfacename],></idispatchimpl>
|
|
|
|
|
Hi,
I need a clarification regarding ATL COM object. Can i use ATL COM to create outlook plugin which needs to send data to web-server for authenticate user. If not how we can send data to web-server from outlook plugin....
Krishna
|
|
|
|
|
Does anyone know a way to sort lists created by this wizard?
I'd like to just call m_coll.sort() but I can't work out how to define the predicate comparison function.
Any ideas?
Thanks,
Gary
|
|
|
|
|
This doesn't answer your question, but could be useful to people trying to sort a collection based on an STL vector. This isn't the most efficient way of doing this though, inserting the point into the collection at the correct point using a binary search would be better. This implementation is quick and nasty!
typedef CAdapt< CComPtr<imyclass> > IMyClassImpl;
class CMyCollection
{
public:
...
static bool compare(const IMyClassImpl & a, const IMyClassImpl & b)
{
float fXA, fXB;
a.m_T->get_x(&fXA);
b.m_T->get_x(&fXB);
return (fXA < fXB);
};
STDMETHOD(Add)(IMyClass* NewVal)
{
// add to the STL vector
m_coll.push_back(CComPtr<imyclass>(NewVal));
// sort points by x value
std::sort(m_coll.begin(), m_coll.end(), compare);
// everything is ok
return S_OK;
}
...
};
|
|
|
|
|
Excelent, thank's for share...
|
|
|
|
|
I think you are totally right here
|
|
|
|
|
Is there a way to use this wizard with visual stuido 7?
thanks!
|
|
|
|
|
how do insert OLE Excel into Visual Basic? Can you help me?
|
|
|
|
|
Is there any class in MFC to that implements ICollectionOnSTLImpl?
I know how to mix ATL in MFC application, but just wonder if there is a class in MFC that will do the same trick.
|
|
|
|
|
can any one tell me how to compile the IDL file?
and which IDL file should i compile in for this wizard
|
|
|
|
|
Fantastic, certainly make my life easier!
|
|
|
|
|
Wizard is working fine, but there is one problem intellisense stop to work whenever i create the collection using the wizard.
--qur
|
|
|
|
|
The auto-generated Remove method, if selected, has an [out, retval] pointer for one of the members of the collection. This value is not referenced in the generated code. To return a reference to the removed object, and conform to the prototype, insert
(*i).m_T.QueryInterface(ppVal);
before
m_coll.erase(i);
Doing this before the erase on the STL collection object ensures the existance of a reference before the object is deleted.
|
|
|
|
|
Thanks, your wizard will save me a lot of time! The code is nice too.
Mat
|
|
|
|
|
1. Selecting either list or vector disables the OK button.
2. Finishing the wizard without selecting container (see above) creates files and function declarations to IDL but not implementation what so ever.
3. Also there's a bug in the forward declaration section in .idl file.
Regards,
Sami
|
|
|
|
|
I noticed that no matter what options I chose for the "unregistered" checkbox the wizard was always placing the DECLARE_NO_REGISTRY() macro in my class.
Turns out the error is at line 60 of COLLECTION.H which was copied in the ..\Template\ATL directory.
The old line
[!if(COLLECTION_UNREGISTERED,"TRUE")]
The new line
[!if=(COLLECTION_UNREGISTERED,"TRUE")}
.. note the "=" after the if.
BTW .. as pointed out in the previous thread .. since explicitedly used "W" version of some apis this wizard will only work on NT systems .. just FYI.
Well, thanks for a great time saver
|
|
|
|
|
The wizard wasn't working for me too, so I debugged it and fixed it, instead of complaining;)
_CollectionDlg.cpp, in _LoadSupportedInterfaces
In the loop below, you need to cut the if (!bInterfacesArePresent){...} and put it after the loop.
for (long i = 0; i<lCount; i++)
{
TYPEKIND TKind;
if ( SUCCEEDED(spTypeLib->GetTypeInfoType(i,&TKind)) && (TKind == TKIND_INTERFACE || TKind == TKIND_DISPATCH))
{
bInterfacesArePresent = TRUE;
CComBSTR bstrName;
spTypeLib->GetDocumentation(i, &bstrName, NULL, NULL, NULL);
::SendDlgItemMessageW(m_hWnd,ID, LB_ADDSTRING, 0,LPARAM(bstrName.m_str));
}
if ( !bInterfacesArePresent )
{
MessageBox(_T("There are no interfaces declared in the TypeLib! Can't use this wizard."),_T("Error"),MB_ICONERROR);
return E_FAIL;
}
}
Also, my system seemed to have some problems with the FindFirstFileW() and SendDlgItemMessageW() so I converted the BSTRs to ANSI and used the ANSI versions.
|
|
|
|
|
But in other hand - it's good to
|
|
|
|
|
I'm new to COM and ATL and I'm trying to produce a simple component that will serve collections of other COM objects.
I've created my COM object, LoginUser and tested it with VB - all OK.
I've created my COM collection for LoginUser objects and tested it with VB - all OK.
What I am unable to do is add LoginUser object to the collection in VC++. I get the collection back with the correct number of items but all of the items appear to be uninitialised. Can anyone tell me how to do this. I've appended a snipet of my code in case anyone can tell me where I've gone astray and how to fix it.
STDMETHODIMP WASP::GetUsers( IWASPrecordset **pUser )
{
HRESULT hr;
IWASPrecordset *pR = NULL;
// Start COM subsystem
CoInitialize( NULL );
// Create a recordset object,
hr = CoCreateInstance ( CLSID_WASPrecordset,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IWASPrecordset),
(void**) &pR );
// Did we succeed
if( SUCCEEDED(hr) )
{
for( i = 0; i < 10; ++i )
{
ILoginUserInt *pU = NULL;
// Create a LoginUser object,
hr = CoCreateInstance ( CLSID_LoginUserInt,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(ILoginUserInt),
(void**) &pU );
// Did we succeed - put user details into login user object
if( SUCCEEDED(hr) )
{
_bstr_t bst( "James" );
// Put user name into user object
pU->put_User( bst );
// Now put object into collection
pR->Add( pU );
// Release my copy of this object
pU->Release();
pU = NULL;
}
}
// Now set the return value to be the new object
pR->QueryInterface(__uuidof(IWASPrecordset),(void **) pUser);
// Release my copy of the object
pR->Release();
pR = NULL;
}
// Stop COM subsystem
::CoUninitialize();
return hr;
}
Thanks
Nic.
|
|
|
|
|
2 things strike me about your code.
Firstly I, and the rest of the group I work with have come to realise that you should always use CComPtr's, (and CComVariant's and CComBSTR's) wherever possible. This leads onto my second observation. If you declare your collection as collection_type< CAdapt < CComPtr < IFoo > > > then when you add a member to it will automatically AddRef. I suspect that when release pU the reference count goes to zero and the instance of your object commits suicide.
Using CAdapt in your collection type prevents the CComPtr override of the & operator so it can be used safely in a collection. The advantage of using this is that if you call pR->release and the ATL collection goes through FinalRelease, when clear() is called on the collection, all the objects in your collection will automatically be Release()d
Tom Mason
|
|
|
|
|
Tom,
I do have my collection declared as you describe list< CAdapt < CComPtr < ILoginUser > > >. I think you are correct about the suicide of my pU objects but I have not yet been able to discover why.
Thanks for taking the time to look over my code.
Nic.
|
|
|
|
|
Nic,
Sorry my last didn't help. Have you tried putting message boxes, breakpoints or trace statement in the FinalRelease() method of your ILoginUser object?
That is something I have done more than once to trace similar problems.
Tom.
|
|
|
|
|
I'v found a better way to do it. For example we have Users object that is a collection and User object. What we need is just to add several users to collection in constructor. This will show you how i made it:
typedef CComObject<cuser> CUserObj;
CUsers::CUsers()
{
for(int i=0;i<10;i++){
CUserObj *usr;
CUserObj::CreateInstance(&usr);
CComBSTR a("aaa");
usr->m_bszName = a.Copy();;
IUser *pObj;
usr->QueryInterface(IID_IUser, (void**)&pObj);
Add(pObj);
}
}
Hope this will be useful to you.
|
|
|
|
|
This wizard is a must have for ATL programmers! Especially since the Titan wizard from moretechnologies is no more available since 1998 and this kind of wizard is not yet integrated in VS.
In David Petersons documentation I read that "the stl::map container will be supported in the future". This is exactly what I still need. Will it be available and if yes, when? Or is there another wizard supporting this feature? Thanks, Gerald
|
|
|
|
|