Using the Windows::Globalization::Calendar object from a Win32 app


The Windows.Globalization.Calendar object is documented as supported for use by both Windows Store apps and desktop apps. The UWP samples repo has a Calendar sample that demonstrates how to use it from a Windows Store app, but how do you use it from a desktop app?

That's our motivation for looking at the Windows Runtime ABI. Normally, these objects are consumed via a mechanism known as projection, whereby the low-level ABI guts of the Windows Runtime are adapted to the usage pattern of a target language, so that the objects feel more like language native objects.¹ That's great if you're using a projected language like C++/CX, C#, or JavaScript, but if you want to use raw C++, then you're going to be talking to the ABI.

Crash course in projection:

Create an object
ABI IWidget* widget;
ActivateInstance(L"Widget", &widget);
C++/CX auto widget = ref new Widget();
C# var widget = new Widget();
JavaScript var widget = new Widget();

At the ABI level, you use Activate­Instance to create an object; this projects as a parameterless constructor. We'll look at parameterized constructors some other time.

Method calls at the ABI level look pretty much like method calls in projection, except that output parameters are passed explicitly as [out] parameters rather than as the return value of the method. (The return value of ABI methods is always HRESULT. We'll look at projected HRESULTs later.)

Invoke a method
no return value with return value
ABI widget->Method(); widget->Method(&result);
C++/CX widget->Method(); result = widget->Method();
C# widget.Method(); result = widget.Method();
JavaScript widget.method(); result = widget.method();

Notice that the first character of the method name is converted to lowercase by the JavaScript projection, so that it matches existing JavaScript convention.

And the last piece of projection for today: Properties.

Read property Write property
ABI widget->get_Foo(&v); widget->put_Foo(v);
C++/CX v = widget->Foo; widget->Foo = v;
C# v = widget.Foo; widget.Foo = v;
JavaScript v = widget.foo; widget.foo = v;

Okay, we now know just enough to be dangerous. We'll write a little console program to get the current date, get the name of the day of the week in a form intended to be displayed on its own, get the last day of the current month, then skip forward six months and check whether daylight saving time will be in effect. Remember, Little Programs do little to no error checking.

#include <windows.h>
#include <wrl/client.h>                // WRL::ComPtr
#include <wrl/wrappers/corewrappers.h> // WRL::Wrappers
#include <windows.globalization.h>     // Windows::Globalization
#include <stdio.h>

namespace WRL = Microsoft::WRL;

int main(int argc, wchar_t** argv)
{
  CCoInitialize init;

  // C++/CX: auto cal = ref new Windows::Globalization::Calendar();
  WRL::ComPtr<ABI::Windows::Globalization::ICalendar> cal;
  Windows::Foundation::ActivateInstance(
    WRL::Wrappers::HStringReference(
      RuntimeClass_Windows_Globalization_Calendar).Get(), &cal);

  // C++/CX: auto dayOfWeek = cal->DayOfWeekAsFullSoloString();
  WRL::Wrappers::HString dayOfWeek;
  cal->DayOfWeekAsFullSoloString(dayOfWeek.GetAddressOf());

  wprintf(L"%ls\n", dayOfWeek.GetRawBuffer(nullptr));

  // C++/CX: lastDayThisMonth = cal->LastDayInThisMonth;
  INT32 lastDayThisMonth;
  cal->get_LastDayInThisMonth(&lastDayThisMonth);
  wprintf(L"Last day in this month is %d\n", lastDayThisMonth);

  // C++/CX: cal->AddMonths(6);
  cal->AddMonths(6);

  // C++/CX: isDST = cal->IsDaylightSavingTime;
  boolean isDST;
  cal->get_IsDaylightSavingTime(&isDST);

  wprintf(L"DST six months from now? %ls\n", isDST ? L"Yes" : L"No");

  return 0;
}

The raw C++ code is a straightforward translation of the corresponding C++/CX code. One thing to note is that we used the symbol Runtime­Class_Windows_Globalization_Calendar rather than hard-coding the string L"Windows.Globalization.Calendar".

Another thing to note is that Windows Runtime strings are handled in the form of HSTRINGs, which we discussed a little while ago.

¹Another approach is Modern C++, which is effectively a projection of the Windows Runtime into native C++.

Comments (20)
  1. skSdnW says:

    I had the unfortunate requirement of using a WinRT API in a win32 app that also supports older SDKs and MinGW (shameless plug; https://github.com/WndSks/Win32WinRTInterop_ShareContractSource ) and that was not fun, never doing that again. When trying to do it at the lowest level you have to interact with interfaces that seem to be computer-generated, there are hundreds of them, all with really long names and probably not meant to be used by any human.

    1. Yup. The interfaces were designed to be projected into something less awkward to use. There wasn't much expectation that human beings would deal with them directly.

      1. skSdnW says:

        Why was there not much expectation? Why was the share API never promoted for use in desktop apps? It does not even work correctly in desktop apps until Win10. MS usually want apps to light up on newer versions. In the past this was GetProcAddress and trying to create the new COM interfaces but starting with Win8 there were basically no new shell features and no helper functions for dealing with the new WinRT stuff. It's the same story with Action Center in 10, no shell32 goodies and I don't want to generate XML and deal with low-level undocumented interfaces just to get basic notification and tile support.

        1. As you may have noticed, Windows 8 had a specific point of view with respect to separating new from old.

  2. Darran Rowe says:

    Hmm, that link to the CCoInitialize class is using CoInitialize and CoUninitialize.
    I thought that when you used any WinRT objects in an application, you had to use RoInitialize.

    1. Curious says:

      "The Windows Runtime provides the RoInitialize function, which is a thin wrapper around CoInitializeEx.
      Despite the fact that CoInitializeEx is usually sufficient, I suggest you use RoInitialize. This function allows future improvements to be made to the Windows Runtime without potentially breaking classic COM. It’s analogous to OleInitialize, which also calls CoInitializeEx and then some. The point is that there’s nothing mysterious about your application’s main thread."

      1. Chris Guzak says:

        CoInit and RoInit do the same thing in desktop apps. they have slightly different behavior in modern processes. this can never change so it is fine to depend on it.

        1. skSdnW says:

          Citation needed?

        2. Darran Rowe says:

          This is actually directly contradictory to the official MSDN documentation.
          The documentation for CoInitializeEx actually says
          "You should call Windows::Foundation::Initialize to initialize the thread instead of CoInitializeEx if you want to use the Windows Runtime APIs or if you want to use both COM and Windows Runtime components. Windows::Foundation::Initialize is sufficient to use for COM components."
          If you notice, it doesn't limit it to WinRT API in new processes, so it also includes WinRT API in Win32 processes too. Another thing to note, Windows::Foundation::Initialize is in roapi and it calls RoInitialize.
          Using CoInitializeEx actually feels like a compatibility issue waiting to happened.

  3. Ivan k says:

    I tried disambiguation. Is it a pattern? https://en.m.wikipedia.org/wiki/Projection. Anyways must be something like that DirectX absrtacting graphics thing?. Anyway, I'm more interested in typescript at the moment. Because Javacscript isn't the best, even with hints. "Ansi C" Javascript would be awesome.

    1. Mike says:

      https://www.infoq.com/presentations/windows-api-winrt

      A little over 16 minutes in. It is a way of presenting WinRT objects so that they appear native to the language.

    2. Projection is a fairly widely used term with many meanings, both in computer science and in plenty of other disciplines. A common theme to the various meanings is that it usually involves a transformation that changes form but preserves function or meaning in some way. Think of shadow puppets: you are using light to project an image of your 3 dimensional hand onto a 2 dimensional surface. The form is changed because you're losing a dimension, but it's still an image of your hand, so in a sense the original meaning was preserved.

      In computer science in general, this pattern is followed. Projection usually means taking some concept, code, api, what have you, and then transforming it in some way that is useful, but still preserves the original semantics as much as possible. This applies to the specific case Raymond is talking about: the low-level ABI is projected into the syntax and usage of each different language, but the semantics are preserved: calling widget.Method() in C# or widget.method() in JavaScript or widget->Method() in C++/CX will all end up running the same widget code. They're just adapted to the conventions of each language.

  4. Ben Voigt (Visual Studio and Development Technologies MVP with C++ focus) says:

    Wasn't it enough to have bool, _Bool, BOOL, and BOOLEAN? Do we really need to add "boolean" on top of all that? Should we look forward to types named boole, Boole, and BOOLE?

    Also, this "direct access" looks messier than real-COM-with-smart-pointers. Are all the `GetAddressOf()` calls an artifact of the `WRL::ComPtr` choice of smart pointer? Is there a choice? (Note that I don't want HRESULTs wrapped in exceptions and thrown, like the "Modern C++ Windows Runtime"... I just want my smart pointers to be smart, not get in the way)

    1. gdalsnes says:

      Wonder why it is not called Boolean, like System.Boolean, instead of inventing a new type that doesn't exist in .NET.

      1. If it were called Boolean, then wouldn't it conflict with System.Boolean? And if it reused the type from System.Boolean, how would you use it from JavaScript?

    2. skSdnW says:

      boolean is at least 10 years old. It is unsigned char just like NTs BOOLEAN but maybe in those days they were still trying to maintain a strict wall between Win API and NT API for anyone outside MS.

  5. Two nits: 1.) While generally pushed, not all ABI methods must return an HRESULT, but if they don't, they can't throw exceptions. 2.) You shouldn't use CCoInitialize there. "New applications should call CoInitializeEx instead of CoInitialize. If you want to use the Windows Runtime, you must call Windows::Foundation::Initialize instead."

  6. John Doe says:

    Uhm, if I need both OLE (e.g. drag&drop) and Windows Runtime (e.g. compass), what should I call (CoInitializeEx, OleInitialize, RoInitialize, Windows::Foundation::Initialize, something else) and in which order?

    1. skSdnW says:

      You can stack STA Co* and Ole*: HRESULT hr = OleInitialize(); if (SUCCEEDED(hr)) DoSomething(), OleUninitialize();

      I would assume that you can stack MTA Co* and Ro* as well?

      1. John Doe says:

        Almost.

        You see, Windows::Foundation::Initialize ( https://msdn.microsoft.com/en-us/library/windows/desktop/hh404185.aspx ) mentions that the otherwise undocumented RO_INIT_SINGLETHREADED flag is not supported for desktop applications.

        At best, you can use it in an MTA thread, but that implies marshaling.

Comments are closed.

Skip to main content