I created a ATL COM dll workspace with VS2008. It has generated MyCom.idl interface. Does it provide any easy way to add a method to this interface?
Yes, there is an option to do that.
It is not that hard to do this without a wizard. I learned that adding COM interface methods the hard way because I was too lazy to look it up in MSDN, so I looked closely at other COM interfaces and experimented by adding properties, methods, etc.
First of all there are two drawbacks when using the wizard, especially when editing existing COM interfaces. First it is slow, especially if you have a large component with dozens of interfaces. I sit in front of a quite decent workstation and opening the wizard and completeing the two or three steps takes longer than adding the method by hand to the IDL, .h and .cpp file.
Second is that when using different Visual Studio and thus different Wizard versions there will be generated c++ dirt like multiple public-statements without any need and stuff like that. However I recommend that you act like the wizard in order to not confuse it further if the next programmer uses the wizard again - which will increase confusion and in the end leading to increased file sizes, unreadable class declarations and longer compile times. Just insert the same type of methods or macros at the same place the wizard does.
So maybe you might find useful what I found out so long. You can find a lot more details in the article "Understanding IDL" which is quite old, but provides essential basics which I will not mention here though.
The IDL file holds all interface definitions your component will export. So if you want to add a method you need to find the corresponding interface definition which is introduced by the "interface" keyword and a name which usally starts with a capital I. The interface is preceded by some annotations surrounded by [ and ] which I will not explain for the sake of simplicity. When using Visual Studio IDL keywords will be highlighted correctly. This is how a typical interface might look like:
[
object,
uuid(0B2499FA-0D73-488C-B961-03FB8327485C),
dual, helpstring("IMyInterface Interface"),
pointer_default(unique)
]
interface IMyInterface : IDispatch {
// ... methods and properties
};
Now let us assume the IMyInterface contains one method only and you want to add a similar second one:
[id(1), EXCLUDE_FROM_HELP] HRESULT DoSomething([in] long newVal, [out, retval] long* pRetVal);
Again there are annotations within the [ ] brackets. When adding a new method increase the id annotations value. Also precede the parameters with the [in] annotation for input parameters, [out] for parameters which will contain the methods results. If there is only one result you may want to use a method which uses the result as return value. Then use the [out, retval] annotation.
So when adding such a method copy the line above, increase the ID and changed the methods name and adapt the parameters accordingly.
Note that the [out] parameters always must be a pointer. There are several reasons for this, the most obvious one is that [in, out] is possible too, thus the result for a parameter should be written to the same memory address inside the component so it is possible to pass it back to the caller easily. Also note that the "real" return value should be an HRESULT handle, which will indicate the success or failure of the method.
Now let us look inside the C++ header. I assume you will find the correct file and place by yourself.
STDMETHOD(DoSomething)(/*[in]*/ long newVal, /*[out, retval]*/ long* pRetVal);
The STDMETHOD macro takes care of the result value which is HRESULT and the calltype. The annoations are not valid here but for clarification of the intended usage within the IDL file they are added as comments. These comments are optional though. Be careful to use the same datatypes as inside the IDL file.
STDMETHODIMP CMyClass::DoSomething( /*[in]*/ long newVal, /*[out, retval]*/ long* pRetVal )
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
long result = GetTheResultValueFromSomewhere();
*pRetVal = result;
return S_OK;
}
The STDMETHODIMP is a different macro than in the header. Basically it does the same though. Important is the AFX_MANAGE_STATE macro if you want to use resources within an MFC dialog for instance. Imagine the value in the example above is determined by prompting the user. Read about it in the Technical Tips blog post.
Understanding the IDL mechanisms will help you as soon as errors occur which do not necessarily refer to a problem in your logic but the interface. It will also allow you to understand what the interface actually does, which will help you avoding errors when using the wizard so I encourage anyone to read at least some basic information about it.
There should be an option if you expand the idl in the class view to right click on a specific class and 'add method' or 'add property' which will take you through a small wizard adding the method to your interface and your associated implementation.
Related
Can I send a list of objects through COM?
I have my class and there is also a list of this class. And I need to send this through a COM function. Can I do that? If yes, then how? Do I need serialization marshaling?
COM does not pin down any particular collection type, it certainly doesn't have anything standard that models a list. By convention, you can model any collection with an interface. So say you have a collection of Foo objects that each implement IFoo. Then declare an IEnumFoo interface similar to:
interface IEnumFoo : IUnknown
{
HRESULT Next( [in] ULONG celt,
[out, size_is(celt), length_is(*pceltFetched)] IFoo **rgelt,
[in,out,unique] ULONG *pceltFetched );
HRESULT Skip( [in] ULONG celt);
HRESULT Reset();
HRESULT Clone( [out] IEnumFoo **ppenum );
}
And then simply return an interface pointer to an implementation of this interface to allow the client code to enumerate the list. Check the MSDN docs for IEnumVARIANT, a very common enumeration interface type for variants. Which also explains what the methods do.
If one side of the interface will be written in a language with a "simpler" type system, such as VB6 or script, then the SAFEARRAY is the way to go. COM's universal marshaller can take care of it.
If you have C++ on both sides of the interface, and you're happy to define the interface in IDL, and generate custom marshalling code, then IDL includes a "size_is" attribute that can be used. See here for details.
To any reasonable degree, there's no way to do so with std::list. I'm sure there is some maniac who could manage it but from my experience, it's not worth it. If doesn't have to be an std::list and just some linked list, then you'll have to either define a new COM interface and provide it's implementation (which can just be a wrapper around std::list) or find some thirdparty resource that has an adequate implementation for your purposes. If you don't have much experience with implementing COM interfaces, then you'll need to do some good amount of reading.
If it can be an array, then you can make your life a lot simpler by using a SAFEARRAY. And SAFEARRAYs can be easier to use with ATL's CComSafeArray, which is pretty much a wrapper. It tries to help/save you from issues, such as reference counting, when dealing with COM objects. You can then just pass the array between your COM objects. Just going to remind you that you still need to pay attention to who is responsible for de-allocating the memory.
I'm trying to call Visual Basic's CreateObject method from my C++ code. In VB, I'd simply type:
Dim obj As Object
obj = CreateObject("WScript.Network")
And that returns me the object from which I can call more methods. But how can I do that in C++? I'm following the MSDN documentation in http://msdn.microsoft.com/en-us/library/bb776046(v=VS.85).aspx, but those parameters are very obscure and I can't figure them out.
The first parameter is a reference to a CLSID, and I can see from the registry that the CLSID for "WScript.Network" is {093FF999-1EA0-4079-9525-9614C3504B74}. But what's the difference between this parameter and the third one, REFIID?
Thanks in advance!
I'll provide my solution, just for the record. It calls the AddWindowsPrinterConnection to install a network printer. It asks for user confirmation, so if you want to bypass that, you need to set HKEY_CURRENT_USER/Printers/LegacyPointAndPrint/DisableLegacyPointAndPrintAdminSecurityWarning to 1 (you can change it back to 0, after everything is done).
CoInitialize(NULL);
{
ATL::CComPtr<IDispatch> test;
_variant_t printerConnection = "\\\\serverAddress\\printerName";
_variant_t result;
test.CoCreateInstance(L"WScript.Network");
test.Invoke1(L"AddWindowsPrinterConnection", &printerConnection, &result);
}
CoUninitialize();
First, you probably want to use CoCreateInstance http://msdn.microsoft.com/en-us/library/ms686615%28VS.85%29.aspx, or the equivalent call inside a smart pointer wrapper (eg: CComPtr<>, _com_ptr<>, etc.).
Second, to your specific question, the IID is the interface ID, the CLSID is the class ID. COM objects can have multiple interfaces on the same object in general, which is why there is a distinction (although VB can only see one, which is why you don't need so to specify anything other than the CLSID for VB).
The "correct" way to duplicate what VB is doing is to create the IDispatch interface on the object, and then enumerate the methods using IDispatch. The "better" way in C++ is to create the direct interface you want to use, and call methods directly through it. However, this requires knowing the interface ID (IID, or REFIID passing the struct by reference), which is specific to the other object.
Hope that helps. I can't provide specifics for your particular interface, but maybe this points you in the right direction.
I'm at the beginning of a C++ project and I've been using Doxygen from the start.
I'd like to know how you use Doxygen in your project, i.e. I have several questions:
1. Where do you put your Doxygen comments? Header or sources?
I think that they should go to the header, because that's where I look to find out how to use methods. However, I like to omit actual parameter names in prototypes, so I can not use #param - or can I? How do you tackle this?
2. Do you document all methods?
I'm only documenting public methods so far, how do you do it? Do you document accessor methods and public variables?
3. Do you always fill out #param and #return?
Where I work (it's Javadoc, but it's the same issue), we have a convention to fill only actually needed properties, i.e. if the brief descriptions says "Returns xys if ...", we omit #return. If the parameter names are obvious, we omit them. I'm still not sure if I like that approach, how do you do it? So far, I've only filled out the brief and nothing else, but not all method prototypes are straightforward enough for that.
4. Which style do you use?
There are several styles in Doxygen: Javadoc (/** ... /), QT (/! ... */) and more. Purely out of interest: Which one do you use? I'm going with Javadoc style ATM because I'm used to it.
1. Where do you put your Doxygen comments? Header or sources?
I can't answer this because I actually don't currently remember where I tend to document in terms of header versus source.
2. Do you document all methods?
Almost completely yes. Every single method gets some form of documentation, unless it is instantly obvious from the variable/method name (and parameter names for methods) what it does in specifics. I tend to go by the rule of "If you can't work out the purpose of a method by it's name and parameter names, it needs a comment. If after commenting you still cannot work out the purpose of the method, re-write the comment. If you still cannot see very quickly the purpose of the method, or if the comment is 'too long' (where 'too long' is an arbitrary measurement >_>), then you need to re-write the method or split it up."
3. Do you always fill out #param and #return?
Yes. Even if it's blindingly obvious from reading the #brief, or if the #return is an exact copy of sentence in the #brief, I still fill them in. It can be very useful to have that sort of scan property for a method's documentation. "Oh, method X, I know what it does and why, but what exactly is its return value in X situation again?" *checks the #return*.
4. Which style do you use?
Javadoc myself, although this is completely subjective. I use the Javadoc syntax because I spent a while writing in Java and got very used to that syntax. I also personally think it makes more sense than the others – I just don't like the QT syntax at all.
1. Where do you put your Doxygen comments? Header or sources?
Documentation goes in headers since this is where the interface is defined.
2. Do you document all methods?
For classes, I document all of the public and protected methods, I generally leave private methods alone.
3. Do you always fill out #param and #return?
I prefer the inline parameter documentation
/*!
* \brief My great class.
*/
class Foo
{
public:
/*!
* \brief My great method.
*/
void method(
int parameter //!< [in] parameter does something great
);
};
to using \param since it results in duplication of the parameter name, and can easily get out of sync with the code when lazy developers forget to change the doxygen.
\return is omitted when there's a void return type. I always use \throw when the method can throw.
4. Which style do you use?
Does not matter as long as its consistent in the entire project.
or a VB6 - compatible - collection object.
We provide hooks into our .net products through a set of API's.
We need to continue to support customers that call our API's from VB6, so we need to continue supporting VB6 collection objects (simple with VBA.Collection in .net).
The problem is supporting some sites that use VBScript to call our API's. VBScript has no concept of a collection object, so to create a collection object to pass to our API we built a VB6 ActiveX DLL that provides a "CreateCollection" method. This method simply creates and passes back a new collection object. Problem solved.
After many years of pruning, porting and re-building, this DLL is the only VB6 code we have. Because of it we still need to install Visual Studio 6 on our Dev & build Machines.
I'm not happy with our reliance on this DLL for several reasons (my personal dislike of VB6 is not one of them). Top of the list is that Microsoft no longer support Visual Studio 6.
My question is, how do I get ATL to create a collection object that implements the same interface as the VB6 collection object.
I've a good handle on C++, but only a loose grasp of ATL - I can create simple objects and implement simple methods, but this is beyond me.
Collections are more or less based on convention. They implement IDispatch and expose some standard methods and properties:
Add() - optional
Remove() - optional
Item()
Count - read-only
_NewEnum - hidden, read-only, returns pointer to enumerator object that implements IEnumVariant
The _NewEnum property is what allows Visual Basic For Each.
In the IDL you use a dual interface and:
DISPID_VALUE for Item()
[propget, id(DISPID_NEWENUM), restricted] HRESULT _NewEnum([out, retval] IUnknown** pVal)
Here are some MSDN entries: Design Considerations for ActiveX Objects
And here is some ATL specific convenience: ATL Collections and Enumerators
Lets target this VBScript snippet
Dim vElem
For Each vElem In MyObject
...
Next
particularly the implementation of MyObject. As a minimum you have to implement a method/propget with DISPID_NEWENUM on the default dispinterface (its dual/dispinterface to talk about DISPIDs). You can name it whatever you want, it doesn't matter. Most collections use NewEnum, and flag it in IDL as hidden. VB6 uses underscore prefix to mark hidden methods so you might see _NewEnum as recommendation but it's kind of a cargo cult ATL does.
You don't need any Count, Item, Add, Remove, Clear or any other method at all (on the default interface). You can supply these as a convenience (particulatly Item accessor and probably Count) but you don't have to, to make the sample code above work.
Next, the retval has to be a separate object (so called enumerator) which implements IEnumVARIANT interface by using a (private) pointer to MyObject. In IDL you can declare retval as IUnknown nothing wrong here. What is most interesting is that you have to implement only the Next method on IEnumVARIANT, you can return E_NOTIMPLEMENTED on the rest if you like or optionally implement them though these are never called by For Each. What makes the implementation even easier is that celt parameter of Next (the number of items requested) is always 1, so For Each requests items always one by one.
What you can use in ATL is CComEnumOnSTL and the like to create a "proxy" enumerator on an STL container, or the array based enumerator ATL provides (and exclude STL).
For a good example of how to implement COM collections that would be used naturally in script programming languages, check out my website
It offers a comprehensive example of how to do that...
Continuing from this question, i am confused whether DISPID_VALUE on IDispatch::Invoke() for script functions and properties (JavaScript in my case) can be considered standard and reliable for invoking the actual function that is represented by the IDispatch?
If yes, is that mentioned anywhere in MSDN?
Please note that the question is about if that behaviour can be expected, not what some interfaces i can't know in advance might look like.
A simple use case would be:
// usage in JavaScript
myObject.attachEvent("TestEvent", function() { alert("rhubarb"); });
// handler in ActiveX, MyObject::attachEvent(), C++
incomingDispatch->Invoke(DISPID_VALUE, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, par, res, ex, err);
edit: tried to clarify the question.
It should be reliable for invokes on objects from scripts if the script defines it consistently. This should be the case for JScript/Javascript in MSHTML, but unfortunately there is really sparse documentation on the subject, I don't have any solid proof in-hand.
In my own experience, a Javascript function passed to attachEvent() should always be consistent- an object received that is a 'function' can only have one callable method that matches itself. Hence the default method is the only one you can find, with DISPID 0. Javascript functions don't ordinarily have member functions, although i'm sure there is a way for this to be possible. If it did have member functions, you would see them the same way as member functions on objects. Member functions in JScript will always be consistent with regard to IDispatchEx, according to the rules of expando functions, as any functions added to an object count as expandos.
IDispatchEx interface # MSDN
The default method or property that DISPID_VALUE invokes should be consistent for a given interface. That method/property has to be specified as DISPID_VALUE in the definition of the interface in the IDL for the type library. The only way it could change is if the owner of the interface released a new version of the interface that changed which method/property was the default but that would violate a fundamental rule of COM interfaces.
As meklarian said, DISPID_VALUE (0) seems to work pretty consistantly for JS functions (thus it works great with a custom attachEvent). I've been using them this way for about a year, and it's always worked. I've also found with an activeX control embedded with an <object> tag that to get it to work consistently, I need to implement IConnectionPointContainer and IConnectionPoint for the main (object tag) IDispatch-implementing CComObject, but any others that I expose to javascript as return values from methods or properties (through Invoke) I have to implement attachEvent and detachEvent myself.
When using Connection Points, the IDispatch objects in question will expect events to be fired to the same DISPID as they are attached to on your IDispatch object..
see http://code.google.com/p/firebreath/source/browse/src/ActiveXPlugin/JSAPI_IDispatchEx.h for an example of implementing the ConnectionPoints.
You can add DISPID's to a DISPINTERFACE, but you cannot change them once it has been published. If you need to, you can use IDispatch::GetIDsOfNames to map names to DISPIDs.
Pick up a copy of Inside Ole (2nd ed) and Inside Ole 2 (2nd ed) for a few bucks used on Amazon. It's a good reference for these obscure OLE incantations.