user2123957
user2123957

Reputation: 23

Building a C++ COM Object to generate a UUID

I have a need for a COM object that generates a GUID. I am a C# developer, but this will be deployed in a unix environment, so I'm thinking I need to build it in C++. This is my first Visual C++ project, and I'm having some trouble finishing it out.

Steps I've taken:

Created a new ATL project in Visual Studio (Dynamic-link library - no other options)

Right-click project -> Add class -> ATL Simple Object (Short name: GuidGenerator; ProgID: InfaGuidGenerator)

View -> ClassView -> IGuidGenerator -> Add Method (Method Name: Generate; Parameter Type: BSTR* [out]; Parameter Name: retGuid)

Added Boost to get the platform independent UUID generator.

// GuidGenerator.cpp : Implementation of CGuidGenerator

#include "stdafx.h"
#include "GuidGenerator.h"
#include <boost/lexical_cast.hpp>
#include <boost/uuid/uuid.hpp>            // uuid class
#include <boost/uuid/uuid_generators.hpp> // generators
#include <boost/uuid/uuid_io.hpp>         // streaming operators etc.

STDMETHODIMP CGuidGenerator::Generate(BSTR* retGuid)
{
    boost::uuids::uuid uuid = boost::uuids::random_generator()();
    std::string uuidStr = boost::lexical_cast<std::string>(uuid);
    //not really sure what to do from here. 
    //I've tried to convert to BSTR.
    //When I assign the resulting value to retGuid, I often get an error:
    //A value of type BSTR cannot be assigned to an entity of type BSTR*

    return S_OK;
}

Can anyone provide me some guidance on the next step?

Thanks.


Edit from comments:

I have already tried to convert to BSTR using the following, but I get an error:

STDMETHODIMP CGuidGenerator::Generate(BSTR* retGuid)
{
    boost::uuids::uuid uuid = boost::uuids::random_generator()();
    std::string uuidStr = boost::lexical_cast<std::string>(uuid);

    int wslen = ::MultiByteToWideChar(CP_ACP, 0 /* no flags */,
                                  uuidStr.data(), uuidStr.length(),
                                  NULL, 0);

    BSTR wsdata = ::SysAllocStringLen(NULL, wslen);
    ::MultiByteToWideChar(CP_ACP, 0 /* no flags */,
                      uuidStr.data(), uuidStr.length(),
                      wsdata, wslen);
    retGuid = wsdata;
    //ERROR: A value of type BSTR cannot be assigned to an entity of type BSTR*

    return S_OK;
}

Upvotes: 2

Views: 1214

Answers (2)

Mr.C64
Mr.C64

Reputation: 42984

Assuming that std::string uuidStr is your string you want to return as BSTR output parameter, consider code like this:

#include <atlbase.h> // for CComBSTR
#include <atlconv.h> // for CA2W

STDMETHODIMP CGuidGenerator::Generate(BSTR* retGuid)
{
    try
    {
        ....

        // Convert uuidStr from ASCII to Unicode
        CA2W wszUuid( uuidStr.c_str() );

        // Build a COM BSTR from the Unicode string
        CComBSTR bstrUuid( wszUuid );

        // Return the BSTR as output parameter
        *retGuid = bstrUuid.Detach();

        // All right
        return S_OK;
    }
    //
    // Catch exceptions and convert them to HRESULTs,
    // as C++ exceptions can't cross COM module boundaries.
    //
    catch(const CAtlException& ex)
    {
        return static_cast<HRESULT>(ex);
    }
    catch(const std::exception& ex)
    {
        // May log the exception message somewhere...

        return E_FAIL;
    }
}

Note how a RAII helper class like CA2W simplifies the conversion from ASCII to Unicode, and CComBSTR simplifies the management of raw BSTRs.

Upvotes: 1

MartinSGill
MartinSGill

Reputation: 1210

This Microsoft article on COM under Unix might help more generally.

As to the next step, a BSTR is not your usual string; .NET hides this complexity from you. A BSTR is a specialised string that's intended to be marshalled across thread/process boundaries.

Have a look at this answer to see how to convert your std::string to a BSTR.

Upvotes: 0

Related Questions