1

I want to be able to serialize a Windows HANDLE:

typedef void *HANDLE

If I try to compile using following:

struct Foo
{
    HANDLE file;

protected:
    friend class boost::serialization::access;

    template<class Archive>
    void serialize(Archive & ar, const unsigned int /*version*/)
    {
        ar & file;
    }
};

I get compile errors:

c:\projects\3rdparty\src\boost\include\boost/mpl/print.hpp(51) : warning C4308: negative integral constant converted to unsigned type
        c:\projects\3rdparty\src\boost\include\boost/serialization/static_warning.hpp(92) : see reference to class template instantiation 'boost::mpl::print<T>' being compiled
        with
        [
            T=boost::serialization::BOOST_SERIALIZATION_STATIC_WARNING_LINE<98>
        ]
        c:\projects\3rdparty\src\boost\include\boost/archive/detail/check.hpp(98) : see reference to class template instantiation 'boost::serialization::static_warning_test<B,L>' being compiled
        with
        [
            B=false,
            L=98
        ]
        c:\projects\3rdparty\src\boost\include\boost/archive/detail/oserializer.hpp(313) : see reference to
function template instantiation 'void boost::archive::detail::check_object_tracking<T>(void)' being compiled
        with
        [
            T=Foo
        ]
        c:\projects\3rdparty\src\boost\include\boost/archive/detail/oserializer.hpp(525) : see reference to
function template instantiation 'void boost::archive::detail::save_non_pointer_type<Archive>::invoke<T>(Archive &,T &)' being compiled
        with
        [
            Archive=boost::archive::text_oarchive,
            T=Foo
        ]

But if I change file to an int, everything is fine. How do I tell boost to serialize HANDLEs as ints?

Thanks

Bevan Collins
  • 1,531
  • 16
  • 25

2 Answers2

4

HANDLE is a Windows-API-specific data type defined in winnt.h. According to the MSDN,

A handle to an object. This type is declared in WinNT.h as follows:

typedef PVOID HANDLE;

So, now we see that HANDLE is really just void * -- representing a handle to an object. Think about what it is that you're trying to do; does it make sense to serialize a pointer to some object in the Windows API?

Instead, try to serialize what it takes to retrieve an equivalent HANDLE; judging by the name of the member, I'm going to guess you used CreateFile -- so, you'll need to know...

  • The file name
  • The desired access (e.g. GENERIC_READ | GENERIC_WRITE)
  • The share mode (e.g. FILE_SHARE_DELETE)
  • Optionally, the security attributes
  • The creation disposition (i.e. CREATE_NEW, TRUNCATE_EXISTING, etc.)
  • The file or device flags and attributes
  • Optionally, a template file -- for copying its attributes when creating a file

Now, if you really don't want to be doing that -- you're positive you want the pointer value -- maybe try serializing it after casting via reinterpret_cast to std::intptr_t or std::uintptr_t (as might be defined in cstdint as of C++11).

ar & reinterpret_cast<std::intptr_t>(file);

... then you should couple this with something as follows (when deserializing):

std::intptr_t _file;
ar & _file;
HANDLE file = std::reinterpret_cast<HANDLE>(_file);
obataku
  • 29,212
  • 3
  • 44
  • 57
  • yup, it makes sense. I'm doing IPC and one of the fields of the structure I want to send is a duplicated HANDLE. Thanks – Bevan Collins Sep 09 '12 at 21:16
  • @BevanCollins make sure `file` is a valid duplicated handle for the target process (i.e. from [`DuplicateHandle`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms724251.aspx))... then just use `intptr_t` I suppose. – obataku Sep 09 '12 at 21:22
  • I would change `reinterpret_cast` to `static_cast` when serializing. [See here](http://stackoverflow.com/questions/310451/should-i-use-static-cast-or-reinterpret-cast-when-casting-a-void-to-whatever) for more details. – Jesse Good Sep 09 '12 at 21:31
  • @BevanCollins right, which is why I suggested casting to `std::intptr_t` -- that should hopefully solve the issue. If you know the size of a pointer in your environment (Windows is pretty consistent with this), you can do what is technically undefined behavior (and rely on your environment providing consistency) and try converting to an integral type like `INT32` or `INT64`, instead. – obataku Sep 09 '12 at 21:33
  • @JesseGood see [this reference on `reinterpret_cast`](http://en.cppreference.com/w/cpp/language/reinterpret_cast)... "Any pointer can be converted to any integral type large enough to hold the value of the pointer (e.g. to `std::uintptr_t`)". C++11 allows this. – obataku Sep 09 '12 at 21:34
  • @oldrinb: Yes, that is the new wording for C++11, however anything pre-C++11 should use `static_cast`. Also, I prefer `static_cast` because the casting is well-defined (although I realize that might be a little pedantic). – Jesse Good Sep 09 '12 at 21:36
  • @JesseGood `reinterpret_cast` is the preferred way to cast between pointer types to `intptr_t` (which doesn't exist pre-C++11, mind you). What you stated is correct for conversion between `void *` and various pointer types, however. – obataku Sep 09 '12 at 21:41
  • Yes, I agree now that it is the preferred way when using `std::intptr_t` (learned a thing today), +1 from me and sorry for the noise. – Jesse Good Sep 09 '12 at 21:56
  • @JesseGood not noise! Interesting discussion :-) to be honest, I hadn't been too sure myself... – obataku Sep 09 '12 at 21:58
0

Ended up solving the problem by splitting the serialization. Seems like a hack though:

struct Foo
{
    HANDLE file;

protected:
    friend class boost::serialization::access;

    template<class Archive>
    void save(Archive & ar, const unsigned int /*version*/) const
    {
        unsigned int _file = reinterpret_cast<unsigned int>(file);
        ar & _file;
    }

    template<class Archive>
    void load(Archive & ar, const unsigned int /*version*/)
    {
        unsigned int _file;
        ar & _file;
        file = reinterpret_cast<HANDLE>(_file);
    }
    BOOST_SERIALIZATION_SPLIT_MEMBER()
};
Bevan Collins
  • 1,531
  • 16
  • 25