COM development picking up <1>
For nearly a year, I have been developing COM components with VC's ATL, and there is a lot of obstacles. After hard work, most of them have been resolved. During this process, accumulated a little experience, now write down, or for the two purposes: organize archive; share some experience with you.
Where is ProgID?
This is the first question I have just encountered when I use ATL wizard. I can't find the progid but I can't find it. It turns out that it is hiding and the component is the same .RGS file, RGS is a script file registered by the component. When you use the regsvr32.exe registration component, the component is called this file. The RGS file is in the form of a resource in the DLL.
2. Handmade a method, which place is modified.
Suppose the component class is called COBJ, the interface called IObj, the method to be added is Open. First, find the definition of interface IOBJ in the IDL file, where to join the following method definition:
[ID (2), Helpstring ("Method Open"] HRESULT OPEN ([OUT, RETVAL] VARIANT_BOOL * RET);
Note: Numbers in the ID do not repeat with the ID already existing
Second, add the following member function declaration in the COBJ class definition header file:
PUBLIC:
STDMETHOD (OPEN) (VARIANT_BOOL * RET);
Finally, the implementation of the function is added to the COBJ class:
STDMETHODIMP OPEN (Variant_Bool * RET)
{
* Ret = varAint_true;
Return S_OK;
}
3. Multi-use assertion help debugging
The COM component is a separate entity that does not know the environment that calls it, nor does it know if the caller is called by the developer's agreement. So you should use Atlassert to set the checkpoint to facilitate debugging. For example, a database connection component, first in the Open method, you must first use the assertion to check if the Connectionstring property is assigned:
Atlassert (m_connstr! = Null);
After the assertion, you should follow the error handler, because in the release version, the assertions are all invalid, your error handling code will work.
IF (m_connstr == null)
Return E_FAIL;
Do you want to find a failure? Here is a judgment principle: your development COM is a COM within its own software, that is, when the client and COM are developed inside you or in the development group, you can do not deal with the assertion failure, and go check the assertion failure Whether the code of the client is not perfect; when you write COM is to submit it to "others", it will handle the assertion failed.
4. Write some macros that can be "lazy".
If a component has a lot of properties, the processing of the attribute is just directly accessing member variables, writing a lot of GET, the PUT function is a bit annoying. These functioners look almost the same, just different data types:
GET function: * pval = m_somemembercar;
PUT function body: m_somemembervar = newval;
We may wish to write a group of macros to handle this boring job. Inspired by the COPY policy class in ATL, I wrote the macro:
/ / Realize the universal macro of attribute write operation
#define prop_put_impl (name, Type) /
STDMETHOD (PUT _ ## Name) /
{/
m _ ## name = newval; / return s_ok; /
}
/ / Realize the universal macro of attribute read operation
#define prop_get_impl (name, Type, CopyAdapter) /
STDMETHOD (GET _ ## Name) (Type * PVAL) /
{/
CopyAdapter :: Destroy (PVAL); /
TYPE SRC = M _ ## name; /
Return CopyAdapter :: Copy (PVAL, & SRC); /
}
/ / Realize the general macro of read and write properties
#define prop_impl (Name, Type, CopyAdapter) /
PROP_PUT_IMPL (Name, TYPE) /
PROP_GET_IMPL (Name, TYPE, COPYADAPTER)
The premise of using this macro is that the naming of the member function of the property must be: "m_ attribute name", if there is a read and write attribute Name, type BSTR, I can implement it in the alignment class
Class ATL_NO_VTABLE COBJ
{
...
PUBLIC:
Prop_Impl (Name, BSTR, COPYBSTR)
Private:
CCOMBSTR M_NAME;
Copybstr is a COPY policy class I wrote, as follows:
Class CopyBSTR
{
PUBLIC:
Static HRESULT COPY (BSTR * P1, BSTR * P2)
{
Atlassert (p2! = Null);
IF (p2 == null)
Return E_POINTER;
CCOMBSTR BSTRTEMP = * P2;
Return BSTRTEMP.COPYTO (P1);
}
Static void init (bstr * str) {}
Static Void Destroy (BSTR * STR) {sysfreestring (* str);
}
It is also very simple to achieve read-only properties:
......
PUBLIC:
PROP_GET_IMPL (Name, BSTR, COPYBSTR)
Private:
m_name;
Implement some common types of writing as follows:
Prop_Impl (PropName, Long, _Copy
Prop_Impl (PropName, Short, _Copy
Prop_Impl (PropName, Varaint_Bool, _Copy
Prop_Impl (PropName, Idispatch *, _ CopyInterface
Prop_Impl (PropName, BSTR, COPYBSTR)
It is also an additional benefit using this macro: defined is the inline function, and the efficiency is a function that is generated by the wizard.
There is also a code segment is very common, such as:
HRESULT HR = Obj-> MethodCall ();
IF (Failed (HR))
Return HR;
We can define such a macro to replace it:
#define ret_ERR_IF_FAIL (EXP) /
DO {/
HRESULT __HR = (EXP); /
IF (Failed (HR) Return HR; /} while (false)
Then use this:
RET_ERR_IF_FAIL (OBJ-> MethodCall ());
RET_ERR_IF_FAIL (OBJ-> MethodCall2 ());
How, the code is neat.
5.BSTR's use
The beginner COM must have to plant heads and distributions in BSTR, and release it, and the memory disclosure is inevitable. Advise everyone try not to use BSTR directly, replace it with encapsulation _BSTR_T and CCOMBSTR. _BSTR_T is a class, it is MS to extension ANSI C , with it, you can use the BSTR type in the SDK mode (non-MFC, ATL). CCOMBSTR is a package to BSTR in ATL. When developing with ATL, in general, CCOMBSTR classes can be used. In the read function of the attribute, you can return a BSTR with CCOMBSTR's COPYTO method. CCOMBSTR overloads the address operator &, with the CCOMBSTR to take the address equal to the BSTR to take the address inside, this, CCOMBSTR can also be used on the OUT, RETVAL type parameter, for example:
A method for a COM class is defined as follows:
HRESULT GETUSERNAME ([In] BSTR Userid, [OUT, RETVAL] BSTR * UserName);
This method can be implemented as follows: The code returns to username:
......
m_username.copyto (username); // m_username is a member variable, type CCOMBSTR
When this COM's client calls GetUserName, you can also use the CCOMBSTR type variable to get UserName.
......
CCOMBSTR BSTRUSERID (L "001"), BStrUserName
Obj-> getUsername (BStrUserid, & bstrusername);
Whether it is a method GetUserName or a client, it is possible to automatically assign, release memory. Only one case exception, that is, when a CCOMBSTR variable is used as an active parameter as an output parameter, it will generate memory leakage if the processing is improper. Such as:
CCOMBSTR BSTRFIELDNAME, BSTRFIELDVALUE
BSTRFIELDNAME = L "USR_ID";
RecordSetobj-> getfieldValueAsstring (BStrfieldName, & BstrfieldValue);
m_userid = BSTRFIELDVALUE;
BSTRFIELDNAME = L "usr_name";
RecordSetobj-> getfieldValueAsstring (BStrfieldName, & BStrfieldValue); // If the method is implemented inside to overwrite the & BSTRFIELDVALUE pointer, the last value of BSTR will be lost. If the internal use of CCOMBSTR.COPYTO will not.
_BSTR_T type is not good, but we always use it when it is not forced, which is to import a COM package class with a #import precompiled instruction. The package class imported with the import is only used as the _bstr_t type as the BSTR parameter type, and in CCOMBSTR, _BSTR_T, BSTR, it is also a troublesome thing. So I wrote a CBSTRADAPT called a CBSTRADAPT in the CADAPT class.
Class CBSTRADAPT
{
_BSTR_T M_STR;
PUBLIC:
CBSTRADAPT (Const _BSTR_T & STR)
{
m_str = Str;}
CBSTRADAPT (Const BSTR STR)
{
m_str = STR;
}
CBSTRADAPT (Const CCOMBSTR & STR)
{
m_str = str.m_str;
}
Operator BSTR ()
{
Return m_str.getbstr ();
}
Operator_BSTR_T ()
{
Return M_STR;
}
Operator ccombstr ()
{
Return CCOMBSTR (m_str.getbstr ());
}
}
Its main purpose is to complete the conversion from CCOMBSTR to _BSTR_T.
For example, there is a way to call:
Bool islogin (_BSTR_T UserID);
The calling code is as follows:
CCOMBSTR BSTRUSERID (L "007");
IF (Obj-> Islogin (CBSTRADAPT (BSTRUSERID))
{
......
}