2

How to setup an event-handler or callback for the Windows start menu (or Windows 8 start screen) opening?

Or, at least, how to check if the start menu is currently open?

I prefer a solution for this problem in C#, but a solution in C++ is also greatly appreciated.

flx
  • 14,146
  • 11
  • 55
  • 70
noir
  • 55
  • 4

1 Answers1

2

Apparently the start screen visibility callback/event thing doesn't work for a desktop app (see the out-#ifed code below), but simple polling works nicely.

#include <algorithm>        // std::swap
#include <functional>       // std::function
#include <iostream>
#include <stdlib.h>         // EXIT_FAILURE, EXIT_SUCCESS
#include <stdexcept>        // std::runtime_error, std::exception
#include <system_error>     // std::system_error
#include <string>           // std::string
using namespace std;

#include <conio.h>          // _kbhit

#undef UNICODE
#define UNICODE
#include <windows.h>
#include <shobjidl.h>       // IAppVisibilityEvents

auto fail( string const& message, int code = 0 )
    -> bool
{ throw system_error( code, system_category(), message ); }

class On_block_exit
{
private:
    function<void()>    f_;
public:
    ~On_block_exit() { f_(); }
    On_block_exit( function<void()> f ): f_( f ) {}
};

enum Success { success };

auto operator>>( HRESULT const hr, Success const )
    -> bool
{ ::SetLastError( hr );  return SUCCEEDED( hr ); }

template< class Interface >
class Com_ptr
{
private:
    Interface*  p_;
public:
    Interface* pointer() const { return p_; }
    Interface* operator->() const { return p_; }

    void** as_out_arg()
    {
        *this = Com_ptr();
        return reinterpret_cast<void**>( &p_ );
    }

    friend
    void swap( Com_ptr& a, Com_ptr& b ) throw()
    { swap( a.p_, b.p_ ); }

    void operator=( Com_ptr rhs ) { swap( *this, rhs ); }

    ~Com_ptr() { if( p_ ) { p_->Release(); } }

    Com_ptr( Interface* const p = nullptr ): p_( p ) {}

    Com_ptr( Com_ptr const& other )
        : p_( other.p_ )
    { if( p_ ) { p_->AddRef(); } }
};

auto is_startscreen_shown( Com_ptr<IAppVisibility> p_app_visibility )
    -> bool
{
    BOOL    result;
    p_app_visibility->IsLauncherVisible( &result )
        >> success
        || fail( "IAppVisibility::IsLauncherVisible", ::GetLastError() );
    return !!result;
}

template< class Base >
struct With_dummy_iunknown_
    : Base
{
    ULONG __stdcall AddRef() override { return 0; }

    ULONG __stdcall Release() override { return 0; }

    HRESULT __stdcall QueryInterface( REFIID riid, void **ppvObject ) override
    {
        if( riid == IID_IUnknown )
        {
            *ppvObject = this;
            return S_OK;
        }
        return E_NOINTERFACE;
    }
};

void cpp_main()
{
    ::CoInitialize( nullptr )
        >> success
        || fail( "CoInitialize", ::GetLastError() );

    Com_ptr<IAppVisibility> p_app_visibility;

    ::CoCreateInstance(
        CLSID_AppVisibility, nullptr, CLSCTX_ALL, IID_IAppVisibility, p_app_visibility.as_out_arg()
        ) >> success
        || fail( "CoCreateInstance CLSID_AppVisibility IID_IAppVisibility", ::GetLastError() );

    cout << "Press the 'Any' key to quit." << endl;
    bool was_shown = false; bool first_iteration = true; int n = 0;
    for( ;; )
    {
        if( _kbhit() ) { break; }

        bool const is_shown = is_startscreen_shown( p_app_visibility );
        if( first_iteration || is_shown != was_shown )
        {
            cout << n << ": ";
            if( is_shown )
            {
                cout << "Currently showing the start screen" << endl;
            }
            else
            {
                cout << "Currently showing normal desktop or app." << endl;
            }
            ++n;
        }
        first_iteration = false;
        was_shown = is_shown;
        Sleep( 50 );
    }

#if 0
    struct Events: IAppVisibilityEvents
    {
        HRESULT __stdcall AppVisibilityOnMonitorChanged(
            HMONITOR                monitor_handle,
            MONITOR_APP_VISIBILITY  previous_mode,
            MONITOR_APP_VISIBILITY  current_mode
            ) override
        { return S_OK; }

        HRESULT __stdcall LauncherVisibilityChange(
            BOOL is_now_visible
            ) override
        {
            cout
                << (!!is_now_visible? "Showing the start screen" : "Hiding start screen, showing desktop/app")
                << endl;
            return S_OK;
        }
    };

    With_dummy_iunknown_<Events> events;
    DWORD cookie = 1;
    p_app_visibility->Advise( &events, &cookie )
        >> success
        || fail( "IAppVisibility::Advise", ::GetLastError() );
    On_block_exit unadvise( [=](){ p_app_visibility->Unadvise( cookie ); } );

    ::MessageBox( 0, L"Press OK to quit.", L"Watching...", MB_SETFOREGROUND );
#endif
}

auto main()
    -> int
{
    try
    {
        cpp_main();
        return EXIT_SUCCESS;
    }
    catch( system_error const& x )
    {
        cerr << "!" << x.what() << " (code " << x.code().value() << ")" << endl;
    }
    catch( exception const& x )
    {
        cerr << "!" << x.what() << endl;
    }
    return EXIT_FAILURE;
}
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Note that if you want to check the current state, you can use [this](https://stackoverflow.com/a/12010841/997940) C# example code, and there's also a [C++ version on MSDN](https://code.msdn.microsoft.com/windowsapps/Start-screen-visibility-b1a72059/) – Yoav Feuerstein Aug 07 '17 at 09:34
  • Wow, I can't remember the last time I saw code with so many complicated pieces in it, that aren't necessary for answering the question being asked. – Remy Lebeau May 24 '23 at 17:20
  • @RemyLebeau: Let's start with the FIRST thing you don't understand or don't understand the purpose of in this context. – Cheers and hth. - Alf May 24 '23 at 20:53
  • @Cheersandhth.-Alf I didn't say I don't understand the code provided. I do understand it all. It is just a lot of unnecessary complexity that isn't relavant to the question being answered. A much simpler example would have been more effective. That's all. – Remy Lebeau May 25 '23 at 03:06
  • @RemyLebeau: A much simpler example would either not provide a complete program that could be tried out, or it would teach unsafe techniques. That is unfortunately the case for most of Microsoft's examples. And they do have to grapple with a lot of bugs and pain due to their unsafe practices. However the answer could have been much more clear with the code partitioned more clearly into support machinery and relevant example, plus the code could be explained. I guess at the time I just presented my exploratory code directly because it was clearly sufficient. – Cheers and hth. - Alf May 25 '23 at 05:24