Walkthrough: Creating a COM Server Using a Text Editor

Attributes were designed to ease the tedium and complexity of traditional COM programming. Using attributes in your applications can automate or simplify several common COM procedures. In this walkthrough you will develop a simple COM server with a text editor and the command-line tools.

To use attributes in your applications, you must add support for them by defining several related macros (such as _ATL_ATTRIBUTES) and including related header files.

To create the header file

  1. Open Notepad or your text editor.

  2. Create a new file called MyIncludes.h.

  3. Add the following code:

    #pragma once
    #define STRICT
    #ifndef _WIN32_WINNT
    #define _WIN32_WINNT 0x0400
    #endif
    #define _ATL_ATTRIBUTES
    #define _ATL_APARTMENT_THREADED
    #define _ATL_NO_AUTOMATIC_NAMESPACE
    #include <atlbase.h>
    #include <atlcom.h>
    #include <atlwin.h>
    #include <atltypes.h>
    #include <atlctl.h>
    #include <atlhost.h>
    using namespace ATL;
    
  4. Save the file.

Include files beginning with "atl" bring in ATL support for the application, and with the definition of _ATL_ATTRIBUTES, support for attributed ATL programming is also added.

You will need to create a source file that implements the inproc server.

To create the source file

  1. In Notepad, create a new file called MyServer.cpp.

  2. Add the following code:

    #include "MyIncludes.h"
    // The module attribute is specified in order to implement DllMain,
    // DllRegisterServer and DllUnregisterServer
    [ module(dll, name = "MyServer", helpstring = "MyServer 1.0 Type Library") ];
    [ emitidl ];
    
  3. Save the file.

Using the module attribute saves you the work of implementing the necessary functions required for a COM inproc server. In addition, you do not need to write a .idl file or the .def file (which exports the functions of your server). This is all done automatically by the module attribute.

To build the project

  • At the command line, enter the following commands:

    cl /LD MyServer.cpp
    regsvr32 MyServer.dll
    

If you encounter any errors, then make sure you have Visual Studio properly installed and the environment variables defined. The Visual Studio environment variables are automatically defined when you run the Visual Studio Command Prompt, or they can be set manually by running vsvars32.bat found at <Visual Studio Installation Directory>\Common7\Tools\vsvars32.bat.

Building the project successfully registers the server with the operating system.

At this point, the COM server does not expose any functionality to the client. This is because the COM server does not implement any server objects. In the next step, you will add server objects to this server.

To add server objects to the server

  1. Open MyServer.cpp and add the following code:

    /////////////////////////////////////////////////////////////////////////////
    // IObject1
    [
       object,
       uuid("103FF9D9-8BC9-4ea8-8CD4-C1E627D04358"),
       dual,
       helpstring("IObject1 Interface"),
       pointer_default(unique)
    ]
    __interface IObject1 : IDispatch
    {
       HRESULT GetANum([out, retval]int* pInt);
    };
    /////////////////////////////////////////////////////////////////////////////
    // CObject1
    [
       coclass,
       threading(apartment),
       vi_progid("MyServer.Object1"),
       progid("MyServer.Object1.1"),
       version(1.0),
       uuid("15615078-523C-43A0-BE6F-651E78A89213"),
       helpstring("Object1 Class")
    ]
    class ATL_NO_VTABLE CObject1 : 
       public IObject1
    {
    public:
       CObject1()
       {
       }
       HRESULT GetANum(int* pInt){
          *pInt = 101;
          return S_OK;
       }
       DECLARE_PROTECT_FINAL_CONSTRUCT()
       HRESULT FinalConstruct()
       {
          return S_OK;
       }
    
       void FinalRelease() 
       {
       }
    };
    
  2. Save your changes and examine the new code.

The code creates a simple COM object (CObject1) that implements a custom interface (IObject1) with a single method. This method (GetANum) returns the value of a data member (of type int).

Save your changes and rebuild the application, using the commands from the previous step:

cl /LD MyServer.cpp
regsvr32 MyServer.dll

To determine if the server works, a test client application must be written.

To create a test client application

  1. Open Notepad and create a new file named Comtest.cpp.

    Comtest.cpp needs to be in the same folder as MyServer.cpp.

  2. Add the following code to Comtest.cpp:

    #include <iostream>
    #include "atlbase.h"
    #import "vc90.tlb" no_namespace
    using namespace std;
    int main()
    {
       CoInitialize(NULL);
       {
          CComPtr<IUnknown> spUnknown;
                spUnknown.CoCreateInstance(__uuidof(CObject1));
          CComPtr<IObject1> pI;
          spUnknown.QueryInterface(&pI);
          int res = 0;
          res = pI->GetANum();
          cout << res << endl;
       }
       CoUninitialize();
    }
    

For the client to know what the server has to offer, you must import the type library of the server by using the #import keyword. Because no specific name was given when the server was created, the compiler generated a type library with a default name (vc90.tlb). The following line in the code previously shown imports the library:

#import "vc90.tlb" no_namespace

When this file is imported into the test application, the client has access to the type information in the COM server. This allows the test application to refer to any types defined within (such as CObject1 and IObject1).

To build and run the test application

  1. At the command line, enter the following command:

    cl /EHsc comtest.cpp
    
  2. Run the application by entering the following command:

    comtest
    

You will see the integer value being printed.

See Also

Concepts

Developing a COM DLL with COM Attributes

Walkthrough: Creating a COM Server Using Wizards