Ambassador

The Royal C++ Embassy

Subscribe to RSS feed

Posts tagged with "COM"

Software Development Kits, Libraries and Components

, ,

We all need some software development kits, libraries or some components (like COM or .NET's ones) to do the work we need.

Throughout our history, operating systems provided their APIs in C. C was a universal language for developers using any language. For instance, Perl people have written Perl modules consuming C libraries, C++ people did the same, so did Delphi guys. C is taught in universites (I'm not sure whether they still do this) and even a Delphi programmer (should?) knows C, just a little.

Companies have made up their own, internal libraries and managed these as assets. Some companies made products and provided their own APIs to third parties so that they can extend application's abilities or reuse the application in their own package.

Most of C++ libraries are just wrappers on some C functions. Because this seems like the best way, first of all. A native, pure C++ library can be very difficult to maintain and reused by third parties. There are lots of reasons, but let's start with some simple ones.
  • C++, in most cases, requires classes' declarations to be shipped (header files, containing class declarations).
  • C++ is more type-safe than C, so vendor should provide more information about the class, try to stick on type safety or use casts, while C API can just return a "handle" (void*, may be?) and "implicitly cast it".
  • Compiled C++ object code is less portable/compatible across different compilers (name decoration, or, mangling? vtable layout? multiple inheritance layout? exceptions?)
  • One little change in a class changes its layout, so vendor should send updates to customers and all customers should rebuild their projects, re-deploy to their customers.
  • Everybody has his/her own coding style and may not like others' style.
  • There are bunch of well-known techniques that are then named "patterns" (somebody once came and said "so you used Factory pattern?", I didn't know what was factory pattern, I thought that approach fits best to my design).
  • Not all compilers support whole ISO/IEC 14882 standard, so vendor should consider obeying standard and compromise some cool features, like exceptions and templates (which could also allow customers to see majority of the library code).

C APIs, on the other hand, have some good sides:
  • Return a handle, request a handle - no need any implementation detail.
    typedef void*    HDATA;  // some data "handle"
    
    HDATA CreateData(const wchar_t** pwszArguments, int iArgs);
    int DecodeData(HDATA h, wchar_t* pbuf, int cchbuf); // use handle returned by above function
    
  • No name decoration. So "void foo()" will be "foo", unlike its decorated version C++, like "??QV?foo?WZW". Therefore, it's far more portable, as there is no standard way for name decoration in ISO/IEC 14882.
  • No "exception" or run-time polymorphism concern, therefore more portable.
  • All languages can access simply, as C is more solid from standard point of view.
  • Everybody can read, or at least can use, C code.
  • In most cases, installing dynamic library solves problem, without rebuilding everything. It's not always the case, however.
  • Few terminologies, no "containment", "aggregation", "inheritance", "some really cool pattern", making C easier to write and reuse.
  • Many more.

This is not a C++ bashing, but these are the facts. Object-oriented programming is not the purpose, it's a technique that we use to solve some sort of big problems. We should not mix the purpose with the tools we use to achieve the goal; they are distinct subjects.

A vendor can provide a C API, but use C++ code in its implementation. This is a pretty common technique, which is still being used today.
class DataClass
{
public:
    void foo();
    int bar();
    DataClass* baz();
};

HDATA CreateData(const wchar_t** pwszArguments, int iArgs)
{
      HDATA h;
      DataClass* pcls;
      // do something with pcls
      h = (HDATA)pcls;
      return h;  // return pointer to a DataClass object.
}

int DecodeData(HDATA h, wchar_t* pbuf, int cchbuf)
{
      DataClass* pcls = (DataClass*)h;
      return pcls->DecodeData(pbuf, cchbuf);
}

This technique prevents any confusions, both in development and consuming a library.

Polymorphism
This is an important subject, as in some cases customer may want to alter some particular behavior of a particular type and API supports polymorphism to allow customer's code to override some behavior (like in bad old MFC).

In C++, we have two types of polymorphism, as you may already know; the run-time polymorphism, through virtual functions and compile time polymorphism through templates.
template<typename T>
class SomeClass
{
public:
     bool some_method(T& t)
     {
         // do something
     }
     void foo(T& t)
     {
        // ...
     }
};

typedef std::vector<int> MyVectorType;
MyVectorType g_vec;

void foo(int i)
{
     SomeClass<MyVectorType::value_type> sc;
     some_method(g_vec[0]);
}

The std::vector itself was polymorphic enough, but having one additional delicate sample is harmless.

The well-known inheritance based run-time polymorphism can be slightly complicated, too - never mind how complex will objects look like in memory.
class Base
{
     virtual void foo() = 0;
     virtual void bar();
public:
     void f();
};

class Derived
{
     // implicit virtual
     void foo()
     {
     }
public:
     void f();
};

void test(Base* pbase)
{
   pbase->foo();
   pbase->bar();
   pbase->f();
}

void polycall()
{
   Derived d;
   test(&d);
}

Hmmm...


Microsoft has asked me "what is a virtual function" during an interview. I have given the explanation in the ISO/IEC 14882 - simply, call goes to final overrider - but in my opinion, the correct answer is "depends on how 'deep' you know". For above example:
  • Base::foo is virtual, so calls can propagate to final overrider, which is Derived::foo, in our case. Note that Derived::foo is an implicit virtual function. If a base class declares a function with exact name and argument list and exact or a covariant return type, the function is virtual in derived classes, regardless of virtual qualifier in declaration of function in derived class.
  • Base::bar is virtual, so calls can propagate to final overrider, too, but Derived does not override Base::bar, therefore call does not propagate Derived.
  • Base::f is not virtual, so calls can't propagate and all Derived::f does is "hiding" base's method. In this case, test function will call Base::f and not Derived::f.

This technique was quite heavily used in famous MFC library. The former, template based (static?) polymorphism is heavily used in WTL, which generates tighter and smaller code.

Addictions, habits and mentality
There are people who like their wrist watches to be on their left wrist and there are who like otherwise. Some like red cars, some black, some white. Much like different tastes in other areas in our lives, we have habits and "tastes" in software world, too! Not everybody likes templates, not everybody likes multiple-inheritance, etc. When it comes to C++, number of tastes and ways increase mind bogglingly. This is where customer starts disliking, hating and swearing vendor or author, architect of API, and it goes until Bill Gates. Never mind that almost every vendor has its own string class that is not compatible with other string classes.
// Vendor code
class DataAcquirer
{
public:
    // can conflict with customer's code?
    DataAcquirer& operator<<(int i);

    // can customer refuse/modify this data?
    virtual void OnDataArrived(const Data& rdata);

    // yet another string class... perhaps, this is "more efficient"...
    VendorString GetDataSource();

    // did they override operator new, too??
};

// too complicated for average C++ programmer.
// may be nightmare for new graduates!
template<typename T, template<class, class> class TSomeContainer, typename TTraits = SomeTraits>
class SomeTemplate : public SomeBase<T, TSomeTraits>
{
   TSomeContainer<T, LibraryAllocator<T> > m_arr;
public:
   typedef typename SomeBase<T, TSomeTraits>::some_type some_type;
};


As a conclusion, I happen to believe making a C++ SDK and happy customers seems rather difficult.

COMponents
I've always said that COM (or an equivalent way) was a perfect way to develop and use SDK. Vendor provides a (bunch of) DLL(s), customer either plugs component or writes some abstraction over it or just use it as-is, by modifying a few properties. They are cute, nice, solid black boxes. Talking about COM;
  • Customer usually does not need a header file, compiler can import type library from DLL.
  • There is no non-standard C++ name mangling.
  • Uses reference counting, less pain to manage memory (we have smart pointers!).
  • It forces "pimpl idiom" on those who want to override particular behavior of component.
  • It has a set of standards (well, COM is a set of standards)
  • C users can (re)use, too (through vtable?)


Stupidity can be better
I believe that a library better be stupid. With stupid, I mean, it should provide details and not an abstraction salad or trying to do everything for me. Because this may leave small room for enhancements. For example, if you take standard C library, it does not try to do everything, but provides some functions, which you can use together and end up with an abstraction. Library provides low(er)-level processing, customer (or user) uses them together and ends up with an abstraction layer. Alternatively, vendor writes a complicated function that has various parameters and flags that behaves different for each case. This is, however, rather less flexible than lower-level functionality.

Clear documentation and one consistent one baseline
Documentation is what customer needs. Customer has to know which conditions are error, which are successful, who is allocating buffers, whether call is thread-safe or does it acquire any lock, etc. In Win32 API, for example, there are some functions where caller provides a NULL pointer to learn how much memory should be allocated to store information. So customer makes two calls; first, to get buffer size (check return value, too, it may be a completely different error!) and second, to do the actual work. This is not a consistent design, in my humble opinion.

Win32 API is very well documented. There are some COM interfaces, some components that are almost not documented, no sample, no nothing. I remember I have written wrappers around various DLLs to intercept calls and try to see how those particular objects work.

Conclusion
In my humble opinion, C is still the best programming language to write APIs.

"I cannot find import library for my GUIDs!"

, , ,

Visual C++ has got a keyword, specifically designed for COM projects; the "__uuidof". So, for example, if you want to instantiate a class with CLSID_Application and you need ISomeInterface, you'd normally do:
ISomeInterface *psi;
psi = NULL;

HRESULT hr = CoCreateInstance(CLSID_Application, NULL, 
                              CLSCTX_INPROC_SERVER,
                              IID_ISomeInterface,
                              (void**)&psi);

If you get a link error for IID_ISomeInterface and CLSID_Application despite the fact that you have essentially tried to link all import libraries suggested in any piece of documentation, try:
HRESULT hr = CoCreateInstance(__uuidof(Application),
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              __uuidof(ISomeInterface),
                              (void**)&psi);

Trick is that this operator returns GUID of given expression, if any provided. Of course, I am talking about "normal" scenerios, like:
#if defined(__cplusplus) && !defined(CINTERFACE)
  MIDL_INTERFACE("00000000-0000-0000-C000-000000000046")
  IUnknown
  {
  public:
    BEGIN_INTERFACE
    virtual HRESULT STDMETHODCALLTYPE QueryInterface
            (REFIID riid, void **ppvObject) = 0;
    virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;
    virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
    END_INTERFACE
  };
#else   /* C style interface */
  typedef struct IUnknownVtbl
  {
    BEGIN_INTERFACE
    HRESULT (STDMETHODCALLTYPE *QueryInterface)(IUnknown *This,
             REFIID riid, void **ppvObject);
    ULONG (STDMETHODCALLTYPE *AddRef)(IUnknown *This);
    ULONG (STDMETHODCALLTYPE *Release)(IUnknown *This);
    END_INTERFACE
  } IUnknownVtbl;

  interface IUnknown
  {
    CONST_VTBL struct IUnknownVtbl *lpVtbl;
  };
#endif

The "MIDL_INTERFACE" macro specifies GUID for the interface (which is IID_IUnknown).
Or (stolen from Microsoft's sample, see critics below):
//  expre_uuidof.cpp 
//  compile  with:  ole32.lib 
#include  "stdio.h" 
#include  "windows.h" 

[emitidl]; 
[module(name="MyLib")]; 
[export] 
struct  stuff  { 
    int  i; 
}; 

 int  main()  { 
    LPOLESTR  lpolestr; 
    StringFromCLSID(__uuidof(MyLib),  &lpolestr); 
    wprintf_s(L"%s",  lpolestr); 
    CoTaskMemFree(lpolestr); 
}

Found a few problems with the sample:
1. C or C++ code cannot be compiled with a lib, but can be linked. So instead of "compile with", it should be "link with"
2. #include "stdio.h" does not seem good. It's supposed to be #include <stdio.h> (mind <>, please)

Or, alternatively, if you import:
#import "SomeLibrary.dll" no_namespace raw_interfaces_only

In fact, as far as I remember, when you import, you can specify "named_guids" option to import directive, which will generate CLSID_SomeStuff in type library header. However, I don't remember whether this requires import library at link stage.

There is some really cool stuff going on here!

, , , ...

It's been awhile I have moved to another branch in devices department, I am not a part of Windows Mobile team for sometime, although I still live in Windows Mobile team's habitat. I and some more coworkers here do some really cool stuff. What we do is secret smile, but really cool stuff.

As you can see from Opera Job Openings, we are running on Windows CE as well. Windows CE is not a Windows XP, as you may already know. This sometimes requires tremenedous amount of work to be done to get a product equivalent to desktop quality - it's a real challenge. In other words, writing serious portions of Win32 API on Windows CE. There are some pitfalls at this re-invention (or porting, or you name it) work. Because there are functions and arguments declared in SDK but are not actually supported on Windows CE. There are functions that do not run like their Win32 equivalents. This requires developer to re-refresh his or her knowledge about Win32 API functions and WinCE API functions. There are lots of stuff to care about on Windows CE - it's a real-time operating system, it's got less memory, less storage, slower CPU, turns out a tough job. So we should be far more careful about resource management, such as threads, messaging, painting, allocations and similar stuff than desktop developers. I don't say they don't respect such issues, but what I am saying is we, for example, should certainly avoid thread thrashing.

Do you think Opera does not care about COM (Component Object Model)? Largely true smile but if you are dealing with Windows, you need to get your feet wet in COM sea. We do some really cool stuff with COM, too. It becomes even more fantastic on devices, since COM is more restricted and minimalistic than desktop or server operating systems.

These are "real engineers" - not just graduates! Few days ago, a group of developers and QA people were talking about new coffee machine. Well, new coffee machine is not your father's coffee machine - it's huge and has got an LCD screen, giving all possible information about the status. This, of course, has grabbed attention and people started wondering complexity of coffee machine. I stood a few steps behind the investigation team, with a cup in my hand, waiting for them to complete their job. I actually thought they are awaiting to get coffe. There are two coffee "outputs" and one hot water output from the "device". Then I have realized that they actually are pressing buttons, reading screen and taking closer look at it! Fortunately, investigation team has received a critical advice; "do not test a coffee machine with standing that close and no cups" from an elder. Filing a bug report about new machine may look like:
Summary: Over-complicated coffee machine for average user 
may cause serious injury.
Module: coffemachine
Module Owner: Opera
Description: When user stands too close to the device 
and watches LCD screen after pressing buttons, coffee
 machine may spray 120 degrees hot water out, which 
could cause severe damage on the user.
Repro: (Do not stand too close to the machine before 
starting test.) Ensure there is no cup "plugged to 
coffee output", Press the button in the middle, press
 the red button.
Resol: Don't play with buttons, and if you do, place 
a cup - just in case.


Fortunately, coffee machine does not hurt anybody without his/her intervention. But I certainly would not want to experience "crash of coffee machine".

Using COM to build an extremely flexible application (framework)

, , , ...

I am a COM fan!party Few years ago, I have written a sample application for my previous employer. It begun as a simple "Internet Explorer hosting" project, but then it's changed. It became a pretty complex, plugin-enabled IE hosting application which we then wanted use as an Internet Kiosk. Company, however, did not want to use the code; "it's impossible for us to manage it", they said. Actually, I believe that it was one of my toughest jobs. It was using private inheritance, private virtual functions, RAII pattern, COM, type discovery and similar stuff.

"Hey, in .NET, such things are far simple!". Could be, but:
  • I don't use .NET
  • I don't like .NET
  • I don't like to be restricted by a stupid run-time
  • With COM and C++, I have got enough run-time support
  • My code is far faster
  • Can be used on FreeBSD with my UBS library (emulate - not all, but - some COM stuff)
  • CLR garbage collection is not compatible enough to deal with COM objects, AFAIK (they may claim it is, but I believe it certainly is not suitable for complex COM scenerios)


Idea
Application consists of some components, which may or may not exist in more than one DLL file. All these components are COM objects. Following is a short list of components used in this idea:

  • Application Logic
  • UI Manager
  • Module Manager


Application Logic (IAppLogic)
This is application business-logic file. In other words, this is the "brain" of the application. It knows everything about application, what to do, when to do and how to do.

UI Manager
Manages user interface stuff. Starting from top-level window, this component is responsible from windows, message loop(s), message handling and providing feedback to IAppLogic, acting as a gateway between user and application logic.

Module Manager
This component is responsible from loading and unloading modules, managing their interactions with each other, helping marshaling (when required), providing various helper functions and provides an export library for some GUIDs (during modules' link time).

Note that this technique does not use well-known CoCreateInstance. Instead, it "implements" CoCreateInstance without registration - modules are not required to be registered.

When application starts, it tries to enumerate DLLs in "modules" folder. After collecting the list, application tries to find a DLL that has got a coclass for "ICustomModuleManager" interface. When one is found, it calls DllGetClassObject and gets the IClassFactory for ICustomModuleManager interface. When IClassFactory is obtained, it instantiates an ICustomModuleManager and immediately calls ICustomModuleManager::Initialize.

Module manager may has a configuration file, in XML, that specifies details about modules. If none exists, module manager does a job much like calling application did (through module manager, anyway) - enumerate DLLs, ask for IAppLogic interface. Rest is a bit complicated. This is why using an XML file is better.

  • Ask module task GUID
  • Collect objects with matching task GUID
  • Instantiate IAppLogic interfaces
  • Call IAppLogic::Initialize for each IAppLogic


IAppLogic contains basic information about UI. If IAppLogic requires a UI, it exposes this information through get_UIManager method.
interface IAppLogic : IUnknown
{
    // ...
    virtual HRESULT STDMETHODCALLTYPE get_UIManager(LPVARIANT lpvaruim) PURE;
    // ...
};
This function returns:
  • VT_BSTR, this could be:
    1 - Path of UI manager DLL
    2 - CLSID or ProgId of UI manager.
  • VT_UNKNOWN, if it already has got a pointer to IUIManager interface.

The helper function
HRESULT GENAPI CreateUIManager(LPVARIANT, IUIManager**, HWND);

helps a lot to decode LPVARIANT returned from get_UIManager. In other words, this function takes required action and returns an IUIManager*.

IUIManager::Initialize(HWND)
This method initializes UI manager and creates window for IAppLogic. If hwnd is NULL, UI manager creates a popup window and other windows as required.

IUIManager::Initialize kicks-off a thread which creates window(s). Therefore, it's not the calling thread that owns the window(s). This is an important detail. Other modules or threads - simply, "foreign code", should talk to UI through "thread messages" and not through window messages. Thread handle is stored as a member. Caller may wait for UI event, filter window messages or wait whole UI application to close. IUIManager provides following methods to allow other modules to interact with UI.

IUIManager::WaitForClose
Waits until all popup windows created with IUIManager::Initialize method destroy. After windows are destroyed, this function returns.

IUIManager::WaitForMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, ULONG ulWaitFlags, ULONG ulTimeout)
Waits for particular message, window and arguments. Wait behavior (i.e. for particular message or for particular window, or particular message sent to a particular window) can be defined with ulWaitFlags.

IUIManager::EnumUIElements(IEnumUIElement**)
Returns an enumerator to enumerate sibling UI elements. Note that this enumerator contains only parent windows created with IUIManager. In other words, this enumerator does not return children of windows created by IUIManager. Children are accessible through GetWindow Windows API function.

IUIManager provides few more helper functions. So, basically, what an application does is:
#include <genapi.h>
#pragma comment(lib, "genapi32.lib")

HRESULT LoadModules();
HRESULT MMCreateInstance(LPCTSTR lpszModuleName, ICustomModuleManager**, LPVARIANT lpvargs, ULONG ulVarCount);

int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
   MSG msg;
   HANDLE* phmodwait;
   UINT umodcount;

   CoInitialize(NULL);
   umodcount = LoadModules();
   if (umodcount < 1)
   {
      CoUninitialize();
      return 1;
   }
   phmodwait = (HANDLE*)LocalAlloc(LPTR, sizeof(HANDLE) * umodcount);
   for (UINT umod = 0; umod < umodcount; umod++)
   {
       // for each module, ask for wait handle and set it in phmodwait.
   }
   WaitForMultipleObjects(umodcount, phmodwait, TRUE, INFINITE);
   LocalFree(phmodwait);
   CoUninitialize();
   return 0;
}

HRESULT LoadModules()
{
    // find all dlls in "modules" sub-folder
    for (int i = 0; i < dllcount; i++)
    {
        // for each module, find ICustomModuleManager
        if (HasModuleManager(lpszModuleNames[i])
        {
             ICustomModuleManager* pcmm = NULL;
             if (MMCreateInstance(lpszModuleNames[i], &pcm, NULL, 0))
             {
                   // failed
             }
             // initialize is called by MMCreateInstance.
             // pcmm->put_ConfigFile(L"config.xml"); // if you have one
             pcmm->CollectTasks();
             // for each task, create IAppLogic, and call IAppLogic::Initialize
        }
    }
}

Well, what we did want to do was similar to this model, though it's not an exact copy (some portions of the code MAY belong to the company, although they actually do not have it). Well, my intention is not giving you whole source code, but an idea. I can disclose some parts of the code, which I have already had before working on this, at home for self-training. What did we do with this model was more than IE hosting. It allows shell viewing, and it certainly was a better IE. I have added lots of fancy stuff to this logic, such as DirectX stuff. When a page is loading, it's faded to gray and an animation with little transparency plays over the IE window.
February 2012
S M T W T F S
January 2012March 2012
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29