COM for Windows CE

      The current generation of Windows CE operating systems supports only a simplified subset of the COM services found in Windows 95. Specifically, COM under Windows CE currently has the following limitations:
  • No support for out-of-process components
  • No support for marshaling or method remoting
  • No apartment model or equivalent mechanism to ensure that access to COM objects is synchronized or thread-centric
      In addition, Windows CE 2.01 (used in the first-generation Palm-size PC) does not support ActiveX controls, IDispatch, or the Automation data types.

    Since Windows CE supports only in-band (do-it-yourself) concurrency control, a thread that wants to invoke COM objects must use the COINIT_MULTITHREADED flag when calling CoInitializeEx:

   HRESULT hr = ::CoInitializeEx(NULL,   
                                 COINIT_MULTITHREADED);
In case you're wondering, the legacy CoInitialize API is undefined under Windows CE, and will thus result in a compile-time error if used. Attempting to pass the COINIT_APARTMENTTHREADED flag to CoInitializeEx will result in an E_INVALIDARG error.

    The net effect of the lack of out-of-band support for concurrency management is that you are completely responsible for synchronizing access to your objects. ActiveX controls, which have thread affinity due to their use of thread-specific resources, should not be accessed by multiple threads. The common practice of using an apartment threaded singleton to synchronize data shared among multiple client threads does not work under Windows CE. You must instead make explicit calls to thread-synchronization primitives to protect member and global data.

    As with the desktop version of COM, a thread may call CoCreateInstance or CoGetClassObject to create a new instance of a component. Under Windows CE, the dwClsContext parameter in those calls must be set to CLSCTX_ INPROC_SERVER. In my experience, despite what the documentation says, Windows CE 2.01 will allow you to activate in-process COM objects by specifying CLSCTX_ ALL or CLSCTX_SERVER, but all other versions of Windows CE will return an E_NOTIMPL error if the flag is not explicitly set to CLSCTX_INPROC_SERVER. Additionally, the COSERVERINFO parameter in the call to CoGetClassObject must be NULL since remote servers are not supported.

 // Error handling left to the imagination of the
 // skilled reader 2nd parameter must be
 // CLSCTX_INPROC_SERVER, third parameter must be NULL
 IClassFactory* pFactory = NULL;
 ::CoGetClassObject(CLSID_MyObject,
                    CLSCTX_INPROC_SERVER, NULL,
                    IID_ICLASSFACTORY, (LPVOID*)        
                    &pFactory);
 pFactory->CreateInstance(NULL,IID_IMyObject,
                          (LPVOID*) m_pObj);
      As the power and prevalence of Windows CE increases, it is likely that Microsoft will eventually introduce Windows CE support for out-of-process servers, remote servers, security, out-of-band concurrency management, and the apartment model. I've heard rumors that future versions of Windows CE will even support a subset of the functionality found in Windows NT Server's Microsoft Message Queuing Service (MSMQ)! With that in mind, you should avoid the temptation to cheat (passing language-specific data types, writing data to [in] parameters with a server method, passing object pointers directly from one thread to another, and so on) while developing COM components for Windows CE. Future versions of Windows CE may cause misbehaved components to break.

Desktop ActiveX Controls

    Once you've created a Windows CE-based ActiveX control, you'll need to provide a version of it that runs under Windows NT so that it can be used from within design tools (resource or form editors). Here are the steps to take to create a desktop version of your control:

  1. Temporarily rename the folder that contains your Windows CE-based ActiveX control. For example, if you had created a project named BuckDog, you'd temporarily rename it to something like BuckDogCE.
  2. Using the desktop equivalent of the wizard used to create your Windows CE-based ActiveX control, create a new project using the original project name. Thus, if I used the WCE ATL COM AppWizard to build my BuckDog ActiveX control, I'd now use the desktop version of that AppWizard (the regular ATL COM AppWizard) to create a project called BuckDog.
  3. With the exception of stdafx.cpp, delete all source files, headers and dependencies from the new, empty project (not the renamed one).
  4. Rename the new .dsp and .dsw files so that they won't conflict with the names of the Windows CE project files. Thus, you'd change BuckDog.dsp and BuckDog.dsw to something like BuckDog_Win32.dsp and BuckDog_Win32.dsw.
  5. Move the Win32® versions of the .dsp and .dsw files into the original project folder (the one that you renamed in step one) and change the name of the folder back to its original name.
  6. Using a text editor, open the Win32 .dsw file and change the internal reference to the .dsp file so that it points to the Win32 version of the file. Thus, in the BuckDog_Win32.dsw file, you'd change the reference to BuckDog.dsp to BuckDog_Win32.dsp.
  7. Using two instances of Visual C++, add all of the files found in the Windows CE project to the Win32 project. Thus, you'd open the BuckDog and BuckDog_Win32 projects in separate instances of MSDEV.EXE and manually add all of the files in the BuckDog project to the BuckDog_Win32 project.
  8. Build the Win32 version of the product, verifying that the output control name is the same name as the original project name, such as BuckDog.dll, not BuckDog_Win32.dll.
  9.     If necessary, add #define statements to the project source code to account for the differences between Windows NT and Windows CE. Beware of the fact that if you compile the project under Windows 95 or Windows 98 (which isn't supported by the toolkit), you'll run into potential problems due to the difference between Unicode and ANSI.