Creating Applications with Mozilla-Chapter 8. XPCOM- P4

Chia sẻ: Thanh Cong | Ngày: | Loại File: PDF | Số trang:18

lượt xem

Creating Applications with Mozilla-Chapter 8. XPCOM- P4

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Tham khảo tài liệu 'creating applications with mozilla-chapter 8. xpcom- p4', công nghệ thông tin, kỹ thuật lập trình phục vụ nhu cầu học tập, nghiên cứu và làm việc hiệu quả

Chủ đề:

Nội dung Text: Creating Applications with Mozilla-Chapter 8. XPCOM- P4

  1. Chapter 8. XPCOM- P4 As you can see, the macro is made up of other macros that implement basic methods of the nsISupports interface. Unless you need to modify these macros, they should be left as is. This macro is used later on when we create our C++ component. Example 8-6 shows a reference implementation of the QueryInterface method in C++. Example 8-6. Reference implementation of QueryInterface NS_IMETHODIMP nsMyImplementation::QueryInterface( REFNSIID aIID, void** aInstancePtr ) { NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!"); if ( !aInstancePtr ) return NS_ERROR_NULL_POINTER; nsISupports* foundInterface; if ( aIID.Equals(nsCOMTypeInfo::GetIID( )) ) foundInterface = NS_STATIC_CAST(nsIX*, this);
  2. else if ( aIID.Equals(nsCOMTypeInfo::GetIID( )) ) foundInterface = NS_STATIC_CAST(nsIY*, this); else if ( aIID.Equals(nsCOMTypeInfo::GetIID( )) ) foundInterface = NS_STATIC_CAST(nsISupports*, NS_STATIC_CAST(nsIX*, this)); else foundInterface = 0; nsresult status; if ( !foundInterface ) { status = NS_NOINTERFACE; } else { NS_ADDREF(foundInterface); status = NS_OK; } *aInstancePtr = foundInterface; return status; } The results macros Since all XPCOM methods return result codes called nsresults, another useful macro is the NS_SUCCEEDED macro. This indicates whether an
  3. XPCOM accessor has returned a successful result. It is defined in nsError.h: #define NS_SUCCEEDED(_nsresult) (!((_nsresult) & 0x80000000)) A related macro, NS_FAILED, is indicates whether an XPCOM accessor returned a failure code result. It too is defined in nsError.h. The following code demonstrates the typical use of these two macros: nsresult rv; nsCOMPtr file(do_CreateInstance(";1", &rv)); if (NS_FAILED(rv)) { printf("FAILED\n"); return rv; } if (NS_SUCCEEDED(rv)) { printf(" SUCCEEDED \n"); return rv; } You may have noticed that the declaration of the identifier rv as the type nsresult. nsresult is a 32-bit unsigned integer declared in nscore.h: typedef PRUint32 nsresult;
  4. We assign an nsCOMPtr or smart pointer named file to a newly created instance of the nsILocalFile component. Using the NS_FAILED and NS_SUCCEEDED macros, we test for the nsresult to see if our attempt to create an instance of the component failed. If it did, rv would be assigned an integer with a specific error return code. Return codes are defined in nsError.h. Alternatively, you can test your results for the success code: nsresult rv = nsComponentManager::CreateInstance(" le/local;1", nsnull, NS_GET_IID(nsILocalFile), (void **)&refp); If a result is successful, the value of rv returns NS_OK, which is 0. Return codes are used in XPCOM instead of exceptions. Exceptions are not allowed because of their inconsistent implementation across different compilers. All error code numbers equate to a specific type of error. For example NS_ERROR_FAILURE and NS_ERROR_NULL_POINTER are common types of error code return values used throughout the Mozilla code base. If a value returned to rv was NS_ERROR_NULL_POINTER, the test for failure would be true and the code would return the numerical result code for NS_ERROR_NULL_POINTER. The nsnull type
  5. Another widely use type is nsnull, defined in nscore.h. Here is the definition: #define nsnull 0 This definition, nsnull, is the most common way to use null. The following code shows how to use nsnull: nsresult rv; nsCOMPtr file = do_CreateInstance(";1", &rv); if (NS_SUCCEEDED(rv)) { char* msg = "we successfully created an instance of file\n"; *_retval = (char*) nsMemory::Alloc(PL_strlen(msg) + 1); if (!*_retval) return NS_ERROR_OUT_OF_MEMORY; PL_strcpy(*_retval, msg); } else { *_retval = nsnull; } The NS_IMETHODIMP macro If you look in the Mozilla C++ source code, you will see the macro NS_IMETHODIMP used frequently. This macro identifies the type of your
  6. interface implementation method. It is also defined in nscore.h, as shown in Example 8-7. Example 8-7. Platform macros in xpcom/base/nscore.h #define NS_IMETHODIMP NS_IMETHODIMP_(nsresult) #ifdef NS_WIN32 #define NS_IMETHODIMP_(type) type _ _stdcall #elif defined(XP_MAC) #define NS_IMETHODIMP_(type) type #elif defined(XP_OS2) #define NS_IMETHODIMP_(type) type #else #define NS_IMETHODIMP_(type) type #endif Example 8-8 shows a typical use of the NS_IMETHODIMP macro. All methods that implement an interface are of the type NS_IMETHODIMP. Example 8-8. NS_IMETHOD macro NS_IMETHODIMP nsMyImpl::GetSomeString(char** _retval) { nsresult rv; nsCOMPtr file =
  7. do_CreateInstance(";1", &rv); if (NS_SUCCEEDED(rv)) { char* msg = "we successfully created an instance of file\n"; *_retval = (char*) nsMemory::Alloc(PL_strlen(msg) + 1); if (!*_retval) return NS_ERROR_OUT_OF_MEMORY; PL_strcpy(*_retval, msg); } else { *_retval = nsnull; } return NS_OK; } The macro in Example 8-8 declares the method GetSomeString as an XPCOM implementation. nsCOMPtr smart pointer As described earlier, XPCOM provides a C++ tool called a smart pointer to manage reference counting. A smart pointer is a template class that acts syntactically, just like an ordinary pointer in C or C++. You can apply * to dereference the pointer, ->, or access what the pointer refers to. Unlike a raw COM interface pointer, however, nsCOMPtr manages AddRef,
  8. Release, and QueryInterface for you, thereby preventing memory leaks. Here is how to create a raw pointer: nsILocalFile *refp(nsnull); nsresult rv = nsComponentManager::CreateInstance(" le/local;1", nsnull, NS_GET_IID(nsILocalFile), (void **)&refp); if (refp) printf("%p\n", (void*)refp); After you create a new object that refp points to, refp is considered an owning reference, and any other pointers that point to it must be "refcounted." Example 8-9 uses anotherPtr and oneMorePtr to point to refp, and manually manages AddRef and Release. Example 8-9. Manual reference counting using raw pointers nsILocalFile *refp(nsnull); nsresult rv = nsComponentManager::CreateInstance(" le/local;1", nsnull, NS_GET_IID(nsILocalFile),
  9. (void **)&refp); nsILocalFile *anotherPtr = refp; NS_IF_ADDREF(anotherPtr); // increment refcount nsILocalFile *oneMorePtr = refp; NS_IF_ADDREF(oneMorePtr); // increment refcount if (!someCondition) { NS_RELEASE(anotherPtr); // decrement refcount return NS_OK; } . . . NS_RELEASE(anotherPtr); // decrement refcount NS_RELEASE(oneMorePtr); // decrement refcount return NS_OK; } In Example 8-9, if someCondition is false, anotherPtr is released and the function then returns (NS_OK). But what about oneMorePtr? In this instance, it is never released; if you remember, an object cannot be released from memory until our refcount is at zero. The refcount is out of sync, oneMorePtr is never decremented before the return, and the object is thus left dangling in memory. With the refcount off, the object leaks. Remember that Release( ) calls the C++ delete operator to free up the allocated XPCOM object only when the count is decremented to 0. If Release thinks there are still references to the object because the
  10. refcount hasn't been properly decremented, delete is never called. The correct code is shown below: if (!someCondition) { NS_RELEASE(anotherPtr); // decrement refcount NS_RELEASE(oneMorePtr); // decrement refcount return NS_OK; } As you can see, manual management of reference counting is prone to error. To alleviate this burden and extra code bloat, nsCOMPtr implements AddRef and Release for you and makes life much easier. Before the nsCOMPtr class is removed from the stack, it calls Release in its destructor. After all references are properly released, delete is called and the object is freed from memory. Example 8-10 shows a typical use of nsCOMPtr. Example 8-10. Using nsCOMPtr in your code nsCOMPtr refp = do_CreateInstance(";1"); nsCOMPtr anotherPtr = refp; nsCOMPtr oneMorePtr = refp; nsresult rv; if (!someCondition) return NS_OK; . . .
  11. //no need to release here because nsCOMPtr smart pointer's destructor // will call release automatically and the above references will be // properly decremented. return NS_OK; Wherever the code returns, all pointers holding references to the nsLocalFile XPCOM object are released automatically in the nsCOMPtr class destructor before the instructions are removed from the stack. By letting nsCOMPtr manage AddRef and Release for you, you remove a margin for error, code complexity, and bloat. 8.2.5. C++ Implementation of nsISimple Now that you have seen some of the C++ tools you need for XPCOM, you can turn to an actual implementation. Earlier in this chapter, the section Section 8.2.1 showed you how to create an interface and implement it in JavaScript. However, you may need a C++ implementation to benefit from the better performance offered by a compiled language. Most components used in Mozilla are written in C++. This section discusses how to create a C++ implementation for the nsISimple interface. A few more steps are involved, but as you will see, they are generally similar to the processes described in the JavaScript component section, facilitated to some extent by the available tools and templates discussed previously. Creating a C++ component
  12. First, you must find a good place to put the source file you create for the component. In your local Mozilla source tree, mozilla/xpcom/sample/ is a great place to start because it's the directory in which the sample XPCOM interface and implementations already reside. First, create a new directory and call it simple: $ mkdir simple $ cd simple You can place the nsISimple interface you created earlier in this new directory as a file called nsISimple.idl: #include "nsISupports.idl" [scriptable, uuid(ce32e3ff-36f8-425f-94be- d85b26e634ee)] interface nsISimple : nsISupports { attribute string yourName; void write( ); void change(in string aName); }; Once you have the interface source file in which the attribute yourName and the methods write( ) and change( ) are defined, you can create a header file for the implementation source file. nsISimple C++ header file
  13. Earlier, you created the type library nsISimple.xpt for the JavaScript component and installed it in the components subdirectory. Since we've already covered those steps, we can move forward to generating a C++ header file. To create a C++ header file from your original IDL, run your IDL file through the xpidl compiler: $ xpidl -m header -w -v -I $XPIDL_INC \ > -o nsISimple nsISimple.idl The generated file is nsISimple.h and is shown in Example 8-11. Example 8-11. nsISimple header file generated by xpidl compiler /* * DO NOT EDIT. THIS FILE IS GENERATED FROM nsISimple.idl */ #ifndef _ _gen_nsISimple_h_ _ #define _ _gen_nsISimple_h_ _ #ifndef _ _gen_nsISupports_h_ _ #include "nsISupports.h" #endif /* For IDL files that don't want to include root IDL files. */ #ifndef NS_NO_VTABLE #define NS_NO_VTABLE #endif
  14. /* starting interface: nsISimple */ #define NS_ISIMPLE_IID_STR "ce32e3ff-36f8-425f- 94be-d85b26e634ee" #define NS_ISIMPLE_IID \ {0xce32e3ff, 0x36f8, 0x425f, \ { 0x94, 0xbe, 0xd8, 0x5b, 0x26, 0xe6, 0x34, 0xee }} class NS_NO_VTABLE nsISimple : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_ISIMPLE_IID) /* attribute string yourName; */ NS_IMETHOD GetYourName(char * *aYourName) = 0; NS_IMETHOD SetYourName(const char * aYourName) = 0; /* void write ( ); */ NS_IMETHOD Write(void) = 0; /* void change (in string aName); */ NS_IMETHOD Change(const char *aName) = 0; }; /* Use this macro when declaring classes that implement this interface. */ #define NS_DECL_NSISIMPLE \ NS_IMETHOD GetYourName(char * *aYourName); \
  15. NS_IMETHOD SetYourName(const char * aYourName); \ NS_IMETHOD Write(void); \ NS_IMETHOD Change(const char *aName); /* Use this macro to declare functions that forward the behavior of this interface to another object. */ #define NS_FORWARD_NSISIMPLE(_to) \ NS_IMETHOD GetYourName(char * *aYourName) { return _to ## GetYourName(aYourName); } \ NS_IMETHOD SetYourName(const char * aYourName) { return _to ## SetYourName(aYourName); } \ NS_IMETHOD Write(void) { return _to ## Write( ); } \ NS_IMETHOD Change(const char *aName) { return _to ## Change(aName); } /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */ #define NS_FORWARD_SAFE_NSISIMPLE(_to) \ NS_IMETHOD GetYourName(char * *aYourName) { return !_to ## ? NS_ERROR_NULL_POINTER : _to ##- >GetYourName(aYourName); } \
  16. NS_IMETHOD SetYourName(const char * aYourName) { return !_to ## ? NS_ERROR_NULL_POINTER : _to ##- >SetYourName(aYourName); } \ NS_IMETHOD Write(void) { return !_to ## ? NS_ERROR_NULL_POINTER : _to ##-> Write( ); } \ NS_IMETHOD Change(const char *aName) { return !_to ## ? NS_ERROR_NULL_POINTER : _to ##-> Change(aName); } #if 0 /* Use the code below as a template for the implementation class for this interface. */ /* Header file */ class nsSimple : public nsISimple { public: NS_DECL_ISUPPORTS NS_DECL_NSISIMPLE nsSimple( ); virtual ~nsSimple( ); /* additional members */ }; /* Implementation file */ NS_IMPL_ISUPPORTS1(nsSimple, nsISimple)
  17. nsSimple::nsSimple( ) { NS_INIT_ISUPPORTS( ); /* member initializers and constructor code */ } nsSimple::~nsSimple( ) { /* destructor code */ } /* attribute string yourName; */ NS_IMETHODIMP nsSimple::GetYourName(char * *aYourName) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSimple::SetYourName(const char * aYourName) { return NS_ERROR_NOT_IMPLEMENTED; } /* void write ( ); */ NS_IMETHODIMP nsSimple::Write( )
  18. { return NS_ERROR_NOT_IMPLEMENTED; } /* void change (in string aName); */ NS_IMETHODIMP nsSimple::Change(const char *aName) { return NS_ERROR_NOT_IMPLEMENTED; } /* End of implementation class template. */ #endif #endif /* _ _gen_nsISimple_h_ _ */
Đồng bộ tài khoản