Fictional Memory
Intersection of C++ and Win32
Friday, September 6, 2013
RxC++ ReactiveCommand
ReactiveCommand models calls to the Execute function as a Sequence of the value passed to each Execute. All subscriptions to it are called on the UI thread. There can be many subscriptions directly to ReactiveCommand, there can also be many async functions registered.
Each async function registered takes the value passed to Execute does work and returns a result. Each call to RegisterAsyncCommand returns a new Sequence of the results from each call to the registered function. All subscriptions to the Sequence of results are called on the UI thread.
ReactiveCommand also provides Sequences of bools, that can be subscribed, that represent its state. When the CanExecute Sequence is bound to the enabled property of the button the state of the ReactiveCommand is communicated into the ui.
By default ReactiveCommand will ignore Execute until all the subscribers (sync and async) to the previous Execute call have finished. This can be changed during construction.
Here is some (out-of-order) usage from the sample.
Subscription - async version (Scenario1.Xaml.cpp):
Subscription - sync version (Scenario1.Xaml.cpp):
Binding (Scenario1.Xaml.cpp):
Creation (Scenario1.Xaml.cpp):
Create state sequences that are used by these ReactiveCommand (Scenario1.Xaml.cpp):
Thursday, September 5, 2013
DeferOperation
I was told that Deferrals did not compose well with Rx. This motivated me to design the DeferOperation operator.
DeferOperation demonstrates how Rx can be extended by a few engineers to provide services for many. Another way to say this is that Rx allows Async Algorithms to be written once, carefully, so that many others can re-use them.
These are all references to the Windows 8.1 Accelerometer sample I am modifying.
Usage (App.Xaml.Cpp):
DeferOperation defaults to the ui thread by reading the context off of the Window, but when the suspending event is fired the Window is not valid, so this uses the current thread scheduler explicitly. publish and connect_forever ensure that the work is done even if nothing subscribes the result.
The implementation follows.
The core of the DeferOperation operator is (cpprx/rx-winrt.hpp):
sop is the first lambda from the usage (cpprx/rx-winrt.hpp):
sob is the second lambda from the usage (the OperationPattern is passed so that the SuspendingOperation and the Deferral are available to the sequence if it wants to see the Deadline or Complete early, for example) (cpprx/rx-winrt.hpp)
Using will retrieve the resource (OperationPattern), subscribe to the result of sob and Dispose the resource when the subscription completes or errors.
OperationPattern provides the abstraction needed by Using. It will call get and complete the deferral (cpprx/rx-winrt.hpp):
Friday, August 30, 2013
Reactive Extensions for C++
Examples
Create a ReactiveCommand and bind it to a Xaml button
Transform SaveAsync method to ReactiveSave
- Rx.NET, RxJS, Rx++ - https://rx.codeplex.com/
- RxJava - https://github.com/Netflix/RxJava
- ReactiveCocoa - https://github.com/ReactiveCocoa/ReactiveCocoa
- Rx.rb - https://github.com/Reactive-Extensions/Rx.rb
- Rx.py - https://rxpy.codeplex.com/
Saturday, May 12, 2012
ZeroMQ and C++ without exceptions
my comment in reply to martin_sustrik.
I wanted to post some code into the comment but I finally gave up. I will post it here instead. Disclaimer: this code has not been compiled.
class foo { public: ~foo() {} foo(foo&& other) { guts.a = std::move(other.a); // no-fail } foo& operator=(foo other) { // no-fail using std::swap; swap(guts.a, other.guts.a); return *this; } static optional<foo> init() { optional<foo> result; type guts = {}; // ~type() cleans up on exit guts.a.reset(new(std::nothrow) int(5)); if (!guts.a) {return result;} // no foo constructed result.reset(foo(guts)); //no-fail move construction return std::move(result); // no-fail move of result } int do() {return guts.a.get();} // a cannot be empty private: struct type { std::unique_ptr<int> a; }; type guts; foo(); // disabled foo(const foo&); // disabled foo(type&& gutsArg) {guts.a = std::move(gutsArg.a);} // no-fail }; auto MyFoo = foo::init(); if (!MyFoo) return; MyFoo->do();
if you want to return a detailed error from init()
change init()
to return a tuple:
static std::tuple<error, optional<foo>> init();and use tie to break the result into local vars:
optional<foo> MyFoo; error MyError; std::tie(MyError, MyFoo) = foo::init();
Tuesday, April 17, 2012
console app that monitors smart cards
Here is a console app that demonstrates how to tie the smart card functions already covered into a working monitor that prints reader state changes and the certs and subject names for the cards that are present.
#pragma once // Exclude rarely-used stuff from // Windows headers #define WIN32_LEAN_AND_MEAN // skip min/max macros from // Windows Headers #define NOMINMAX // Windows Header Files: #include <windows.h> #include <Unknwn.h> #include <winscard.h> #include <ncrypt.h> #include <Wincrypt.h> #include <credentialprovider.h> // C++ library header files: #include <type_traits> #include <algorithm> #include <new> #include <memory> #include <utility> #include <limits> #include <iterator> #include <thread> #include <future> #include <mutex> #include <vector> #include <iostream> #include <iomanip> // local header files: #define LIBRARIES_NAMESPACE nslib #include <libraries.h> namespace lib = LIBRARIES_NAMESPACE; #include "..\win32project1\scard_monitor.h"
Set a breakpoint in each of these functions and see where error codes first make an appearance in this module. the static anchor is a device to make sure that the compiler does not eliminate or fold them together.
void unique_error_report_initiated( HRESULT value, unique_hresult_def::tag &&) { static HRESULT anchor; anchor = value; } void unique_error_report_reset( HRESULT value, unique_hresult_def::tag &&) { static HRESULT anchor; anchor = value; } void unique_error_report_initiated( DWORD value, unique_winerror_def::tag &&) { static DWORD anchor; anchor = value; } void unique_error_report_reset( DWORD value, unique_winerror_def::tag &&) { static DWORD anchor; anchor = value; }
Helper functions to print status and certificate properties.
template<typename Certificate> void printCertificates( lib::rng::range<Certificate> certificates) { for (auto & key : certificates) { unique_winerror winerror; PCCERT_CONTEXT certcontext = ( CertCreateCertificateContext( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &key.cert[0], key.cert.size() ) ); winerror = make_winerror_if(!certcontext); if (!winerror) { std::wcout << L"could not get cert context" << std::endl; continue; } DWORD sizesubject = 0; std::wstring subjectname; for (bool getsize = true; ; getsize = false) { sizesubject = CertGetNameString( certcontext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, getsize ? nullptr : &subjectname[0], sizesubject ); if (sizesubject == 1) { std::wcout << L"could not get subject name" << std::endl; break; } if (getsize) { subjectname.resize(sizesubject - 1); } else { std::wcout << L"key name: " << key.key.c_str() << L" subject name: " << subjectname.c_str() << std::endl; break; } } } } template<typename Stream> Stream& printSCardState(Stream& stream, DWORD state) { stream << ((state == SCARD_STATE_UNAWARE) ? L" SCARD_STATE_UNAWARE" : L"") << ((state & SCARD_STATE_PRESENT) ? L" SCARD_STATE_PRESENT" : L"") << ((state & SCARD_STATE_ATRMATCH) ? L" SCARD_STATE_ATRMATCH" : L"") << ((state & SCARD_STATE_CHANGED) ? L" SCARD_STATE_CHANGED" : L"") << ((state & SCARD_STATE_EMPTY) ? L" SCARD_STATE_EMPTY" : L"") << ((state & SCARD_STATE_EXCLUSIVE) ? L" SCARD_STATE_EXCLUSIVE" : L"") << ((state & SCARD_STATE_IGNORE) ? L" SCARD_STATE_IGNORE" : L"") << ((state & SCARD_STATE_INUSE) ? L" SCARD_STATE_INUSE" : L"") << ((state & SCARD_STATE_MUTE) ? L" SCARD_STATE_MUTE" : L"") << ((state & SCARD_STATE_UNAVAILABLE) ? L" SCARD_STATE_UNAVAILABLE" : L"") << ((state & SCARD_STATE_UNKNOWN) ? L" SCARD_STATE_UNKNOWN" : L"") << ((state & SCARD_STATE_UNPOWERED) ? L" SCARD_STATE_UNPOWERED" : L"") ; return stream; } int wmain(int argc, WCHAR* argv[]) { unique_winerror winerror; for (;;) { SCARDCONTEXT context = NULL; HANDLE waitfor[] = {SCardAccessStartedEvent()}; ON_UNWIND_AUTO([] {SCardReleaseStartedEvent();}); winerror = smart_card::monitor_smartcard_readers( [&](SCARDCONTEXT context) { context = context; }, [&]() { context = NULL; }, [&]() -> bool { if (WAIT_OBJECT_0 != WaitForMultipleObjects( lib::rng::size(waitfor), waitfor, FALSE, INFINITE) ) { // monitor_smardcard_readers will return // SCARD_E_CANCELLED return false; } return true; }, [&]( lib::rng::range<SCARD_READERSTATE*> readersrange ) { for (auto & state : readersrange) { auto stateChanges = ( (state.dwCurrentState ^ state.dwEventState) & std::numeric_limits<unsigned short>::max() ); std::wcout << L"nothread - " << state.szReader << L" changes: " << std::hex << std::showbase << stateChanges << L"[" ; printSCardState(std::wcout, stateChanges) << L"] state: " << std::hex << std::showbase << state.dwEventState << L"[" ; printSCardState(std::wcout, state.dwEventState) << L"]" << std::endl ; if ( state.dwCurrentState != SCARD_STATE_UNAWARE && ((state.dwEventState & SCARD_STATE_PRESENT) != SCARD_STATE_PRESENT || stateChanges == SCARD_STATE_INUSE || stateChanges == SCARD_STATE_UNPOWERED || (state.dwEventState & ( SCARD_STATE_UNPOWERED | SCARD_STATE_EMPTY | SCARD_STATE_IGNORE | SCARD_STATE_UNKNOWN | SCARD_STATE_UNAVAILABLE | SCARD_STATE_MUTE)) || state.cbAtr == 0) ) { // we have seen this reader before // and one of: // no card // only flipped INUSE // only flipped UNPOWERED // UNPOWERED EMPTY UNKNOWN UNAVAILABLE MUTE // no atr // // don't try to read the card continue; } CardWithProvider card; std::tie(winerror, card) = smart_card::smartcard_name_and_provider( lib::rng::make_range_raw(state.rgbAtr) ); if (!winerror) { continue; } KeyWithCertificateVector certificates; std::tie(winerror, certificates) = smart_card::smartcard_certificates( card.kspname); if (!winerror) { continue; } std::wcout << L"nothread -" << L" kspname: " << card.kspname.c_str() << std::endl; printCertificates( lib::rng::make_range_raw(certificates) ); } } ); winerror.suppress(); } return 0; }
Thursday, April 12, 2012
smartcard_certificates implementation
Given a provider name NCryptEnumKeys
can be used to retrieve all the keys known to that provider. Since the KSP provider is shared across all cards associated to it, the keys from all associated cards will be returned.
Here is a function that uses NCryptEnumKeys
and NCryptGetProperty
to retrieve the keys and certificates from a KSP provider name.
struct KeyWithCertificate { std::wstring key; std::vector<BYTE> cert; }; typedef std::vector<KeyWithCertificate> KeyWithCertificateVector; inline std::pair<unique_winerror, KeyWithCertificateVector> smartcard_certificates( const std::wstring& kspstring ) { unique_winerror winerror; KeyWithCertificateVector output; NCRYPT_PROV_HANDLE provider = NULL; ON_UNWIND_AUTO( [&] { if (provider) { NCryptFreeObject(provider); } } ); winerror.reset( NCryptOpenStorageProvider( &provider, kspstring.c_str(), 0 ) ); if (!winerror) { return std::make_pair(winerror, std::move(output)); } NCryptKeyName* keyname = nullptr; ON_UNWIND_AUTO( [&] { if (keyname) { NCryptFreeBuffer(keyname); } } ); void* enumstate = nullptr; ON_UNWIND_AUTO( [&] { if (enumstate) { NCryptFreeBuffer(enumstate); } } ); for (;;) { winerror.reset( NCryptEnumKeys( provider, NULL, &keyname, &enumstate, NCRYPT_SILENT_FLAG ) ); if (winerror == winerror_cast(NTE_NO_MORE_ITEMS)) { winerror.suppress().release(); break; } if (!winerror) { return std::make_pair(winerror, std::move(output)); } KeyWithCertificate keystate; keystate.key = keyname->pszName; NCRYPT_KEY_HANDLE key = NULL; ON_UNWIND_AUTO( [&] { if (key) { NCryptFreeObject(key); } } ); winerror.reset( NCryptOpenKey( provider, &key, keyname->pszName, keyname->dwLegacyKeySpec, NCRYPT_SILENT_FLAG ) ); if (!winerror) { return std::make_pair(winerror, std::move(output)); } DWORD sizecert = 0; for (bool getsize = true; ; getsize = false) { winerror.reset( NCryptGetProperty( key, NCRYPT_CERTIFICATE_PROPERTY, getsize ? nullptr : &keystate.cert[0], sizecert, &sizecert, 0 ) ); if (!winerror) { return std::make_pair(winerror, std::move(output)); } if (getsize) { keystate.cert.resize(sizecert); } else { break; } } output.push_back(keystate); } return std::make_pair(winerror, std::move(output)); }
Tuesday, April 10, 2012
smartcard_name_and_provider implementation
Once the reader monitor has reported the ATR for the card in a reader, it can be used to access the card.
Windows has a model that gives card manufacturers three options for providing access to a smart card through the crypto apis.
- KSP/CSP - the manufacturer can supply a full KSP(Key Storage Provider) and CSP (Cryptographic Service Provider) and associate it with an ATR.
- Smart Card Minidriver - the manufacturer can supply a minidriver dll that the built-in Smart Card KSP and CSP will load to provide access and associate it with an ATR.
- PICS/GIDS - the manufacturer can provide a card that implements the GIDS or PICS card standard. Windows has built-in minidrivers for these.
Because of the first option, once an ATR is in hand, SCardGetCardTypeProviderName
must be called to retrieve the KSP or CSP provider name that should be used for the card.
SCardListCards
will retrieve the card name from an ATR, which is then passed to SCardGetCardTypeProviderName
. Windows only supports one KSP/CSP for an ATR, so even though SCardListCards
returns a list of card names any one of them can be used to retrieve the provider names.
Here is the code to get a card name and the KSP provider name.
struct CardWithProvider { std::wstring cardname; std::wstring kspname; }; inline std::pair<unique_winerror, CardWithProvider> smartcard_name_and_provider( lib::rng::range<const BYTE*> atr ) { unique_winerror winerror; unique_close_scardcontext context; CardWithProvider output; winerror.reset( SCardEstablishContext( SCARD_SCOPE_USER, NULL, NULL, context.replace() ) ); if (!winerror) { return std::make_pair(winerror, std::move(output)); } LPWSTR kspstring = nullptr; ON_UNWIND( unwind_kspstring, [&] { if (kspstring) { SCardFreeMemory(context.get(), kspstring); } } ); DWORD ksplength = SCARD_AUTOALLOCATE; LPWSTR cardsstring = nullptr; ON_UNWIND_AUTO( [&] { if (cardsstring) { SCardFreeMemory(context.get(), cardsstring); } } ); DWORD cardslength = SCARD_AUTOALLOCATE; winerror.reset( SCardListCards( context.get(), atr.begin(), NULL, NULL, reinterpret_cast<LPWSTR>(&cardsstring), &cardslength ) ); if (winerror == winerror_cast(SCARD_E_READER_UNAVAILABLE)) { winerror.suppress(); // don't free the static string we are // about to assign unwind_kspstring.dismiss(); kspstring = MS_SMART_CARD_KEY_STORAGE_PROVIDER; ksplength = wcslen(kspstring); } else if (!winerror) { return std::make_pair(winerror, std::move(output)); } else { winerror.reset( SCardGetCardTypeProviderName( context.get(), cardsstring, SCARD_PROVIDER_KSP, reinterpret_cast<LPWSTR>(&kspstring), &ksplength ) ); if (!winerror) { return std::make_pair(winerror, std::move(output)); } } if (cardsstring) { output.cardname = cardsstring; } output.kspname = kspstring; return std::make_pair(winerror, std::move(output)); }