From 008d19fd17edef6ad12b51a333e89b628786e895 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Mon, 22 Apr 2024 01:07:59 -0400 Subject: [PATCH 1/2] Working on auto-reconnect --- build/build.cake | 26 +- build/replace_running_service_x64.bat | 4 +- build/staging/version/BundleInfo.wxi | 2 +- diagnostics/trace-logging/MidiServices.wprp | 2 +- docs/api/service/MidiService.md | 2 + docs/api/service/README.md | 2 +- docs/api/session/MidiSession.md | 22 +- docs/api/simple-types/MidiUniqueId.md | 4 - .../Midi2.LoopbackMidiBidi.h | 4 +- .../Midi2.MidiSrvSessionTracker.cpp | 38 +++ .../Midi2.MidiSrvSessionTracker.h | 2 + .../nuget/Microsoft.Devices.Midi2.nuspec | 2 +- .../Midi2Client/MidiEndpointConnection.cpp | 165 ++++++++--- .../Midi2Client/MidiEndpointConnection.h | 69 +++-- .../Midi2Client/MidiEndpointConnection.idl | 1 - .../MidiEndpointConnection_AutoReconnect.cpp | 131 +++++++++ src/api/Client/Midi2Client/MidiService.cpp | 276 +++++++++-------- src/api/Client/Midi2Client/MidiSession.cpp | 18 +- src/api/Client/Midi2Client/MidiSession.h | 16 +- src/api/Client/Midi2Client/MidiSession.idl | 6 + .../Midi2Client/Windows.Devices.Midi2.vcxproj | 13 +- src/api/Client/Midi2Client/pch.h | 1 + src/api/Client/Midi2Client/trace_logging.cpp | 29 +- src/api/Client/Midi2Client/trace_logging.h | 5 + src/api/InBoxApps/mididmp/main.cpp | 278 ++++++++++++++---- src/api/InBoxApps/mididmp/mididmp.vcxproj | 36 ++- .../InBoxApps/mididmp/mididmp.vcxproj.filters | 3 + .../InBoxApps/mididmp/mididmp_field_defs.h | 77 +++++ src/api/InBoxApps/mididmp/pch.h | 12 +- src/api/Service/Exe/MidiSessionTracker.cpp | 7 + src/api/Service/Exe/MidiSessionTracker.h | 3 +- src/api/Service/Exe/MidiSrvRPC.idl | 3 + src/api/Service/Exe/MidiSrvRpc.cpp | 20 ++ src/api/idl/MidiAbstraction.idl | 6 + .../api-package/WindowsMidiServices.wxs | 9 +- src/oob-setup/console-package/MidiConsole.wxs | 37 ++- .../settings-package/MidiSettings.wxs | 1 + .../Endpoint/EndpointMonitorCommand.cs | 14 +- .../Endpoint/EndpointSendMessageCommand.cs | 2 +- .../Commands/Service/ServiceStatusCommand.cs | 9 + src/user-tools/midi-console/Midi/Midi.csproj | 10 +- src/user-tools/midi-console/Midi/Program.cs | 1 + .../Midi/Resources/Strings.Designer.cs | 9 + .../midi-console/Midi/Resources/Strings.resx | 3 + 44 files changed, 1033 insertions(+), 347 deletions(-) create mode 100644 src/api/Client/Midi2Client/MidiEndpointConnection_AutoReconnect.cpp create mode 100644 src/api/InBoxApps/mididmp/mididmp_field_defs.h diff --git a/build/build.cake b/build/build.cake index 8528044df..90c9b5cff 100644 --- a/build/build.cake +++ b/build/build.cake @@ -436,8 +436,8 @@ Task("BuildConsoleApp") Configuration = configuration, PublishSingleFile = false, - PublishTrimmed = true, - //PublishAot = true, // not currently supported in cake, so make sure this is set in project file + PublishTrimmed = false, // must be true for AOT + //PublishAot = false, // not currently supported in cake, so make sure this is set in project file SelfContained = false, Framework = frameworkVersion, Runtime = rid @@ -613,17 +613,17 @@ Task("CopyAPIArtifacts") if (!DirectoryExists(platReleaseFolder)) CreateDirectory(platReleaseFolder); - CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.winmd"), platReleaseFolder); - CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.dll"), platReleaseFolder); - CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.pri"), platReleaseFolder); - CopyFiles(System.IO.Path.Combine(apiStagingDir, "Windows.Devices.Midi2.h"), platReleaseFolder); - CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/base.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/")); - CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/Windows.Devices.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/")); - CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Devices.Enumeration.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/")); - CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Devices.Midi.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/")); - CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Foundation.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/")); - CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Foundation.Collections.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/")); - CopyFiles(System.IO.Path.Combine(apiStagingDir, "winrt/impl/Windows.Devices.Midi2.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/")); + CopyFiles(System.IO.Path.Combine(apiStagingDir, $"{MIDI2_NAMESPACE}.winmd"), platReleaseFolder); + CopyFiles(System.IO.Path.Combine(apiStagingDir, $"{MIDI2_NAMESPACE}.dll"), platReleaseFolder); + CopyFiles(System.IO.Path.Combine(apiStagingDir, $"{MIDI2_NAMESPACE}.pri"), platReleaseFolder); + CopyFiles(System.IO.Path.Combine(apiStagingDir, $"{MIDI2_NAMESPACE}.h"), platReleaseFolder); + CopyFiles(System.IO.Path.Combine(apiStagingDir, $"winrt/base.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/")); + CopyFiles(System.IO.Path.Combine(apiStagingDir, $"winrt/Windows.Devices.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/")); + CopyFiles(System.IO.Path.Combine(apiStagingDir, $"winrt/impl/Windows.Devices.Enumeration.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/")); + CopyFiles(System.IO.Path.Combine(apiStagingDir, $"winrt/impl/Windows.Devices.Midi.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/")); + CopyFiles(System.IO.Path.Combine(apiStagingDir, $"winrt/impl/Windows.Foundation.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/")); + CopyFiles(System.IO.Path.Combine(apiStagingDir, $"winrt/impl/Windows.Foundation.Collections.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/")); + CopyFiles(System.IO.Path.Combine(apiStagingDir, $"winrt/impl/{MIDI2_NAMESPACE}.2.h"), System.IO.Path.Combine(platReleaseFolder, "winrt/impl/")); }); diff --git a/build/replace_running_service_x64.bat b/build/replace_running_service_x64.bat index 9a7d2a726..5d9bf144e 100644 --- a/build/replace_running_service_x64.bat +++ b/build/replace_running_service_x64.bat @@ -20,9 +20,9 @@ copy /Y %buildoutput%\Midi2.*Abstraction.dll %servicepath% copy /Y %buildoutput%\Midi2.*Transform.dll %servicepath% echo Windows.Devices.Midi2.dll -copy /Y %buildoutput%\Windows.Devices.Midi2.dll %apipath% +copy /Y %buildoutput%\*.Devices.Midi2.dll %apipath% echo Windows.Devices.Midi2.pri -copy /Y %buildoutput%\Windows.Devices.Midi2.pri %apipath% +copy /Y %buildoutput%\*.Devices.Midi2.pri %apipath% diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi index f46400e41..c3a7cb7a4 100644 --- a/build/staging/version/BundleInfo.wxi +++ b/build/staging/version/BundleInfo.wxi @@ -1,4 +1,4 @@ - + diff --git a/diagnostics/trace-logging/MidiServices.wprp b/diagnostics/trace-logging/MidiServices.wprp index d5c379fbc..a7a3a3eea 100644 --- a/diagnostics/trace-logging/MidiServices.wprp +++ b/diagnostics/trace-logging/MidiServices.wprp @@ -18,7 +18,7 @@ - + diff --git a/docs/api/service/MidiService.md b/docs/api/service/MidiService.md index c5694ae4c..6d6f034ad 100644 --- a/docs/api/service/MidiService.md +++ b/docs/api/service/MidiService.md @@ -12,6 +12,8 @@ has_children: false The MidiService class contains a number of static functions which enable working with the service outside of a specific session. +## Static Methods + ### Service Health | `IsAvailable()` | Returns true of Windows MIDI Services is available on this PC. Calling this function is typically the first step in using Windows MIDI Services in your application. | diff --git a/docs/api/service/README.md b/docs/api/service/README.md index 7da2bfba5..0d5886ea2 100644 --- a/docs/api/service/README.md +++ b/docs/api/service/README.md @@ -7,4 +7,4 @@ has_children: true # Service -The MidiService class is a utility class which provides access to health and status information related to the MidiSrv Service. This is also where you can check to see if Windows MIDI Services is available on this PC. +The `MidiService` class is a utility class which provides access to health and status information related to the MidiSrv Service. This is also where you can check to see if Windows MIDI Services is available on this PC. diff --git a/docs/api/session/MidiSession.md b/docs/api/session/MidiSession.md index 114a9e927..eeba0420a 100644 --- a/docs/api/session/MidiSession.md +++ b/docs/api/session/MidiSession.md @@ -8,47 +8,43 @@ has_children: false # MidiSession +## Remarks + Before you can connect to an endpoint, you must start a new MIDI session. An application may have any number of sessions open. For example, the application may open one session per open project, or one session per tab in the case of a browser. The lifetime of endpoint connections opened through a session are controlled through the session. ## Properties -| Property | Description | -| -------- | ----------- | | `Id` | Generated Id for the session | | `Name` | Name for this session. To change the name after creating the session, use the `UpdateName()` function. This will update the service | | `Settings` | The settings used to create this session | | `IsOpen` | True if this session is open and ready to use | | `Connections` | Map of all endpoint connections created through this session. Disconnecting an endpoint using `DisconnectEndpointConnection` will remove the connection from this map. The map key is the generated connection GUID that identifies an instance of an endpoint connection | -## Static Member Functions +## Static Methods The two static functions are factory-pattern methods for creating a new session. -| Static Function | Description | -| -------- | ----------- | | `CreateSession(sessionName)` | Create and return a new session with the specified name | | `CreateSession(sessionName, settings)` | Create and return a new session with the specified name and settings | -## Functions +## Methods -| Function | Description | -| -------- | ----------- | -| `CreateEndpointConnection(endpointDeviceId)` | Create a new connection to the specified endpoint device Id | -| `CreateEndpointConnection(endpointDeviceId, options)` | Create a new connection to the specified endpoint device Id, using the provided connection options | -| `CreateEndpointConnection(endpointDeviceId, options, settings)` | Create a new connection to the specified endpoint device Id, using the provided connection options and the endpoint-specific settings | +| `CreateEndpointConnection(String)` | Create a new connection to the specified endpoint device Id | +| `CreateEndpointConnection(String, Boolean)` | Create a new connection to the specified endpoint device Id, with an option to reconnect if a device is disconnected and then reconnected while the connection object is alive | +| `CreateEndpointConnection(String, Boolean, IMidiEndpointConnectionSettings)` | Create a new connection to the specified endpoint device Id, using the provided reconnect value and endpoint-specific settings | | `CreateVirtualDeviceAndConnection(deviceDefinition)` | Create the device-side of an app-to-app virtual endpoint. The calling application will perform as a MIDI device, responding to discovery and other MIDI 2.0 protocol messages. | | `DisconnectEndpointConnection(endpointConnectionId)` | Cleanly disconnect an endpoint connection and remove it from the connection map | | `UpdateName(newName)` | Update the name of this session locally and in the MIDI Service | > Note: If you manually close a MidiEndpointConnection using `IClosable` (or `IDisposable`), it will not be removed from the MidiSession's collection of endpoints. Instead, use the `DisconnectEndpointConnection` method of the session to keep both in sync. For that reason, we do not recommend that you wrap the `CreateEndpointConnection` calls in a using statement. -## IDL +## See also [MidiSession IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiSession.idl) -## Sample +### Sample ```cs using (var session = MidiSession.CreateSession("API Sample Session")) diff --git a/docs/api/simple-types/MidiUniqueId.md b/docs/api/simple-types/MidiUniqueId.md index a6fad7610..8f888be35 100644 --- a/docs/api/simple-types/MidiUniqueId.md +++ b/docs/api/simple-types/MidiUniqueId.md @@ -32,15 +32,11 @@ In the specification, Byte1 is the LSB and Byte4 is the MSB. We follow that conv ## Static Properties -| Static Property | Description | -| --------------- | ----------- | | `LabelShort` | Returns the localized abbreviation for use in UI. | | `LabelFull` | Returns the localized full name for use in UI. | ## Static Methods -| Function | Description | -| --------------- | ----------- | | `CreateBroadcast()` | Constructs a broadcast `MidiUniqueId` per the MIDI CI specification | | `CreateRandom()` | Constructs a random `MidiUniqueId` per the MIDI CI specification | diff --git a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.h b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.h index 127086eeb..34f5bcd62 100644 --- a/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.h +++ b/src/api/Abstraction/LoopbackMidiAbstraction/Midi2.LoopbackMidiBidi.h @@ -44,8 +44,8 @@ class CMidi2LoopbackMidiBiDi : //wil::com_ptr_nothrow m_associatedBiDiCallback; - wil::com_ptr_nothrow m_callback; - LONGLONG m_callbackContext; + wil::com_ptr_nothrow m_callback{ nullptr }; + LONGLONG m_callbackContext{}; std::wstring m_endpointId{}; }; diff --git a/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvSessionTracker.cpp b/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvSessionTracker.cpp index ce1d8599f..bf1fb7693 100644 --- a/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvSessionTracker.cpp +++ b/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvSessionTracker.cpp @@ -15,6 +15,44 @@ CMidi2MidiSrvSessionTracker::Initialize() return S_OK; } +HRESULT +CMidi2MidiSrvSessionTracker::VerifyConnectivity() +{ + TraceLoggingWrite( + MidiSrvAbstractionTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_INFO), + TraceLoggingPointer(this, "this") + ); + + wil::unique_rpc_binding bindingHandle; + + RETURN_IF_FAILED(GetMidiSrvBindingHandle(&bindingHandle)); + + RETURN_IF_FAILED([&]() + { + // RPC calls are placed in a lambda to work around compiler error C2712, limiting use of try/except blocks + // with structured exception handling. + RpcTryExcept RETURN_IF_FAILED(MidiSrvVerifyConnectivity(bindingHandle.get())); + RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) RETURN_IF_FAILED(HRESULT_FROM_WIN32(RpcExceptionCode())); + RpcEndExcept + + return S_OK; + }()); + + return S_OK; + + + + + + + + +} + + + _Use_decl_annotations_ HRESULT CMidi2MidiSrvSessionTracker::AddClientSession( diff --git a/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvSessionTracker.h b/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvSessionTracker.h index ec75e26ff..cb9931ad9 100644 --- a/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvSessionTracker.h +++ b/src/api/Abstraction/MidiSrvAbstraction/Midi2.MidiSrvSessionTracker.h @@ -27,6 +27,8 @@ class CMidi2MidiSrvSessionTracker : // This is called from the API STDMETHOD(GetSessionListJson(_Out_ BSTR* SessionList)); + STDMETHOD(VerifyConnectivity)(); + STDMETHOD(Cleanup()); private: diff --git a/src/api/Client/Midi2Client-Projection/nuget/Microsoft.Devices.Midi2.nuspec b/src/api/Client/Midi2Client-Projection/nuget/Microsoft.Devices.Midi2.nuspec index d1e2f0e46..af4efd8c3 100644 --- a/src/api/Client/Midi2Client-Projection/nuget/Microsoft.Devices.Midi2.nuspec +++ b/src/api/Client/Midi2Client-Projection/nuget/Microsoft.Devices.Midi2.nuspec @@ -2,7 +2,7 @@ Microsoft.Devices.Midi2 - 1.0.0-preview.6-0194 + 1.0.0-preview.6-0197 Microsoft Corporation Windows MIDI Services SDK. Minimum package necessary to use Windows MIDI Services from an app on a PC that has Windows MIDI Services installed. MIT diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection.cpp b/src/api/Client/Midi2Client/MidiEndpointConnection.cpp index 23c568f35..b1784e8ec 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection.cpp +++ b/src/api/Client/Midi2Client/MidiEndpointConnection.cpp @@ -70,14 +70,15 @@ namespace MIDI_CPP_NAMESPACE::implementation } - _Use_decl_annotations_ bool MidiEndpointConnection::InternalInitialize( winrt::guid sessionId, winrt::com_ptr serviceAbstraction, winrt::guid const connectionId, - winrt::hstring const endpointDeviceId + winrt::hstring const endpointDeviceId, + midi2::IMidiEndpointConnectionSettings connectionSettings, + bool autoReconnect ) { internal::LogInfo(__FUNCTION__, L"Internal Initialize "); @@ -89,10 +90,11 @@ namespace MIDI_CPP_NAMESPACE::implementation m_endpointDeviceId = endpointDeviceId; - //WINRT_ASSERT(!m_endpointDeviceId.empty()); - //WINRT_ASSERT(serviceAbstraction != nullptr); - m_serviceAbstraction = serviceAbstraction; + + m_connectionSettings = connectionSettings; + m_autoReconnect = autoReconnect; + return true; } @@ -104,46 +106,102 @@ namespace MIDI_CPP_NAMESPACE::implementation } } + // this does all the connection except plugin init + _Use_decl_annotations_ + bool MidiEndpointConnection::InternalOpen() + { + internal::LogInfo(__FUNCTION__, L"Connection Open "); + + + DWORD mmcssTaskId{}; + + LPCWSTR connectionSettingsJsonString = nullptr; + + if (m_connectionSettings != nullptr) + { + connectionSettingsJsonString = m_connectionSettings.SettingsJson().c_str(); + } + + ABSTRACTIONCREATIONPARAMS abstractionCreationParams{ MidiDataFormat_UMP, connectionSettingsJsonString }; + + auto result = m_endpointAbstraction->Initialize( + (LPCWSTR)(EndpointDeviceId().c_str()), + &abstractionCreationParams, + &mmcssTaskId, + (IMidiCallback*)(this), + 0, + m_sessionId + ); + + if (SUCCEEDED(result)) + { + m_isOpen = true; + m_closeHasBeenCalled = false; + } + else + { + internal::LogHresultError(__FUNCTION__, L"Failed to open connection", result); + } + + return true; + } + + // this does all the reconnection except for plugin init + _Use_decl_annotations_ + bool MidiEndpointConnection::InternalReopenAfterDisconnect() + { + internal::LogInfo(__FUNCTION__, L"Connection Reopen "); + + // Activate the endpoint for this device. Will fail if the device is not a BiDi device + if (!ActivateMidiStream()) + { + internal::LogGeneralError(__FUNCTION__, L"Could not activate MIDI Stream"); + + return false; + } + + return InternalOpen(); + } + _Use_decl_annotations_ bool MidiEndpointConnection::Open() { internal::LogInfo(__FUNCTION__, L"Connection Open "); - if (!IsOpen()) + // Activate the endpoint for this device. Will fail if the device is not a BiDi device + if (!ActivateMidiStream()) { - // Activate the endpoint for this device. Will fail if the device is not a BiDi device - if (!ActivateMidiStream(m_serviceAbstraction, __uuidof(IMidiBiDi), (void**)&m_endpointAbstraction)) - { - internal::LogGeneralError(__FUNCTION__, L"Could not activate MIDI Stream"); + internal::LogGeneralError(__FUNCTION__, L"Could not activate MIDI Stream"); + + return false; + } - return false; - } + if (!IsOpen()) + { if (m_endpointAbstraction != nullptr) { try { InitializePlugins(); - DWORD mmcssTaskId{}; - ABSTRACTIONCREATIONPARAMS abstractionCreationParams{ MidiDataFormat_UMP }; - - winrt::check_hresult(m_endpointAbstraction->Initialize( - (LPCWSTR)(EndpointDeviceId().c_str()), - &abstractionCreationParams, - &mmcssTaskId, - (IMidiCallback*)(this), - 0, - m_sessionId - )); - - // provide a copy to the output logic - m_isOpen = true; - - CallOnConnectionOpenedOnPlugins(); - - return true; + if (InternalOpen()) + { + // If autoReconnect, set up a watcher + if (m_autoReconnect) + { + StartDeviceWatcher(); + } + + CallOnConnectionOpenedOnPlugins(); + + return true; + } + else + { + return false; + } } catch (winrt::hresult_error const& ex) { @@ -151,12 +209,20 @@ namespace MIDI_CPP_NAMESPACE::implementation m_endpointAbstraction = nullptr; + return false; + } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception initializing endpoint interface. Service may be unavailable."); + + m_endpointAbstraction = nullptr; + return false; } } else { - internal::LogGeneralError(__FUNCTION__, L"Endpoint interface is nullptr"); + internal::LogGeneralError(__FUNCTION__, L"Endpoint abstraction interface is nullptr"); return false; } @@ -179,15 +245,7 @@ namespace MIDI_CPP_NAMESPACE::implementation try { CleanupPlugins(); - - if (m_endpointAbstraction != nullptr) - { - m_endpointAbstraction->Cleanup(); - m_endpointAbstraction = nullptr; - } - - // output is also input, so don't call cleanup - m_endpointAbstraction = nullptr; + DeactivateMidiStream(); m_isOpen = false; @@ -209,20 +267,31 @@ namespace MIDI_CPP_NAMESPACE::implementation } } + _Use_decl_annotations_ + bool MidiEndpointConnection::DeactivateMidiStream() + { + internal::LogInfo(__FUNCTION__, L"Deactivating MIDI Stream"); + if (m_endpointAbstraction != nullptr) + { + // todo: some error / hresult handling here + m_endpointAbstraction->Cleanup(); + m_endpointAbstraction == nullptr; + } + return true; + } + + _Use_decl_annotations_ - bool MidiEndpointConnection::ActivateMidiStream( - winrt::com_ptr serviceAbstraction, - const IID& iid, - void** iface) noexcept + bool MidiEndpointConnection::ActivateMidiStream() noexcept { internal::LogInfo(__FUNCTION__, L"Activating MIDI Stream"); try { - winrt::check_hresult(serviceAbstraction->Activate(iid, iface)); + winrt::check_hresult(m_serviceAbstraction->Activate(__uuidof(IMidiBiDi), (void**) &m_endpointAbstraction)); return true; } @@ -230,6 +299,12 @@ namespace MIDI_CPP_NAMESPACE::implementation { internal::LogHresultError(__FUNCTION__, L"hresult exception activating stream. Service may be unavailable", ex); + return false; + } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception activating stream. Service may be unavailable"); + return false; } } diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection.h b/src/api/Client/Midi2Client/MidiEndpointConnection.h index aab2d20a9..426e6b26c 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection.h +++ b/src/api/Client/Midi2Client/MidiEndpointConnection.h @@ -47,13 +47,6 @@ namespace MIDI_CPP_NAMESPACE::implementation void Tag(_In_ foundation::IInspectable value) noexcept { m_tag = value; } - bool InternalInitialize( - _In_ winrt::guid sessionId, - _In_ winrt::com_ptr serviceAbstraction, - _In_ winrt::guid const connectionId, - _In_ winrt::hstring const endpointDeviceId); - - midi2::MidiSendMessageResults SendSingleMessagePacket( _In_ midi2::IMidiUniversalPacket const& ump) noexcept; @@ -138,11 +131,6 @@ namespace MIDI_CPP_NAMESPACE::implementation ) noexcept; - _Success_(return == true) - bool Open(); - - void InternalClose(); - STDMETHOD(Callback)(_In_ PVOID data, _In_ UINT size, _In_ LONGLONG timestamp, _In_ LONGLONG) override; winrt::event_token MessageReceived(_In_ foundation::TypedEventHandler const& handler) @@ -162,10 +150,32 @@ namespace MIDI_CPP_NAMESPACE::implementation } void AddMessageProcessingPlugin(_In_ midi2::IMidiEndpointMessageProcessingPlugin const& plugin); - void RemoveMessageProcessingPlugin(_In_ winrt::guid id); + // public open method + _Success_(return == true) + bool Open(); + + + + _Success_(return == true) + bool InternalInitialize( + _In_ winrt::guid sessionId, + _In_ winrt::com_ptr serviceAbstraction, + _In_ winrt::guid const connectionId, + _In_ winrt::hstring const endpointDeviceId, + _In_ midi2::IMidiEndpointConnectionSettings connectionSettings, + _In_ bool autoReconnect + ); + + + void InternalClose(); + private: + midi2::IMidiEndpointConnectionSettings m_connectionSettings; + bool m_autoReconnect{ false }; + + uint64_t m_maxAllowedTimestampOffset{}; @@ -210,19 +220,42 @@ namespace MIDI_CPP_NAMESPACE::implementation void* GetUmpDataPointer( _In_ midi2::IMidiUniversalPacket const& ump, _Out_ uint8_t & dataSizeOut); + + midi2::MidiSendMessageResults SendMessageResultFromHRESULT(_In_ HRESULT hr); _Success_(return == true) - bool ActivateMidiStream( - _In_ winrt::com_ptr serviceAbstraction, - _In_ const IID & iid, - _Out_ void** iface) noexcept; + bool ActivateMidiStream() noexcept; void InitializePlugins() noexcept; void CallOnConnectionOpenedOnPlugins() noexcept; void CleanupPlugins() noexcept; + _Success_(return == true) + bool InternalOpen(); + + _Success_(return == true) + bool DeactivateMidiStream(); + + _Success_(return == true) + bool InternalReopenAfterDisconnect(); + + + winrt::Windows::Devices::Enumeration::DeviceWatcher m_autoReconnectDeviceWatcher{ nullptr }; + + _Success_(return == true) + bool StartDeviceWatcher(); + + _Success_(return == true) + bool StopDeviceWatcher(); + + void DeviceAddedHandler(_In_ enumeration::DeviceWatcher source, _In_ enumeration::DeviceInformation args); + void DeviceUpdatedHandler(_In_ enumeration::DeviceWatcher source, _In_ enumeration::DeviceInformationUpdate args); + void DeviceRemovedHandler(_In_ enumeration::DeviceWatcher source, _In_ enumeration::DeviceInformationUpdate args); + + enumeration::DeviceWatcher::Added_revoker m_autoReconnectDeviceWatcherAddedRevoker; + enumeration::DeviceWatcher::Updated_revoker m_autoReconnectDeviceWatcherUpdatedRevoker; + enumeration::DeviceWatcher::Removed_revoker m_autoReconnectDeviceWatcherRemovedRevoker; - midi2::MidiSendMessageResults SendMessageResultFromHRESULT(_In_ HRESULT hr); }; diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection.idl b/src/api/Client/Midi2Client/MidiEndpointConnection.idl index c94b7f588..4547d8258 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection.idl +++ b/src/api/Client/Midi2Client/MidiEndpointConnection.idl @@ -22,7 +22,6 @@ namespace MIDI_MIDL_NAMESPACE [default_interface] runtimeclass MidiEndpointConnection : IMidiMessageReceivedEventSource, IMidiEndpointConnectionSource - { static String GetDeviceSelector(); diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection_AutoReconnect.cpp b/src/api/Client/Midi2Client/MidiEndpointConnection_AutoReconnect.cpp new file mode 100644 index 000000000..fceef36ae --- /dev/null +++ b/src/api/Client/Midi2Client/MidiEndpointConnection_AutoReconnect.cpp @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#include "pch.h" +#include "MidiEndpointConnection.h" + + +// auto deviceAddedHandler = TypedEventHandler(this, &CMidi2KSMidiEndpointManager::OnDeviceAdded); +// auto deviceUpdatedHandler = TypedEventHandler(this, &CMidi2KSMidiEndpointManager::OnDeviceUpdated); +// auto deviceRemovedHandler = TypedEventHandler(this, &CMidi2KSMidiEndpointManager::OnDeviceRemoved); +// m_DeviceAdded = m_Watcher.Added(winrt::auto_revoke, deviceAddedHandler); +// m_DeviceUpdated = m_Watcher.Updated(winrt::auto_revoke, deviceUpdatedHandler); +// m_DeviceRemoved = m_Watcher.Removed(winrt::auto_revoke, deviceRemovedHandler); + + +namespace MIDI_CPP_NAMESPACE::implementation +{ + _Use_decl_annotations_ + bool MidiEndpointConnection::StartDeviceWatcher() + { + // start the device watcher for the specific Id + winrt::hstring deviceSelector( + L"Id:=\"" + m_endpointDeviceId + L"\" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True"); + + m_autoReconnectDeviceWatcher = enumeration::DeviceInformation::CreateWatcher(deviceSelector); + + if (m_autoReconnectDeviceWatcher != nullptr) + { + if (m_autoReconnectDeviceWatcher.Status() == enumeration::DeviceWatcherStatus::Stopped) + { + auto deviceAddedHandler = foundation::TypedEventHandler(this, &MidiEndpointConnection::DeviceAddedHandler); + auto deviceUpdatedHandler = foundation::TypedEventHandler(this, &MidiEndpointConnection::DeviceUpdatedHandler); + auto deviceRemovedHandler = foundation::TypedEventHandler(this, &MidiEndpointConnection::DeviceRemovedHandler); + + m_autoReconnectDeviceWatcherAddedRevoker = m_autoReconnectDeviceWatcher.Added(winrt::auto_revoke, deviceAddedHandler); + m_autoReconnectDeviceWatcherUpdatedRevoker = m_autoReconnectDeviceWatcher.Updated(winrt::auto_revoke, deviceUpdatedHandler); + m_autoReconnectDeviceWatcherRemovedRevoker = m_autoReconnectDeviceWatcher.Removed(winrt::auto_revoke, deviceRemovedHandler); + + m_autoReconnectDeviceWatcher.Start(); + + return true; + } + else + { + // can only start the watcher when it's in a stopped state + return false; + } + } + else + { + return false; + } + } + + _Use_decl_annotations_ + bool MidiEndpointConnection::StopDeviceWatcher() + { + if (m_autoReconnectDeviceWatcher != nullptr) + { + m_autoReconnectDeviceWatcher.Stop(); + + m_autoReconnectDeviceWatcherAddedRevoker.revoke(); + m_autoReconnectDeviceWatcherUpdatedRevoker.revoke(); + m_autoReconnectDeviceWatcherRemovedRevoker.revoke(); + + return true; + } + else + { + return false; + } + } + + + + _Use_decl_annotations_ + void MidiEndpointConnection::DeviceAddedHandler( + enumeration::DeviceWatcher source, + enumeration::DeviceInformation args + ) + { + UNREFERENCED_PARAMETER(source); + UNREFERENCED_PARAMETER(args); + + internal::LogInfo(__FUNCTION__, args.Id().c_str()); + + // if not m_isOpen, then perform all the opening logic again + if (!m_isOpen) + { + InternalReopenAfterDisconnect(); + } + + } + + _Use_decl_annotations_ + void MidiEndpointConnection::DeviceUpdatedHandler( + enumeration::DeviceWatcher source, + enumeration::DeviceInformationUpdate args + ) + { + UNREFERENCED_PARAMETER(source); + UNREFERENCED_PARAMETER(args); + + internal::LogInfo(__FUNCTION__, args.Id().c_str()); + + + // may have nothing to do here. TBD + + } + + + _Use_decl_annotations_ + void MidiEndpointConnection::DeviceRemovedHandler( + enumeration::DeviceWatcher source, + enumeration::DeviceInformationUpdate args + ) + { + UNREFERENCED_PARAMETER(source); + UNREFERENCED_PARAMETER(args); + + internal::LogInfo(__FUNCTION__, args.Id().c_str()); + + InternalClose(); + + } +} \ No newline at end of file diff --git a/src/api/Client/Midi2Client/MidiService.cpp b/src/api/Client/Midi2Client/MidiService.cpp index 98df167fa..a5c86542d 100644 --- a/src/api/Client/Midi2Client/MidiService.cpp +++ b/src/api/Client/Midi2Client/MidiService.cpp @@ -19,14 +19,28 @@ namespace MIDI_CPP_NAMESPACE::implementation try { - //auto serviceAbstraction = winrt::create_instance(__uuidof(Midi2MidiSrvAbstraction), CLSCTX_ALL); - auto serviceAbstraction = winrt::try_create_instance(__uuidof(Midi2MidiSrvAbstraction), CLSCTX_ALL); + auto serviceAbstraction = winrt::create_instance(__uuidof(Midi2MidiSrvAbstraction), CLSCTX_ALL); + //auto serviceAbstraction = winrt::try_create_instance(__uuidof(Midi2MidiSrvAbstraction), CLSCTX_ALL); // winrt::try_create_instance indicates failure by returning an empty com ptr if (serviceAbstraction == nullptr) { return false; } + + winrt::com_ptr tracker; + + auto sessionTrackerResult = serviceAbstraction->Activate(__uuidof(IMidiSessionTracker), (void**)&tracker); + + if (SUCCEEDED(sessionTrackerResult)) + { + if (SUCCEEDED(tracker->VerifyConnectivity())) + { + return true; + } + } + + return false; } catch (...) { @@ -47,185 +61,211 @@ namespace MIDI_CPP_NAMESPACE::implementation auto responseSummary = winrt::make_self(); - if (responseSummary == nullptr) + try { - // just need to fail - return nullptr; - } + if (responseSummary == nullptr) + { + internal::LogGeneralError(__FUNCTION__, L"Could not create response summary."); - if (pingCount == 0) - { - responseSummary->InternalSetFailed(L"Ping count is zero."); - return *responseSummary; - } + // just need to fail + return nullptr; + } - std::vector> pings{}; - pings.resize(pingCount); + if (pingCount == 0) + { + responseSummary->InternalSetFailed(L"Ping count is zero."); + return *responseSummary; + } - if (timeoutMilliseconds == 0) - { - responseSummary->InternalSetFailed(L"Timeout milliseconds is zero."); - return *responseSummary; - } + std::vector> pings{}; + pings.resize(pingCount); - // we use the full session API from here to get accurate timing + if (timeoutMilliseconds == 0) + { + responseSummary->InternalSetFailed(L"Timeout milliseconds is zero."); + return *responseSummary; + } - auto session = midi2::MidiSession::CreateSession(L"Ping Test"); + // we use the full session API from here to get accurate timing - if (session == nullptr) - { - responseSummary->InternalSetFailed(L"Unable to create session."); - return *responseSummary; - } + auto session = midi2::MidiSession::CreateSession(L"Ping Test"); - // This ID must be consistent with what the service is set up to use. + if (session == nullptr) + { + responseSummary->InternalSetFailed(L"Unable to create session."); + return *responseSummary; + } - auto endpoint = session.CreateEndpointConnection(MIDI_DIAGNOSTICS_PING_BIDI_ID); + // This ID must be consistent with what the service is set up to use. - if (endpoint == nullptr) - { - responseSummary->InternalSetFailed(L"Unable to create ping endpoint."); - return *responseSummary; - } + auto endpoint = session.CreateEndpointConnection(MIDI_DIAGNOSTICS_PING_BIDI_ID, false); + if (endpoint == nullptr) + { + responseSummary->InternalSetFailed(L"Unable to create ping endpoint."); + return *responseSummary; + } - // originally I was going to use a random number for this, but using the low bits of - // the timestamp makes more sense, and will be unique enough for this. - uint32_t pingSourceId = (uint32_t)(MidiClock::Now() & 0x00000000FFFFFFFF); + // originally I was going to use a random number for this, but using the low bits of + // the timestamp makes more sense, and will be unique enough for this. - wil::unique_event_nothrow allMessagesReceived; - allMessagesReceived.create(); + uint32_t pingSourceId = (uint32_t)(MidiClock::Now() & 0x00000000FFFFFFFF); - uint8_t receivedCount{ 0 }; + wil::unique_event_nothrow allMessagesReceived; + allMessagesReceived.create(); - // we have the session and the endpoint, so set up the ping handler - // changing any of this internal implementation detail requires a coordinated change with the server code - // this is not an official UMP ping message. This is just an internal representation that is - // recognized only by this endpoint, and should never be used elsewhere. + uint8_t receivedCount{ 0 }; + // we have the session and the endpoint, so set up the ping handler + // changing any of this internal implementation detail requires a coordinated change with the server code + // this is not an official UMP ping message. This is just an internal representation that is + // recognized only by this endpoint, and should never be used elsewhere. - auto MessageReceivedHandler = [&](foundation::IInspectable const& /*sender*/, midi2::MidiMessageReceivedEventArgs const& args) - { - internal::MidiTimestamp actualReceiveEventTimestamp = MidiClock::Now(); + auto MessageReceivedHandler = [&](foundation::IInspectable const& /*sender*/, midi2::MidiMessageReceivedEventArgs const& args) + { + internal::MidiTimestamp actualReceiveEventTimestamp = MidiClock::Now(); - uint32_t word0; - uint32_t word1; - uint32_t word2; - uint32_t word3; + uint32_t word0; + uint32_t word1; + uint32_t word2; + uint32_t word3; - args.FillWords(word0, word1, word2, word3); + args.FillWords(word0, word1, word2, word3); - // ensure this is a ping message, just in case + // ensure this is a ping message, just in case - if (word0 == INTERNAL_PING_RESPONSE_UMP_WORD0 && word1 == pingSourceId) - { - if (word2 < pings.size()) + if (word0 == INTERNAL_PING_RESPONSE_UMP_WORD0 && word1 == pingSourceId) { - // word2 is our ping index - pings[word2]->InternalSetReceiveInfo(args.Timestamp(), actualReceiveEventTimestamp); + if (word2 < pings.size()) + { + // word2 is our ping index + pings[word2]->InternalSetReceiveInfo(args.Timestamp(), actualReceiveEventTimestamp); - receivedCount++; + receivedCount++; - if (receivedCount == pingCount) + if (receivedCount == pingCount) + { + allMessagesReceived.SetEvent(); + } + } + else { - allMessagesReceived.SetEvent(); + // something really wrong happened. Our index has been messed up. } } else { - // something really wrong happened. Our index has been messed up. + // someone else is sending stuff to this ping service. Naughty if not another ping. } - } - else - { - // someone else is sending stuff to this ping service. Naughty if not another ping. - } + }; - }; + // any failures after this need to revoke the event handler as well + auto eventRevokeToken = endpoint.MessageReceived(winrt::auto_revoke, MessageReceivedHandler); - // any failures after this need to revoke the event handler as well - auto eventRevokeToken = endpoint.MessageReceived(MessageReceivedHandler); + // open the endpoint. We've already set options for it not to send out discovery messages + if (!endpoint.Open()) + { + internal::LogGeneralError(__FUNCTION__, L"Could not open ping endpoint."); - // open the endpoint. We've already set options for it not to send out discovery messages - if (!endpoint.Open()) - { - internal::LogGeneralError(__FUNCTION__, L"Could not open ping endpoint."); + responseSummary->InternalSetFailed(L"Endpoint open failed. The service may be unavailable."); - responseSummary->InternalSetFailed(L"Endpoint open failed. The service may be unavailable."); - endpoint.MessageReceived(eventRevokeToken); + session.DisconnectEndpointConnection(endpoint.ConnectionId()); - session.DisconnectEndpointConnection(endpoint.ConnectionId()); + return *responseSummary; + } + // send out the ping messages - return *responseSummary; - } + for (uint32_t pingIndex = 0; pingIndex < pingCount; pingIndex++) + { + internal::PackedPingRequestUmp request; - // send out the ping messages + auto response = winrt::make_self(); - for (uint32_t pingIndex = 0; pingIndex < pingCount; pingIndex++) - { - internal::PackedPingRequestUmp request; + internal::MidiTimestamp timestamp = MidiClock::Now(); - auto response = winrt::make_self(); + // Add this info to the tracking before we send, so no race condition + // granted that this adds a few ticks to add this to the collection and build the object - internal::MidiTimestamp timestamp = MidiClock::Now(); + response->InternalSetSendInfo(pingSourceId, pingIndex, timestamp); - // Add this info to the tracking before we send, so no race condition - // granted that this adds a few ticks to add this to the collection and build the object + // + // TODO: Should this use copy_from? + pings[pingIndex] = response; - response->InternalSetSendInfo(pingSourceId, pingIndex, timestamp); + // send the ping - // - // TODO: Should this use copy_from? - pings[pingIndex] = response; + auto sendMessageResult = endpoint.SendSingleMessageWords(timestamp, request.Word0, pingSourceId, pingIndex, request.Padding); - // send the ping - endpoint.SendSingleMessageWords(timestamp, request.Word0, pingSourceId, pingIndex, request.Padding); + if (midi2::MidiEndpointConnection::SendMessageFailed(sendMessageResult)) + { + internal::LogGeneralError(__FUNCTION__, L"Ping send message failed."); - //Sleep(0); - } + responseSummary->InternalSetFailed(L"Sending message failed"); - // Wait for all responses to come in (receivedCount == pingCount). If not all responses come back, report the failure. - if (!allMessagesReceived.wait(timeoutMilliseconds)) - { - responseSummary->InternalSetFailed(L"Not all ping responses received within appropriate time window."); - internal::LogGeneralError(__FUNCTION__, L"Not all ping responses received within appropriate time window."); - } - else - { - // all received - responseSummary->InternalSetSucceeded(); + session.DisconnectEndpointConnection(endpoint.ConnectionId()); - // copy over the holding array into the response and also calculate the totals + return *responseSummary; + } - uint64_t totalPing{ 0 }; + //Sleep(0); + } - for (const auto& response : pings) + // Wait for all responses to come in (receivedCount == pingCount). If not all responses come back, report the failure. + if (!allMessagesReceived.wait(timeoutMilliseconds)) { - totalPing += response->ClientDeltaTimestamp(); + responseSummary->InternalSetFailed(L"Not all ping responses received within appropriate time window."); + internal::LogGeneralError(__FUNCTION__, L"Not all ping responses received within appropriate time window."); + } + else + { + // all received + responseSummary->InternalSetSucceeded(); + + // copy over the holding array into the response and also calculate the totals + + uint64_t totalPing{ 0 }; + + for (const auto& response : pings) + { + totalPing += response->ClientDeltaTimestamp(); - responseSummary->InternalAddResponse(*response); + responseSummary->InternalAddResponse(*response); - // does I need to remove the com_ptr ref or will going out of scope be sufficient? + // does I need to remove the com_ptr ref or will going out of scope be sufficient? + } + + uint64_t averagePing = totalPing / responseSummary->Responses().Size(); + + responseSummary->InternalSetTotals(totalPing, averagePing); } - uint64_t averagePing = totalPing / responseSummary->Responses().Size(); + session.DisconnectEndpointConnection(endpoint.ConnectionId()); + session.Close(); - responseSummary->InternalSetTotals(totalPing, averagePing); + return *responseSummary; } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception pinging service."); - // unwire the event and close the session. - endpoint.MessageReceived(eventRevokeToken); - - session.DisconnectEndpointConnection(endpoint.ConnectionId()); - session.Close(); + if (responseSummary != nullptr) + { + responseSummary->InternalSetFailed(L"Exception pinging service"); - return *responseSummary; + return *responseSummary; + } + else + { + return nullptr; + } + } } + _Use_decl_annotations_ - midi2::MidiServicePingResponseSummary MidiService::PingService(uint8_t const pingCount) noexcept + midi2::MidiServicePingResponseSummary MidiService::PingService(uint8_t const pingCount) noexcept { return PingService(pingCount, pingCount * 20 + 1000); } diff --git a/src/api/Client/Midi2Client/MidiSession.cpp b/src/api/Client/Midi2Client/MidiSession.cpp index 033c7b369..5fbb2042c 100644 --- a/src/api/Client/Midi2Client/MidiSession.cpp +++ b/src/api/Client/Midi2Client/MidiSession.cpp @@ -154,7 +154,8 @@ namespace MIDI_CPP_NAMESPACE::implementation _Use_decl_annotations_ midi2::MidiEndpointConnection MidiSession::CreateEndpointConnection( winrt::hstring const& endpointDeviceId, - midi2::IMidiEndpointConnectionSettings const& /*settings*/ + bool const autoReconnect, + midi2::IMidiEndpointConnectionSettings const& settings ) noexcept { internal::LogInfo(__FUNCTION__, L"Creating Endpoint Connection"); @@ -167,7 +168,7 @@ namespace MIDI_CPP_NAMESPACE::implementation // generate internal endpoint Id auto connectionInstanceId = foundation::GuidHelper::CreateNewGuid(); - if (endpointConnection->InternalInitialize(m_id, m_serviceAbstraction, connectionInstanceId, normalizedDeviceId)) + if (endpointConnection->InternalInitialize(m_id, m_serviceAbstraction, connectionInstanceId, normalizedDeviceId, settings, autoReconnect)) { m_connections.Insert(connectionInstanceId, *endpointConnection); @@ -190,12 +191,21 @@ namespace MIDI_CPP_NAMESPACE::implementation } } + _Use_decl_annotations_ + midi2::MidiEndpointConnection MidiSession::CreateEndpointConnection( + winrt::hstring const& endpointDeviceId, + bool autoReconnect + ) noexcept + { + return CreateEndpointConnection(endpointDeviceId, autoReconnect, nullptr); + } + _Use_decl_annotations_ midi2::MidiEndpointConnection MidiSession::CreateEndpointConnection( winrt::hstring const& endpointDeviceId ) noexcept { - return CreateEndpointConnection(endpointDeviceId, nullptr); + return CreateEndpointConnection(endpointDeviceId, false, nullptr); } @@ -393,7 +403,7 @@ namespace MIDI_CPP_NAMESPACE::implementation internal::LogInfo(__FUNCTION__, L"Virtual device creation worked."); // Creating the device succeeded, so create the connection - auto connection = CreateEndpointConnection(endpointDeviceId, nullptr); + auto connection = CreateEndpointConnection(endpointDeviceId, false, nullptr); internal::LogInfo(__FUNCTION__, L"Created endpoint connection"); diff --git a/src/api/Client/Midi2Client/MidiSession.h b/src/api/Client/Midi2Client/MidiSession.h index 323fda290..67233e76f 100644 --- a/src/api/Client/Midi2Client/MidiSession.h +++ b/src/api/Client/Midi2Client/MidiSession.h @@ -19,12 +19,12 @@ namespace MIDI_CPP_NAMESPACE::implementation MidiSession() = default; ~MidiSession(); - static MIDI_CPP_NAMESPACE::MidiSession CreateSession( + static midi2::MidiSession CreateSession( _In_ hstring const& sessionName, - _In_ MIDI_CPP_NAMESPACE::MidiSessionSettings const& settings + _In_ midi2::MidiSessionSettings const& settings ) noexcept; - static MIDI_CPP_NAMESPACE::MidiSession CreateSession( + static midi2::MidiSession CreateSession( _In_ hstring const& sessionName ) noexcept; @@ -38,12 +38,18 @@ namespace MIDI_CPP_NAMESPACE::implementation foundation::Collections::IMapView Connections() { return m_connections.GetView(); } - MIDI_CPP_NAMESPACE::MidiEndpointConnection CreateEndpointConnection( + midi2::MidiEndpointConnection CreateEndpointConnection( _In_ winrt::hstring const& endpointDeviceId ) noexcept; - MIDI_CPP_NAMESPACE::MidiEndpointConnection CreateEndpointConnection( + midi2::MidiEndpointConnection CreateEndpointConnection( _In_ winrt::hstring const& endpointDeviceId, + _In_ bool const autoReconnect + ) noexcept; + + midi2::MidiEndpointConnection CreateEndpointConnection( + _In_ winrt::hstring const& endpointDeviceId, + _In_ bool const autoReconnect, _In_ midi2::IMidiEndpointConnectionSettings const& settings ) noexcept; diff --git a/src/api/Client/Midi2Client/MidiSession.idl b/src/api/Client/Midi2Client/MidiSession.idl index 8096a0be0..d4f3fdcf8 100644 --- a/src/api/Client/Midi2Client/MidiSession.idl +++ b/src/api/Client/Midi2Client/MidiSession.idl @@ -38,6 +38,12 @@ namespace MIDI_MIDL_NAMESPACE MidiEndpointConnection CreateEndpointConnection( String endpointDeviceId, + Boolean autoReconnect + ); + + MidiEndpointConnection CreateEndpointConnection( + String endpointDeviceId, + Boolean autoReconnect, IMidiEndpointConnectionSettings settings ); diff --git a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj index ae0767adb..733058580 100644 --- a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj +++ b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj @@ -344,9 +344,9 @@ MidiStreamConfigurationRequestedSettings.idl - - MidiStreamConfigurationRequestReceivedEventArgs.idl - + + MidiStreamConfigurationRequestReceivedEventArgs.idl + MidiStreamMessageBuilder.idl @@ -394,6 +394,7 @@ MidiEndpointConnection.idl + MidiEndpointConnection.idl @@ -490,9 +491,9 @@ MidiStreamConfigurationRequestedSettings.idl - - MidiStreamConfigurationRequestReceivedEventArgs.idl - + + MidiStreamConfigurationRequestReceivedEventArgs.idl + MidiStreamMessageBuilder.idl diff --git a/src/api/Client/Midi2Client/pch.h b/src/api/Client/Midi2Client/pch.h index 7c2765e69..87f6581a0 100644 --- a/src/api/Client/Midi2Client/pch.h +++ b/src/api/Client/Midi2Client/pch.h @@ -31,6 +31,7 @@ #include namespace json = ::winrt::Windows::Data::Json; +namespace enumeration = ::winrt::Windows::Devices::Enumeration; #include #include diff --git a/src/api/Client/Midi2Client/trace_logging.cpp b/src/api/Client/Midi2Client/trace_logging.cpp index 32d87c8c7..231b3a7b7 100644 --- a/src/api/Client/Midi2Client/trace_logging.cpp +++ b/src/api/Client/Midi2Client/trace_logging.cpp @@ -14,13 +14,14 @@ namespace WindowsMidiServicesInternal { bool g_traceLoggingRegistered = false; - // Microsoft.Devices.Midi2.Api hashed tracing guid: 5c055d9e-0ac2-58ee-f647-c1f00339a6ec + // Microsoft.Windows.Devices.Midi2.Api hashed tracing guid: 2c7d1437-4f43-5713-170b-adbe4cff4dff + // PS> [System.Diagnostics.Tracing.EventSource]::new("Microsoft.Windows.Devices.Midi2.Api").Guid TRACELOGGING_DEFINE_PROVIDER( g_hLoggingProvider, TRACELOGGING_PROVIDER_NAME, - // {5c055d9e-0ac2-58ee-f647-c1f00339a6ec} - (0x5c055d9e, 0x0ac2, 0x58ee, 0xf6, 0x47, 0xc1, 0xf0, 0x03, 0x39, 0xa6, 0xec)); + // {2c7d1437-4f43-5713-170b-adbe4cff4dff} + (0x2c7d1437, 0x4f43, 0x5713, 0x17, 0x0b, 0xad, 0xbe, 0x4c, 0xff, 0x4d, 0xff)); void WINAPI LoggingProviderEnabledCallback( _In_ LPCGUID /*sourceId*/, @@ -93,6 +94,28 @@ namespace WindowsMidiServicesInternal ); } + _Use_decl_annotations_ + void LogHresultError( + const char* location, + const wchar_t* message, + HRESULT hr) noexcept + { + //OutputDebugString(L"" __FUNCTION__ L"API HRESULT Error. Use tracing provider for details."); + + if (!g_traceLoggingRegistered) RegisterTraceLogging(); + + TraceLoggingWrite( + g_hLoggingProvider, + "MIDI.HresultError", + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingKeyword(TRACE_KEYWORD_API_GENERAL), + TraceLoggingHResult(hr, "HRESULT"), + TraceLoggingString(location, "Location"), + TraceLoggingWideString(message, "Message") + ); + } + + _Use_decl_annotations_ void LogGeneralError( const char* location, diff --git a/src/api/Client/Midi2Client/trace_logging.h b/src/api/Client/Midi2Client/trace_logging.h index 5c2258b33..dd6f34562 100644 --- a/src/api/Client/Midi2Client/trace_logging.h +++ b/src/api/Client/Midi2Client/trace_logging.h @@ -43,6 +43,11 @@ namespace WindowsMidiServicesInternal _In_z_ const wchar_t* message, _In_ winrt::hresult_error const& ex) noexcept; + void LogHresultError( + _In_z_ const char* location, + _In_z_ const wchar_t* message, + _In_ HRESULT const hr) noexcept; + void LogGeneralError( _In_z_ const char* location, _In_z_ const wchar_t* message) noexcept; diff --git a/src/api/InBoxApps/mididmp/main.cpp b/src/api/InBoxApps/mididmp/main.cpp index 0ca6c54e4..b65cee97a 100644 --- a/src/api/InBoxApps/mididmp/main.cpp +++ b/src/api/InBoxApps/mididmp/main.cpp @@ -9,26 +9,12 @@ #pragma once -#include - -#include -#include -#include - -#include - -#include - -#include -#include - - #include "pch.h" using namespace winrt; using namespace winrt::Windows::Foundation; -const std::wstring fieldSeparator = L" : "; +const std::wstring fieldSeparator = MIDIDMP_FIELD_SEPARATOR; void OutputBlankLine() { @@ -38,7 +24,7 @@ void OutputBlankLine() void OutputSectionHeader(_In_ std::wstring headerText) { - const std::wstring sectionHeaderSeparator = std::wstring(79, '='); + const std::wstring sectionHeaderSeparator = std::wstring(MIDIDMP_SEPARATOR_REPEATING_CHAR_COUNT_PER_LINE, MIDIDMP_SECTION_HEADER_SEPARATOR_CHAR); std::wcout << std::endl @@ -53,7 +39,7 @@ void OutputSectionHeader(_In_ std::wstring headerText) void OutputItemSeparator() { - const std::wstring itemSeparator = std::wstring(79, '-'); + const std::wstring itemSeparator = std::wstring(MIDIDMP_SEPARATOR_REPEATING_CHAR_COUNT_PER_LINE, MIDIDMP_ITEM_SEPARATOR_CHAR); std::wcout << itemSeparator @@ -67,10 +53,19 @@ void OutputHeader(_In_ std::wstring headerText) << std::endl; } +void OutputFieldLabel(_In_ std::wstring fieldName) +{ + std::wcout + << std::setw(MIDIDMP_MAX_FIELD_LABEL_WIDTH) + << std::left + << fieldName; +} + void OutputStringField(_In_ std::wstring fieldName, _In_ winrt::hstring value) { + OutputFieldLabel(fieldName); + std::wcout - << fieldName << fieldSeparator << value.c_str() << std::endl; @@ -78,13 +73,25 @@ void OutputStringField(_In_ std::wstring fieldName, _In_ winrt::hstring value) void OutputStringField(_In_ std::wstring fieldName, _In_ std::wstring value) { + OutputFieldLabel(fieldName); + std::wcout - << fieldName << fieldSeparator << value << std::endl; } +void OutputBooleanField(_In_ std::wstring fieldName, _In_ bool value) +{ + OutputFieldLabel(fieldName); + + std::wcout + << fieldSeparator + << std::boolalpha + << value + << std::endl; +} + void OutputGuidField(_In_ std::wstring fieldName, _In_ winrt::guid value) { OutputStringField(fieldName, internal::GuidToString(value)); @@ -96,7 +103,7 @@ void OutputCurrentTime() { auto const time = std::chrono::current_zone()->to_local(std::chrono::system_clock::now()); - OutputStringField(L"current_time", std::format(L"{:%Y-%m-%d %X}", time)); + OutputStringField(MIDIDMP_FIELD_LABEL_CURRENT_TIME, std::format(L"{:%Y-%m-%d %X}", time)); } @@ -104,8 +111,9 @@ void OutputCurrentTime() void OutputTimestampField(_In_ std::wstring fieldName, _In_ uint64_t value) { + OutputFieldLabel(fieldName); + std::wcout - << fieldName << fieldSeparator << value << std::endl; @@ -113,9 +121,23 @@ void OutputTimestampField(_In_ std::wstring fieldName, _In_ uint64_t value) void OutputNumericField(_In_ std::wstring fieldName, _In_ uint32_t value) { + OutputFieldLabel(fieldName); + + std::wcout + << fieldSeparator + << std::dec + << value + << std::endl; +} + +void OutputHexNumericField(_In_ std::wstring fieldName, _In_ uint32_t value) +{ + OutputFieldLabel(fieldName); + std::wcout - << fieldName << fieldSeparator + << L"0x" + << std::hex << value << std::endl; } @@ -125,8 +147,9 @@ void OutputError(_In_ std::wstring errorMessage) { const std::wstring errorLabel = L"ERROR"; + OutputFieldLabel(errorLabel); + std::wcout - << errorLabel << fieldSeparator << errorMessage << std::endl; @@ -142,7 +165,7 @@ bool DoSectionTransports(_In_ bool verbose) try { - OutputSectionHeader(L"enum_transports"); + OutputSectionHeader(MIDIDMP_SECTION_LABEL_ENUM_TRANSPORTS); auto transports = midi2::MidiService::GetInstalledTransportPlugins(); @@ -150,11 +173,13 @@ bool DoSectionTransports(_In_ bool verbose) { for (auto const& transport : transports) { - OutputGuidField(L"id", transport.Id()); - OutputStringField(L"name", transport.Name()); - OutputStringField(L"version", transport.Version()); - OutputStringField(L"author", transport.Author()); - OutputStringField(L"description", transport.Description()); + OutputGuidField(MIDIDMP_FIELD_LABEL_TRANSPORT_ID, transport.Id()); + OutputStringField(MIDIDMP_FIELD_LABEL_TRANSPORT_NAME, transport.Name()); + OutputStringField(MIDIDMP_FIELD_LABEL_TRANSPORT_MNEMONIC, transport.Mnemonic()); + OutputStringField(MIDIDMP_FIELD_LABEL_TRANSPORT_VERSION, transport.Version()); + OutputStringField(MIDIDMP_FIELD_LABEL_TRANSPORT_AUTHOR, transport.Author()); + OutputStringField(MIDIDMP_FIELD_LABEL_TRANSPORT_DESCRIPTION, transport.Description()); + OutputItemSeparator(); } } else @@ -174,7 +199,7 @@ bool DoSectionTransports(_In_ bool verbose) bool DoSectionMidi2ApiEndpoints(_In_ bool verbose) { - OutputSectionHeader(L"enum_ump_api_endpoints"); + OutputSectionHeader(MIDIDMP_SECTION_LABEL_MIDI2_API_ENDPOINTS); // list devices @@ -206,25 +231,25 @@ bool DoSectionMidi2ApiEndpoints(_In_ bool verbose) // These names should not be localized because customers may parse these output fields - OutputStringField(L"endpoint_device_id", device.Id()); - OutputStringField(L"name", device.Name()); - OutputStringField(L"transport_mnemonic", device.TransportMnemonic()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_ID, device.Id()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_NAME, device.Name()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_MNEMONIC, device.TransportMnemonic()); if (verbose) { - OutputStringField(L"name_user_supplied", device.UserSuppliedName()); - OutputStringField(L"name_endpoint_supplied", device.EndpointSuppliedName()); - OutputStringField(L"name_transport_supplied", device.TransportSuppliedName()); - OutputStringField(L"description_transport_supplied", device.TransportSuppliedDescription()); - OutputStringField(L"description_user_supplied", device.UserSuppliedDescription()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_USER_SUPPLIED_NAME, device.UserSuppliedName()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_ENDPOINT_SUPPLIED_NAME, device.EndpointSuppliedName()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_SUPPLIED_NAME, device.TransportSuppliedName()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_SUPPLIED_DESC, device.TransportSuppliedDescription()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_USER_SUPPLIED_DESC, device.UserSuppliedDescription()); } auto parent = device.GetParentDeviceInformation(); if (parent != nullptr) { - OutputStringField(L"parent_id", parent.Id()); - OutputStringField(L"parent_name", parent.Name()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_PARENT_ID, parent.Id()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_PARENT_NAME, parent.Name()); } else { @@ -250,7 +275,7 @@ bool DoSectionMidi1ApiEndpoints(_In_ bool verbose) { UNREFERENCED_PARAMETER(verbose); - OutputSectionHeader(L"enum_midi1_api_input_endpoints"); + OutputSectionHeader(MIDIDMP_SECTION_LABEL_MIDI1_API_INPUT_ENDPOINTS); try { @@ -262,8 +287,8 @@ bool DoSectionMidi1ApiEndpoints(_In_ bool verbose) { auto device = midi1Inputs.GetAt(i); - OutputStringField(L"endpoint_device_id", device.Id()); - OutputStringField(L"name", device.Name()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI1_ENDPOINT_ID, device.Id()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI1_ENDPOINT_NAME, device.Name()); if (i != midi1Inputs.Size() - 1) { @@ -278,7 +303,7 @@ bool DoSectionMidi1ApiEndpoints(_In_ bool verbose) return false; } - OutputSectionHeader(L"enum_midi1_api_output_endpoints"); + OutputSectionHeader(MIDIDMP_SECTION_LABEL_MIDI1_API_OUTPUT_ENDPOINTS); try {// outputs @@ -289,8 +314,8 @@ bool DoSectionMidi1ApiEndpoints(_In_ bool verbose) { auto device = midi1Outputs.GetAt(i); - OutputStringField(L"endpoint_device_id", device.Id()); - OutputStringField(L"name", device.Name()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI1_ENDPOINT_ID, device.Id()); + OutputStringField(MIDIDMP_FIELD_LABEL_MIDI1_ENDPOINT_NAME, device.Name()); if (i != midi1Outputs.Size() - 1) { @@ -312,25 +337,33 @@ bool DoSectionPingTest(_In_ bool verbose, _In_ uint8_t pingCount) { UNREFERENCED_PARAMETER(verbose); - OutputSectionHeader(L"ping_test"); + OutputSectionHeader(MIDIDMP_SECTION_LABEL_PING_TEST); - OutputTimestampField(L"ping_attempt_count", pingCount); + OutputNumericField(MIDIDMP_FIELD_LABEL_PING_ATTEMPT_COUNT, (uint32_t)pingCount); auto pingResult = midi2::MidiService::PingService(pingCount); - OutputNumericField(L"ping_return_count", pingResult.Responses().Size()); - - if (pingResult.Success()) + if (pingResult != nullptr) { - OutputTimestampField(L"ping_time_round_trip_total_ticks", pingResult.TotalPingRoundTripMidiClock()); - OutputTimestampField(L"ping_time_round_trip_average_ticks", pingResult.AveragePingRoundTripMidiClock()); + OutputNumericField(MIDIDMP_FIELD_LABEL_PING_RETURN_COUNT, pingResult.Responses().Size()); + + if (pingResult.Success()) + { + OutputTimestampField(MIDIDMP_FIELD_LABEL_PING_ROUND_TRIP_TOTAL_TICKS, pingResult.TotalPingRoundTripMidiClock()); + OutputTimestampField(MIDIDMP_FIELD_LABEL_PING_ROUND_TRIP_AVERAGE_TICKS, pingResult.AveragePingRoundTripMidiClock()); + } + else + { + OutputError(L"Ping test failed"); + OutputStringField(MIDIDMP_FIELD_LABEL_PING_FAILURE_REASON, pingResult.FailureReason()); + + return false; + } } else { - OutputError(L"Ping test failed"); - OutputStringField(L"ping_failure_reason", pingResult.FailureReason()); - + OutputError(L"Ping test failed. Return was null."); return false; } } @@ -346,14 +379,131 @@ bool DoSectionPingTest(_In_ bool verbose, _In_ uint8_t pingCount) bool DoSectionClock(_In_ bool verbose) { - OutputSectionHeader(L"midi_clock"); + UNREFERENCED_PARAMETER(verbose); + + OutputSectionHeader(MIDIDMP_SECTION_LABEL_MIDI_CLOCK); - OutputTimestampField(L"clock_frequency", midi2::MidiClock::TimestampFrequency()); - OutputTimestampField(L"clock_now", midi2::MidiClock::Now()); + OutputTimestampField(MIDIDMP_FIELD_LABEL_CLOCK_FREQUENCY, midi2::MidiClock::TimestampFrequency()); + OutputTimestampField(MIDIDMP_FIELD_LABEL_CLOCK_NOW, midi2::MidiClock::Now()); return true; } +bool DoSectionServiceStatus(_In_ bool verbose) +{ + UNREFERENCED_PARAMETER(verbose); + + OutputSectionHeader(MIDIDMP_SECTION_LABEL_SERVICE_STATUS); + + OutputBooleanField(MIDIDMP_FIELD_LABEL_SERVICE_AVAILABLE, midi2::MidiService::IsAvailable()); + + return true; +} + + +std::wstring GetOSVersion() +{ + try + { + OSVERSIONINFOW versionInfo{}; + + NTSTATUS (WINAPI *rtlGetVersion)(PRTL_OSVERSIONINFOW) = nullptr; + + HINSTANCE ntdll = LoadLibrary(L"ntdll.dll"); + + if (ntdll != nullptr) + { + rtlGetVersion = (NTSTATUS(WINAPI*)(PRTL_OSVERSIONINFOW)) GetProcAddress(ntdll, "RtlGetVersion"); + + if (rtlGetVersion != nullptr) + { + rtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo); + } + + // do this before anything else so we ensure if frees + FreeLibrary(ntdll); + + if (rtlGetVersion != nullptr) + { + return std::wstring( + std::to_wstring(versionInfo.dwMajorVersion) + + L"." + + std::to_wstring(versionInfo.dwMinorVersion) + + L"." + + std::to_wstring(versionInfo.dwBuildNumber) + + L" " + + versionInfo.szCSDVersion + ); + } + + } + + return L"unknown"; + + } + catch (...) + { + return L"exception"; + } +} + + +std::wstring GetProcessorArchitectureString(WORD arch) +{ + switch (arch) + { + case PROCESSOR_ARCHITECTURE_AMD64: + return L"64-bit Intel/AMD"; + case PROCESSOR_ARCHITECTURE_ARM: + return L"32-bit Arm"; + case PROCESSOR_ARCHITECTURE_ARM64: + return L"Arm64"; + case PROCESSOR_ARCHITECTURE_IA64: + return L"Itanium"; + case PROCESSOR_ARCHITECTURE_INTEL: + return L"32-bit Intel x86"; + default: + return L"Unknown"; + } + +} + +void OutputSystemInfo(_In_ SYSTEM_INFO& sysinfo) +{ + // that sysinfo.dwNumberOfProcessors can return some strange results. + +// OutputNumericField(L"num_processors", sysinfo.dwNumberOfProcessors); + std::wstring processorArchitecture = GetProcessorArchitectureString(sysinfo.wProcessorArchitecture); + + OutputStringField(MIDIDMP_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_ARCH, processorArchitecture); + OutputNumericField(MIDIDMP_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_LEVEL, sysinfo.wProcessorLevel); + OutputHexNumericField(MIDIDMP_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_REVISION, sysinfo.wProcessorRevision); +} + +bool DoSectionSystemInfo(_In_ bool verbose) +{ + OutputSectionHeader(MIDIDMP_SECTION_LABEL_OS); + OutputStringField(MIDIDMP_FIELD_LABEL_OS_VERSION, GetOSVersion()); + + + // if running under emulation on Arm64, this is going to return the emulated sys info + OutputSectionHeader(MIDIDMP_SECTION_LABEL_APPARENT_SYSTEM_INFO); + + SYSTEM_INFO sysinfo; + ::GetNativeSystemInfo(&sysinfo); + OutputSystemInfo(sysinfo); + + // if running under emulation on Arm64, this is going to return the Arm info + OutputSectionHeader(MIDIDMP_SECTION_LABEL_NATIVE_SYSTEM_INFO); + + SYSTEM_INFO sysinfoNative; + ::GetNativeSystemInfo(&sysinfoNative); + OutputSystemInfo(sysinfoNative); + + return true; +} + + int __cdecl main() { winrt::init_apartment(); @@ -362,14 +512,18 @@ int __cdecl main() bool pingTest = true; bool midiClock = true; - OutputHeader(L"Microsoft Windows MIDI Services"); + OutputHeader(MIDIDMP_PRODUCT_NAME); - OutputSectionHeader(L"header"); + OutputSectionHeader(MIDIDMP_SECTION_LABEL_HEADER); OutputCurrentTime(); try { + DoSectionSystemInfo(verbose); + + DoSectionServiceStatus(verbose); + auto transportsWorked = DoSectionTransports(verbose); if (transportsWorked) @@ -403,7 +557,7 @@ int __cdecl main() RETURN_FAIL; } - OutputSectionHeader(L"end_of_file"); + OutputSectionHeader(MIDIDMP_SECTION_LABEL_END_OF_FILE); RETURN_SUCCESS; } diff --git a/src/api/InBoxApps/mididmp/mididmp.vcxproj b/src/api/InBoxApps/mididmp/mididmp.vcxproj index 73659d27c..89bfb305f 100644 --- a/src/api/InBoxApps/mididmp/mididmp.vcxproj +++ b/src/api/InBoxApps/mididmp/mididmp.vcxproj @@ -11,7 +11,7 @@ Win32Proj mididmp 10.0 - 10.0.17134.0 + 10.0.20348.0 @@ -19,18 +19,10 @@ Debug ARM64 - - Debug - Win32 - Release ARM64 - - Release - Win32 - Debug x64 @@ -69,29 +61,29 @@ - - $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\ - - - $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\ - $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\ - $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\ + $(IncludePath) + $(LibraryPath) $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\ + $(IncludePath) + $(LibraryPath) $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\ + $(IncludePath) + $(LibraryPath) $(SolutionDir)VSFiles\intermediate\$(ProjectName)\$(Platform)\$(Configuration)\ $(SolutionDir)VSFiles\$(Platform)\$(Configuration)\ + $(IncludePath) + $(LibraryPath) @@ -113,6 +105,8 @@ Console false + %(AdditionalDependencies) + %(AdditionalDependencies) @@ -120,6 +114,9 @@ WIN32;%(PreprocessorDefinitions) stdcpp20 + + ntdll.lib;%(AdditionalDependencies) + @@ -127,10 +124,8 @@ true true NDEBUG;%(PreprocessorDefinitions) - stdcpp20 stdcpp20 stdcpp20 - %(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir); %(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir); %(AdditionalIncludeDirectories);$(SolutionDir)inc;$(SolutionDir)VSFiles\intermediate\idl\$(Platform)\$(Configuration);$(IntDir); @@ -139,9 +134,12 @@ true true false + %(AdditionalDependencies) + %(AdditionalDependencies) + diff --git a/src/api/InBoxApps/mididmp/mididmp.vcxproj.filters b/src/api/InBoxApps/mididmp/mididmp.vcxproj.filters index 388ab2e23..f90a3bf1a 100644 --- a/src/api/InBoxApps/mididmp/mididmp.vcxproj.filters +++ b/src/api/InBoxApps/mididmp/mididmp.vcxproj.filters @@ -18,6 +18,9 @@ Header Files + + Header Files + diff --git a/src/api/InBoxApps/mididmp/mididmp_field_defs.h b/src/api/InBoxApps/mididmp/mididmp_field_defs.h new file mode 100644 index 000000000..93afb4b52 --- /dev/null +++ b/src/api/InBoxApps/mididmp/mididmp_field_defs.h @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +#pragma once + +// All field names here in case you want to parse the file. Just include or +// copy these definitions into your code + +#define MIDIDMP_PRODUCT_NAME L"Microsoft Windows MIDI Services" + +#define MIDIDMP_MAX_FIELD_LABEL_WIDTH 25 + +#define MIDIDMP_FIELD_SEPARATOR L" : " +#define MIDIDMP_SECTION_HEADER_SEPARATOR_CHAR '=' +#define MIDIDMP_ITEM_SEPARATOR_CHAR '-' +#define MIDIDMP_SEPARATOR_REPEATING_CHAR_COUNT_PER_LINE 79 + +#define MIDIDMP_FIELD_LABEL_ERROR L"ERROR" + +#define MIDIDMP_SECTION_LABEL_ENUM_TRANSPORTS L"enum_transports" +#define MIDIDMP_FIELD_LABEL_TRANSPORT_ID L"transport_id" +#define MIDIDMP_FIELD_LABEL_TRANSPORT_NAME L"name" +#define MIDIDMP_FIELD_LABEL_TRANSPORT_MNEMONIC L"mnemonic" +#define MIDIDMP_FIELD_LABEL_TRANSPORT_VERSION L"version" +#define MIDIDMP_FIELD_LABEL_TRANSPORT_AUTHOR L"author" +#define MIDIDMP_FIELD_LABEL_TRANSPORT_DESCRIPTION L"description" + +#define MIDIDMP_SECTION_LABEL_MIDI2_API_ENDPOINTS L"enum_ump_api_endpoints" +#define MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_ID L"endpoint_device_id" +#define MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_NAME L"name" +#define MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_MNEMONIC L"transport_mnemonic" +#define MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_USER_SUPPLIED_NAME L"name_user_supplied" +#define MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_ENDPOINT_SUPPLIED_NAME L"name_endpoint_supplied" +#define MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_SUPPLIED_NAME L"name_transport_supplied" +#define MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_TRANSPORT_SUPPLIED_DESC L"desc_transport_supplied" +#define MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_USER_SUPPLIED_DESC L"desc_user_supplied" +#define MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_PARENT_ID L"parent_id" +#define MIDIDMP_FIELD_LABEL_MIDI2_ENDPOINT_PARENT_NAME L"parent_name" + +#define MIDIDMP_SECTION_LABEL_MIDI1_API_INPUT_ENDPOINTS L"enum_midi1_api_inputs_endpoints" +#define MIDIDMP_SECTION_LABEL_MIDI1_API_OUTPUT_ENDPOINTS L"enum_midi1_api_output_endpoints" +#define MIDIDMP_FIELD_LABEL_MIDI1_ENDPOINT_ID L"endpoint_device_id" +#define MIDIDMP_FIELD_LABEL_MIDI1_ENDPOINT_NAME L"name" + +#define MIDIDMP_SECTION_LABEL_PING_TEST L"ping_test" +#define MIDIDMP_FIELD_LABEL_PING_ATTEMPT_COUNT L"ping_attempt_count" +#define MIDIDMP_FIELD_LABEL_PING_RETURN_COUNT L"ping_return_count" +#define MIDIDMP_FIELD_LABEL_PING_ROUND_TRIP_TOTAL_TICKS L"round_trip_total_ticks" +#define MIDIDMP_FIELD_LABEL_PING_ROUND_TRIP_AVERAGE_TICKS L"round_trip_average_ticks" +#define MIDIDMP_FIELD_LABEL_PING_FAILURE_REASON L"ping_failure_reason" + +#define MIDIDMP_SECTION_LABEL_MIDI_CLOCK L"midi_clock" +#define MIDIDMP_FIELD_LABEL_CLOCK_FREQUENCY L"clock_frequency" +#define MIDIDMP_FIELD_LABEL_CLOCK_NOW L"clock_now" + +#define MIDIDMP_SECTION_LABEL_SERVICE_STATUS L"service_status" +#define MIDIDMP_FIELD_LABEL_SERVICE_AVAILABLE L"available" + +#define MIDIDMP_SECTION_LABEL_OS L"os" +#define MIDIDMP_FIELD_LABEL_OS_VERSION L"os_version" + +#define MIDIDMP_SECTION_LABEL_APPARENT_SYSTEM_INFO L"apparent_system_info" +#define MIDIDMP_SECTION_LABEL_NATIVE_SYSTEM_INFO L"native_system_info" +#define MIDIDMP_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_ARCH L"processor_architecture" +#define MIDIDMP_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_LEVEL L"processor_level" +#define MIDIDMP_FIELD_LABEL_SYSTEM_INFO_PROCESSOR_REVISION L"processor_revision" + +#define MIDIDMP_SECTION_LABEL_HEADER L"header" +#define MIDIDMP_FIELD_LABEL_CURRENT_TIME L"current_time" + +#define MIDIDMP_SECTION_LABEL_END_OF_FILE L"end_of_file" + diff --git a/src/api/InBoxApps/mididmp/pch.h b/src/api/InBoxApps/mididmp/pch.h index 9231a816c..ab34cbca1 100644 --- a/src/api/InBoxApps/mididmp/pch.h +++ b/src/api/InBoxApps/mididmp/pch.h @@ -9,8 +9,15 @@ #pragma once +#include +#include +#include + #include +#include +#include + #include #include @@ -19,6 +26,7 @@ #include + namespace midi2 = winrt::Microsoft::Devices::Midi2; namespace foundation = winrt::Windows::Foundation; namespace collections = winrt::Windows::Foundation::Collections; @@ -26,4 +34,6 @@ namespace collections = winrt::Windows::Foundation::Collections; #include "combaseapi.h" #include "wstring_util.h" -namespace internal = ::WindowsMidiServicesInternal; \ No newline at end of file +namespace internal = ::WindowsMidiServicesInternal; + +#include "mididmp_field_defs.h" \ No newline at end of file diff --git a/src/api/Service/Exe/MidiSessionTracker.cpp b/src/api/Service/Exe/MidiSessionTracker.cpp index de99dae30..410021792 100644 --- a/src/api/Service/Exe/MidiSessionTracker.cpp +++ b/src/api/Service/Exe/MidiSessionTracker.cpp @@ -15,6 +15,13 @@ CMidiSessionTracker::Initialize() return S_OK; } + +HRESULT +CMidiSessionTracker::VerifyConnectivity() +{ + return S_OK; +} + _Use_decl_annotations_ HRESULT CMidiSessionTracker::AddClientSession( diff --git a/src/api/Service/Exe/MidiSessionTracker.h b/src/api/Service/Exe/MidiSessionTracker.h index a299abfcb..4cbcdf136 100644 --- a/src/api/Service/Exe/MidiSessionTracker.h +++ b/src/api/Service/Exe/MidiSessionTracker.h @@ -41,7 +41,6 @@ class CMidiSessionTracker : public Microsoft::WRL::RuntimeClass< IMidiSessionTracker> { public: - CMidiSessionTracker() {} ~CMidiSessionTracker() {} @@ -58,6 +57,8 @@ class CMidiSessionTracker : public Microsoft::WRL::RuntimeClass< // This is called from the API STDMETHOD(GetSessionListJson)(_Out_ BSTR* SessionList); + STDMETHOD(VerifyConnectivity)(); + STDMETHOD(Cleanup)(); private: diff --git a/src/api/Service/Exe/MidiSrvRPC.idl b/src/api/Service/Exe/MidiSrvRPC.idl index 9842d6b4c..6656b1a40 100644 --- a/src/api/Service/Exe/MidiSrvRPC.idl +++ b/src/api/Service/Exe/MidiSrvRPC.idl @@ -133,4 +133,7 @@ interface MidiSrvRPC [in] handle_t BindingHandle, [out] BSTR* TransformListJson); + HRESULT MidiSrvVerifyConnectivity( + [in] handle_t BindingHandle); + } diff --git a/src/api/Service/Exe/MidiSrvRpc.cpp b/src/api/Service/Exe/MidiSrvRpc.cpp index c3562e523..8aeaf9d52 100644 --- a/src/api/Service/Exe/MidiSrvRpc.cpp +++ b/src/api/Service/Exe/MidiSrvRpc.cpp @@ -64,6 +64,26 @@ HRESULT MidiSrvTestRpc( return S_OK; } + +HRESULT MidiSrvVerifyConnectivity( + handle_t /*BindingHandle*/) +{ + TraceLoggingWrite( + MidiSrvTelemetryProvider::Provider(), + __FUNCTION__, + TraceLoggingLevel(WINEVENT_LEVEL_INFO) + ); + + std::shared_ptr sessionTracker; + + auto coInit = wil::CoInitializeEx(COINIT_MULTITHREADED); + + // Client manager creates the client, fills in the MIDISRV_CLIENT information + RETURN_IF_FAILED(g_MidiService->GetSessionTracker(sessionTracker)); + + return sessionTracker->VerifyConnectivity(); +} + HRESULT MidiSrvCreateClient( /* [in] */ handle_t BindingHandle, /* [string][in] */ __RPC__in_string LPCWSTR MidiDevice, diff --git a/src/api/idl/MidiAbstraction.idl b/src/api/idl/MidiAbstraction.idl index 0e9d832cb..80ddf53f0 100644 --- a/src/api/idl/MidiAbstraction.idl +++ b/src/api/idl/MidiAbstraction.idl @@ -19,12 +19,14 @@ import "MidiDeviceManagerInterface.idl"; typedef struct { MidiDataFormat DataFormat; + LPCWSTR InstanceConfigurationJsonData; } ABSTRACTIONCREATIONPARAMS, *PABSTRACTIONCREATIONPARAMS; typedef struct { MidiDataFormat DataFormatIn; MidiDataFormat DataFormatOut; + LPCWSTR InstanceConfigurationJsonData; } TRANSFORMCREATIONPARAMS, *PTRANSFORMCREATIONPARAMS; @@ -302,6 +304,10 @@ interface IMidiSessionTracker : IUnknown [out] BSTR* SessionList ); + // TODO: May want to move this to an interface that deals with client and service disconnects + // and general connection health/tracking + HRESULT VerifyConnectivity(); + HRESULT Cleanup(); }; diff --git a/src/oob-setup/api-package/WindowsMidiServices.wxs b/src/oob-setup/api-package/WindowsMidiServices.wxs index fc1b30796..fea4e6c93 100644 --- a/src/oob-setup/api-package/WindowsMidiServices.wxs +++ b/src/oob-setup/api-package/WindowsMidiServices.wxs @@ -161,10 +161,11 @@ Bitness="always64" Directory="API_INSTALLFOLDER" Guid="dc48ab14-dd46-4ff2-8d59-10b9a8215c7e"> - - - - + + + + + + + - - - + + + + - + - - + + + - - + + - - - - - + + + + + + + + + + + + + diff --git a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointMonitorCommand.cs b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointMonitorCommand.cs index 60d5fc51b..21ff7a8fb 100644 --- a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointMonitorCommand.cs +++ b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointMonitorCommand.cs @@ -81,6 +81,11 @@ public sealed class Settings : MessageListenerCommandSettings [DefaultValue(false)] public bool WarnIfSkippedIncrementMessage { get; set; } + [LocalizedDescription("ParameterMonitorEndpointAutoReconnect")] + [CommandOption("-r|--auto-reconnect")] + [DefaultValue(true)] + public bool AutoReconnect { get; set; } + //[EnumLocalizedDescription("ParameterMonitorEndpointSkipToKeepUp", typeof(CaptureFieldDelimiter))] //[CommandOption("-k|--keep-up-display")] //[DefaultValue(false)] @@ -189,7 +194,8 @@ public override int Execute(CommandContext context, Settings settings) return (int)MidiConsoleReturnCode.ErrorCreatingSession; } - var connection = session.CreateEndpointConnection(endpointId); + var connection = session.CreateEndpointConnection(endpointId, settings.AutoReconnect); + if (connection == null) { AnsiConsole.WriteLine(Strings.ErrorUnableToCreateEndpointConnection); @@ -227,8 +233,10 @@ public override int Execute(CommandContext context, Settings settings) UInt64 lastMessageReceivedEventTimestamp = 0; UInt32 countMessagesReceived = 0; - - MonitorEndpointConnectionStatusInTheBackground(endpointId); + if (!settings.AutoReconnect) + { + MonitorEndpointConnectionStatusInTheBackground(endpointId); + } bool continueWaiting = true; diff --git a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessageCommand.cs b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessageCommand.cs index 22aab4bca..d338baf32 100644 --- a/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessageCommand.cs +++ b/src/user-tools/midi-console/Midi/Commands/Endpoint/EndpointSendMessageCommand.cs @@ -311,7 +311,7 @@ public override int Execute(CommandContext context, Settings settings) if (maxTimestampScheduled > MidiClock.Now) { - int sleepMs = (int)Math.Ceiling(MidiClock.ConvertTimestampToMilliseconds(maxTimestampScheduled - MidiClock.Now)); + int sleepMs = (int)Math.Ceiling(MidiClock.ConvertTimestampTicksToMilliseconds(maxTimestampScheduled - MidiClock.Now)); sleepMs += 1000; // we wait an extra second to avoid any timing issues diff --git a/src/user-tools/midi-console/Midi/Commands/Service/ServiceStatusCommand.cs b/src/user-tools/midi-console/Midi/Commands/Service/ServiceStatusCommand.cs index 6d3c5a752..5a1a625f0 100644 --- a/src/user-tools/midi-console/Midi/Commands/Service/ServiceStatusCommand.cs +++ b/src/user-tools/midi-console/Midi/Commands/Service/ServiceStatusCommand.cs @@ -32,6 +32,15 @@ public override int Execute(CommandContext context, Settings settings) // check to see if the service is running. // NOTE: Equivalent code can't be moved to the SDK due to Desktop/WinRT limitations. + if (MidiService.IsAvailable()) + { + AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatSuccess("Service reported as available by API.")); + } + else + { + AnsiConsole.MarkupLine(AnsiMarkupFormatter.FormatError("Service reported as NOT available by API.")); + } + string serviceName = "MidiSrv"; ServiceController controller; diff --git a/src/user-tools/midi-console/Midi/Midi.csproj b/src/user-tools/midi-console/Midi/Midi.csproj index e406975ec..bdadcac84 100644 --- a/src/user-tools/midi-console/Midi/Midi.csproj +++ b/src/user-tools/midi-console/Midi/Midi.csproj @@ -13,7 +13,7 @@ true true - True + False ARM64;x64 @@ -23,15 +23,15 @@ true - True + False - + - - + + diff --git a/src/user-tools/midi-console/Midi/Program.cs b/src/user-tools/midi-console/Midi/Program.cs index 38c3f0908..6f23cd788 100644 --- a/src/user-tools/midi-console/Midi/Program.cs +++ b/src/user-tools/midi-console/Midi/Program.cs @@ -71,6 +71,7 @@ config.AddBranch("endpoint", endpoint => { endpoint.AddExample("endpoint", "properties", "--verbose"); + //endpoint.AddExample("endpoint", "properties"); endpoint.AddExample("endpoint", "send-message", "0x21234567"); endpoint.SetDescription(Strings.CommandEndpointDescription); diff --git a/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs b/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs index 164c95e87..84785b595 100644 --- a/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs +++ b/src/user-tools/midi-console/Midi/Resources/Strings.Designer.cs @@ -807,6 +807,15 @@ internal static string ParameterListenerMessagesFilter { } } + /// + /// Looks up a localized string similar to Continue monitoring and automatically reconnect after device disconnection, if device becomes available (unplug/replug).. + /// + internal static string ParameterMonitorEndpointAutoReconnect { + get { + return ResourceManager.GetString("ParameterMonitorEndpointAutoReconnect", resourceCulture); + } + } + /// /// Looks up a localized string similar to The direction of the endpoint referenced by the Id. If this doesn't match the endpoint type, the connection will fail.. /// diff --git a/src/user-tools/midi-console/Midi/Resources/Strings.resx b/src/user-tools/midi-console/Midi/Resources/Strings.resx index 6a27366f9..bbb4d1544 100644 --- a/src/user-tools/midi-console/Midi/Resources/Strings.resx +++ b/src/user-tools/midi-console/Midi/Resources/Strings.resx @@ -366,6 +366,9 @@ (not yet implemented) + + Continue monitoring and automatically reconnect after device disconnection, if device becomes available (unplug/replug). + The direction of the endpoint referenced by the Id. If this doesn't match the endpoint type, the connection will fail. From 37a639f10d7fc5f0cb44d10fc9b061cb58f2bed5 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Fri, 26 Apr 2024 01:05:01 -0400 Subject: [PATCH 2/2] Fix endpoint connection bugs introduced with auto-reconnect --- build/replace_running_service_x64.bat | 4 +- build/staging/version/BundleInfo.wxi | 2 +- .../nuget/Microsoft.Devices.Midi2.nuspec | 4 +- .../Midi2Client/MidiEndpointConnection.cpp | 112 +++++++++++++----- .../MidiEndpointConnection_AutoReconnect.cpp | 74 +++++++----- .../MidiEndpointConnection_Plugins.cpp | 16 ++- ...iEndpointConnection_SendInfrastructure.cpp | 12 +- ...ndpointConnection_SendMultipleMessages.cpp | 29 +---- ...diEndpointConnection_SendSingleMessage.cpp | 62 ++++++++-- src/api/Client/Midi2Client/MidiService.cpp | 99 ++++++++++++++-- src/api/Client/Midi2Client/MidiSession.cpp | 59 ++++++--- .../Midi2Client/Windows.Devices.Midi2.vcxproj | 10 +- src/api/Client/Midi2Client/trace_logging.cpp | 38 ++++-- src/api/Client/Midi2Client/trace_logging.h | 6 + src/api/InBoxApps/mididmp/main.cpp | 23 ++-- .../Exe/MidiEndpointProtocolManager.cpp | 2 +- src/user-tools/midi-console/Midi/Midi.csproj | 6 +- src/user-tools/midi-console/Midi/Program.cs | 4 +- 18 files changed, 399 insertions(+), 163 deletions(-) diff --git a/build/replace_running_service_x64.bat b/build/replace_running_service_x64.bat index 5d9bf144e..4471614a1 100644 --- a/build/replace_running_service_x64.bat +++ b/build/replace_running_service_x64.bat @@ -19,9 +19,9 @@ copy /Y %buildoutput%\MidiSrv.exe %servicepath% copy /Y %buildoutput%\Midi2.*Abstraction.dll %servicepath% copy /Y %buildoutput%\Midi2.*Transform.dll %servicepath% -echo Windows.Devices.Midi2.dll +echo API impl copy /Y %buildoutput%\*.Devices.Midi2.dll %apipath% -echo Windows.Devices.Midi2.pri +echo API pri copy /Y %buildoutput%\*.Devices.Midi2.pri %apipath% diff --git a/build/staging/version/BundleInfo.wxi b/build/staging/version/BundleInfo.wxi index c3a7cb7a4..9ef75eca0 100644 --- a/build/staging/version/BundleInfo.wxi +++ b/build/staging/version/BundleInfo.wxi @@ -1,4 +1,4 @@ - + diff --git a/src/api/Client/Midi2Client-Projection/nuget/Microsoft.Devices.Midi2.nuspec b/src/api/Client/Midi2Client-Projection/nuget/Microsoft.Devices.Midi2.nuspec index af4efd8c3..bfcec7b97 100644 --- a/src/api/Client/Midi2Client-Projection/nuget/Microsoft.Devices.Midi2.nuspec +++ b/src/api/Client/Midi2Client-Projection/nuget/Microsoft.Devices.Midi2.nuspec @@ -2,8 +2,8 @@ Microsoft.Devices.Midi2 - 1.0.0-preview.6-0197 - Microsoft Corporation + 1.0.0-preview.6-0198 + Microsoft Windows MIDI Services SDK. Minimum package necessary to use Windows MIDI Services from an app on a PC that has Windows MIDI Services installed. MIT https://github.com/microsoft/midi/ diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection.cpp b/src/api/Client/Midi2Client/MidiEndpointConnection.cpp index b1784e8ec..bde65134f 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection.cpp +++ b/src/api/Client/Midi2Client/MidiEndpointConnection.cpp @@ -18,12 +18,12 @@ namespace MIDI_CPP_NAMESPACE::implementation _Use_decl_annotations_ HRESULT MidiEndpointConnection::Callback(PVOID data, UINT size, LONGLONG timestamp, LONGLONG) { - internal::LogInfo(__FUNCTION__, L"Message Received"); + // internal::LogInfo(__FUNCTION__, L"Message Received in API callback"); try { // one copy of the event args for this gets sent to all listeners and the main event - auto args = winrt::make_self(data, size, timestamp); + auto args = winrt::make_self(data, size, timestamp); // we failed to create the event args if (args == nullptr) @@ -63,12 +63,23 @@ namespace MIDI_CPP_NAMESPACE::implementation } catch (winrt::hresult_error const& ex) { - internal::LogHresultError(__FUNCTION__, L"hresult exception calling message handlers", ex); + internal::LogHresultError(__FUNCTION__, L"hresult exception handling received messages", ex); return E_FAIL; } - } + catch (std::exception const& ex) + { + internal::LogStandardExceptionError(__FUNCTION__, L"hresult exception handling received messages", ex); + + return E_FAIL; + } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception handling received message"); + return E_FAIL; + } + } _Use_decl_annotations_ @@ -81,7 +92,14 @@ namespace MIDI_CPP_NAMESPACE::implementation bool autoReconnect ) { - internal::LogInfo(__FUNCTION__, L"Internal Initialize "); + internal::LogInfo(__FUNCTION__, L"Enter"); + + if (serviceAbstraction == nullptr) + { + internal::LogGeneralError(__FUNCTION__, L"Error initializing endpoint. Service abstraction is null"); + + return false; + } try { @@ -94,7 +112,6 @@ namespace MIDI_CPP_NAMESPACE::implementation m_connectionSettings = connectionSettings; m_autoReconnect = autoReconnect; - return true; } @@ -110,8 +127,14 @@ namespace MIDI_CPP_NAMESPACE::implementation _Use_decl_annotations_ bool MidiEndpointConnection::InternalOpen() { - internal::LogInfo(__FUNCTION__, L"Connection Open "); + internal::LogInfo(__FUNCTION__, L"Enter"); + if (m_endpointAbstraction == nullptr) + { + internal::LogGeneralError(__FUNCTION__, L"Failed to open connection. endpoint abstraction is null"); + + return false; + } DWORD mmcssTaskId{}; @@ -119,10 +142,14 @@ namespace MIDI_CPP_NAMESPACE::implementation if (m_connectionSettings != nullptr) { - connectionSettingsJsonString = m_connectionSettings.SettingsJson().c_str(); + internal::LogInfo(__FUNCTION__, L"Connection settings were specified. Including JSON in service call."); + + connectionSettingsJsonString = static_cast(m_connectionSettings.SettingsJson().c_str()); } - ABSTRACTIONCREATIONPARAMS abstractionCreationParams{ MidiDataFormat_UMP, connectionSettingsJsonString }; + ABSTRACTIONCREATIONPARAMS abstractionCreationParams{ }; + abstractionCreationParams.DataFormat = MidiDataFormat_UMP; + abstractionCreationParams.InstanceConfigurationJsonData = connectionSettingsJsonString; auto result = m_endpointAbstraction->Initialize( (LPCWSTR)(EndpointDeviceId().c_str()), @@ -135,6 +162,8 @@ namespace MIDI_CPP_NAMESPACE::implementation if (SUCCEEDED(result)) { + internal::LogInfo(__FUNCTION__, L"Endpoint abstraction successfully initialized"); + m_isOpen = true; m_closeHasBeenCalled = false; } @@ -143,6 +172,8 @@ namespace MIDI_CPP_NAMESPACE::implementation internal::LogHresultError(__FUNCTION__, L"Failed to open connection", result); } + internal::LogInfo(__FUNCTION__, L"Connection Opened"); + return true; } @@ -150,15 +181,9 @@ namespace MIDI_CPP_NAMESPACE::implementation _Use_decl_annotations_ bool MidiEndpointConnection::InternalReopenAfterDisconnect() { - internal::LogInfo(__FUNCTION__, L"Connection Reopen "); + internal::LogInfo(__FUNCTION__, L"Enter"); - // Activate the endpoint for this device. Will fail if the device is not a BiDi device - if (!ActivateMidiStream()) - { - internal::LogGeneralError(__FUNCTION__, L"Could not activate MIDI Stream"); - - return false; - } + if (!ActivateMidiStream()) return false; return InternalOpen(); } @@ -167,19 +192,12 @@ namespace MIDI_CPP_NAMESPACE::implementation _Use_decl_annotations_ bool MidiEndpointConnection::Open() { - internal::LogInfo(__FUNCTION__, L"Connection Open "); - - // Activate the endpoint for this device. Will fail if the device is not a BiDi device - if (!ActivateMidiStream()) - { - internal::LogGeneralError(__FUNCTION__, L"Could not activate MIDI Stream"); - - return false; - } - + internal::LogInfo(__FUNCTION__, L"Enter"); if (!IsOpen()) { + if (!ActivateMidiStream()) return false; + if (m_endpointAbstraction != nullptr) { try @@ -200,6 +218,8 @@ namespace MIDI_CPP_NAMESPACE::implementation } else { + internal::LogGeneralError(__FUNCTION__, L"InternalOpen() returned false."); + return false; } } @@ -229,6 +249,8 @@ namespace MIDI_CPP_NAMESPACE::implementation } else { + internal::LogInfo(__FUNCTION__, L"Connection already open"); + // already open. Just return true here. Not fatal in any way, so no error return true; } @@ -238,7 +260,7 @@ namespace MIDI_CPP_NAMESPACE::implementation void MidiEndpointConnection::InternalClose() { - internal::LogInfo(__FUNCTION__, L"Connection Close"); + internal::LogInfo(__FUNCTION__, L"Enter"); if (m_closeHasBeenCalled) return; @@ -257,6 +279,8 @@ namespace MIDI_CPP_NAMESPACE::implementation { internal::LogGeneralError(__FUNCTION__, L"Exception on close"); } + + internal::LogInfo(__FUNCTION__, L"Complete"); } MidiEndpointConnection::~MidiEndpointConnection() @@ -270,16 +294,26 @@ namespace MIDI_CPP_NAMESPACE::implementation _Use_decl_annotations_ bool MidiEndpointConnection::DeactivateMidiStream() { - internal::LogInfo(__FUNCTION__, L"Deactivating MIDI Stream"); + internal::LogInfo(__FUNCTION__, L"Enter"); if (m_endpointAbstraction != nullptr) { // todo: some error / hresult handling here - m_endpointAbstraction->Cleanup(); + auto hr = m_endpointAbstraction->Cleanup(); + + if (FAILED(hr)) + { + internal::LogHresultError(__FUNCTION__, L"Failed HRESULT cleaning up endpoint abstraction", hr); + + return false; + } + m_endpointAbstraction == nullptr; } + internal::LogInfo(__FUNCTION__, L"Stream deactivated"); + return true; } @@ -287,12 +321,28 @@ namespace MIDI_CPP_NAMESPACE::implementation _Use_decl_annotations_ bool MidiEndpointConnection::ActivateMidiStream() noexcept { - internal::LogInfo(__FUNCTION__, L"Activating MIDI Stream"); + internal::LogInfo(__FUNCTION__, L"Enter"); + + if (m_serviceAbstraction == nullptr) + { + internal::LogGeneralError(__FUNCTION__, L"Service abstraction is null."); + + return false; + } try { winrt::check_hresult(m_serviceAbstraction->Activate(__uuidof(IMidiBiDi), (void**) &m_endpointAbstraction)); + if (m_endpointAbstraction == nullptr) + { + internal::LogGeneralError(__FUNCTION__, L"Endpoint Abstraction failed to Activate and return null."); + + return false; + } + + internal::LogInfo(__FUNCTION__, L"Activated IMidiBiDi"); + return true; } catch (winrt::hresult_error const& ex) diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection_AutoReconnect.cpp b/src/api/Client/Midi2Client/MidiEndpointConnection_AutoReconnect.cpp index fceef36ae..689fa2f21 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection_AutoReconnect.cpp +++ b/src/api/Client/Midi2Client/MidiEndpointConnection_AutoReconnect.cpp @@ -9,57 +9,75 @@ #include "pch.h" #include "MidiEndpointConnection.h" - -// auto deviceAddedHandler = TypedEventHandler(this, &CMidi2KSMidiEndpointManager::OnDeviceAdded); -// auto deviceUpdatedHandler = TypedEventHandler(this, &CMidi2KSMidiEndpointManager::OnDeviceUpdated); -// auto deviceRemovedHandler = TypedEventHandler(this, &CMidi2KSMidiEndpointManager::OnDeviceRemoved); -// m_DeviceAdded = m_Watcher.Added(winrt::auto_revoke, deviceAddedHandler); -// m_DeviceUpdated = m_Watcher.Updated(winrt::auto_revoke, deviceUpdatedHandler); -// m_DeviceRemoved = m_Watcher.Removed(winrt::auto_revoke, deviceRemovedHandler); - - namespace MIDI_CPP_NAMESPACE::implementation { + // TODO: An optimization here would be to host the watcher at the session level + // and simply have it filter based on the Ids it sees in the update messages. Then + // include internal functions in this class that it would call _Use_decl_annotations_ bool MidiEndpointConnection::StartDeviceWatcher() { - // start the device watcher for the specific Id - winrt::hstring deviceSelector( - L"Id:=\"" + m_endpointDeviceId + L"\" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True"); + internal::LogInfo(__FUNCTION__, L"Enter"); - m_autoReconnectDeviceWatcher = enumeration::DeviceInformation::CreateWatcher(deviceSelector); - - if (m_autoReconnectDeviceWatcher != nullptr) + try { - if (m_autoReconnectDeviceWatcher.Status() == enumeration::DeviceWatcherStatus::Stopped) - { - auto deviceAddedHandler = foundation::TypedEventHandler(this, &MidiEndpointConnection::DeviceAddedHandler); - auto deviceUpdatedHandler = foundation::TypedEventHandler(this, &MidiEndpointConnection::DeviceUpdatedHandler); - auto deviceRemovedHandler = foundation::TypedEventHandler(this, &MidiEndpointConnection::DeviceRemovedHandler); + // start the device watcher for the specific Id + winrt::hstring deviceSelector( + L"System.Devices.DeviceInstanceId:=\"" + m_endpointDeviceId + L"\" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True"); + + internal::LogInfo(__FUNCTION__, deviceSelector.c_str()); - m_autoReconnectDeviceWatcherAddedRevoker = m_autoReconnectDeviceWatcher.Added(winrt::auto_revoke, deviceAddedHandler); - m_autoReconnectDeviceWatcherUpdatedRevoker = m_autoReconnectDeviceWatcher.Updated(winrt::auto_revoke, deviceUpdatedHandler); - m_autoReconnectDeviceWatcherRemovedRevoker = m_autoReconnectDeviceWatcher.Removed(winrt::auto_revoke, deviceRemovedHandler); - m_autoReconnectDeviceWatcher.Start(); + m_autoReconnectDeviceWatcher = enumeration::DeviceInformation::CreateWatcher(deviceSelector); - return true; + if (m_autoReconnectDeviceWatcher != nullptr) + { + if (m_autoReconnectDeviceWatcher.Status() == enumeration::DeviceWatcherStatus::Stopped) + { + auto deviceAddedHandler = foundation::TypedEventHandler(this, &MidiEndpointConnection::DeviceAddedHandler); + auto deviceUpdatedHandler = foundation::TypedEventHandler(this, &MidiEndpointConnection::DeviceUpdatedHandler); + auto deviceRemovedHandler = foundation::TypedEventHandler(this, &MidiEndpointConnection::DeviceRemovedHandler); + + m_autoReconnectDeviceWatcherAddedRevoker = m_autoReconnectDeviceWatcher.Added(winrt::auto_revoke, deviceAddedHandler); + m_autoReconnectDeviceWatcherUpdatedRevoker = m_autoReconnectDeviceWatcher.Updated(winrt::auto_revoke, deviceUpdatedHandler); + m_autoReconnectDeviceWatcherRemovedRevoker = m_autoReconnectDeviceWatcher.Removed(winrt::auto_revoke, deviceRemovedHandler); + + m_autoReconnectDeviceWatcher.Start(); + + return true; + } + else + { + // can only start the watcher when it's in a stopped state + return false; + } } else { - // can only start the watcher when it's in a stopped state return false; } + } - else + catch (winrt::hresult_error hr) { + internal::LogHresultError(__FUNCTION__, L"HRESULT Exception starting device watcher", hr); + return false; } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception starting device watcher"); + + return false; + } + } _Use_decl_annotations_ bool MidiEndpointConnection::StopDeviceWatcher() { + internal::LogInfo(__FUNCTION__, L"Enter"); + if (m_autoReconnectDeviceWatcher != nullptr) { m_autoReconnectDeviceWatcher.Stop(); @@ -92,6 +110,8 @@ namespace MIDI_CPP_NAMESPACE::implementation // if not m_isOpen, then perform all the opening logic again if (!m_isOpen) { + internal::LogInfo(__FUNCTION__, L"Reopening after disconnect (m_isOpen == false)"); + InternalReopenAfterDisconnect(); } diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection_Plugins.cpp b/src/api/Client/Midi2Client/MidiEndpointConnection_Plugins.cpp index c0c56cb69..494e2936f 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection_Plugins.cpp +++ b/src/api/Client/Midi2Client/MidiEndpointConnection_Plugins.cpp @@ -28,9 +28,10 @@ namespace MIDI_CPP_NAMESPACE::implementation catch (...) { internal::LogGeneralError(__FUNCTION__, L"Exception initializing plugins."); - } } + + internal::LogInfo(__FUNCTION__, L"Initializing complete"); } @@ -49,6 +50,8 @@ namespace MIDI_CPP_NAMESPACE::implementation internal::LogGeneralError(__FUNCTION__, L"Exception calling Open on plugins."); } } + + internal::LogInfo(__FUNCTION__, L"Notifying message processing plugins complete"); } void MidiEndpointConnection::CleanupPlugins() noexcept @@ -66,6 +69,8 @@ namespace MIDI_CPP_NAMESPACE::implementation internal::LogGeneralError(__FUNCTION__, L"Exception cleaning up plugins."); } } + + internal::LogInfo(__FUNCTION__, L"Cleaning up plugins - complete"); } @@ -74,6 +79,8 @@ namespace MIDI_CPP_NAMESPACE::implementation _Use_decl_annotations_ void MidiEndpointConnection::AddMessageProcessingPlugin(midi2::IMidiEndpointMessageProcessingPlugin const& plugin) { + internal::LogInfo(__FUNCTION__, L"Enter"); + m_messageProcessingPlugins.Append(plugin); try @@ -92,11 +99,16 @@ namespace MIDI_CPP_NAMESPACE::implementation { internal::LogGeneralError(__FUNCTION__, L"Exception initializing or calling OnEndpointConnectionOpened on newly-added plugin"); } + + internal::LogInfo(__FUNCTION__, L"Complete"); + } _Use_decl_annotations_ void MidiEndpointConnection::RemoveMessageProcessingPlugin(winrt::guid id) { + internal::LogInfo(__FUNCTION__, L"Enter"); + for (uint32_t i = 0; i < m_messageProcessingPlugins.Size(); i++) { if (m_messageProcessingPlugins.GetAt(i).Id() == id) @@ -106,6 +118,8 @@ namespace MIDI_CPP_NAMESPACE::implementation } } + internal::LogInfo(__FUNCTION__, L"Complete"); + } diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection_SendInfrastructure.cpp b/src/api/Client/Midi2Client/MidiEndpointConnection_SendInfrastructure.cpp index 8931ef3f0..bbcc454e8 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection_SendInfrastructure.cpp +++ b/src/api/Client/Midi2Client/MidiEndpointConnection_SendInfrastructure.cpp @@ -68,7 +68,7 @@ namespace MIDI_CPP_NAMESPACE::implementation _Use_decl_annotations_ - bool MidiEndpointConnection::ValidateUmp(uint32_t word0, uint8_t wordCount) noexcept + bool MidiEndpointConnection::ValidateUmp(uint32_t word0, uint8_t wordCount) noexcept { if (!internal::IsValidSingleUmpWordCount(wordCount)) return false; @@ -89,7 +89,7 @@ namespace MIDI_CPP_NAMESPACE::implementation uint8_t sizeInBytes, internal::MidiTimestamp timestamp) { - internal::LogInfo(__FUNCTION__, L"Sending message raw"); + //internal::LogInfo(__FUNCTION__, L"Sending message raw"); try { @@ -108,10 +108,16 @@ namespace MIDI_CPP_NAMESPACE::implementation return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::TimestampOutOfRange; } + if (endpoint != nullptr) { auto hr = endpoint->SendMidiMessage(data, sizeInBytes, timestamp); + if (FAILED(hr)) + { + internal::LogHresultError(__FUNCTION__, L"SendMidiMessage returned a failure code", hr); + } + return SendMessageResultFromHRESULT(hr); } else @@ -136,7 +142,7 @@ namespace MIDI_CPP_NAMESPACE::implementation winrt::com_ptr endpoint, midi2::IMidiUniversalPacket const& ump) { - internal::LogInfo(__FUNCTION__, L"Sending message internal"); + //internal::LogInfo(__FUNCTION__, L"Sending message internal"); try { uint8_t umpDataSize{}; diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection_SendMultipleMessages.cpp b/src/api/Client/Midi2Client/MidiEndpointConnection_SendMultipleMessages.cpp index 12a2327c3..23e2cf09f 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection_SendMultipleMessages.cpp +++ b/src/api/Client/Midi2Client/MidiEndpointConnection_SendMultipleMessages.cpp @@ -14,7 +14,6 @@ namespace MIDI_CPP_NAMESPACE::implementation { - _Use_decl_annotations_ midi2::MidiSendMessageResults MidiEndpointConnection::SendMultipleMessagesBuffer( internal::MidiTimestamp const timestamp, @@ -107,7 +106,6 @@ namespace MIDI_CPP_NAMESPACE::implementation } return midi2::MidiSendMessageResults::Succeeded; - } @@ -290,6 +288,8 @@ namespace MIDI_CPP_NAMESPACE::implementation if (!MidiEndpointConnection::SendMessageSucceeded(result)) { + internal::LogGeneralError(__FUNCTION__, L"Failed to send message"); + // if any fail, we return immediately. return result; @@ -300,29 +300,4 @@ namespace MIDI_CPP_NAMESPACE::implementation } - //_Use_decl_annotations_ - //midi2::MidiSendMessageResults MidiEndpointConnection::SendMultipleMessagesPacketArray( - // winrt::array_view messages) noexcept - //{ - // internal::LogInfo(__FUNCTION__, L"Enter"); - - // for (auto const& ump : messages) - // { - // auto result = SendUmpInternal(m_endpointAbstraction, ump); - - // if (!MidiEndpointConnection::SendMessageSucceeded(result)) - // { - // // if any fail, we return immediately. - - // return result; - // } - // } - - // return midi2::MidiSendMessageResults::Succeeded; - - //} - - - - } diff --git a/src/api/Client/Midi2Client/MidiEndpointConnection_SendSingleMessage.cpp b/src/api/Client/Midi2Client/MidiEndpointConnection_SendSingleMessage.cpp index e2d5613e0..926a08dcf 100644 --- a/src/api/Client/Midi2Client/MidiEndpointConnection_SendSingleMessage.cpp +++ b/src/api/Client/Midi2Client/MidiEndpointConnection_SendSingleMessage.cpp @@ -22,7 +22,7 @@ namespace MIDI_CPP_NAMESPACE::implementation midi2::MidiMessageStruct const& message ) noexcept { - internal::LogInfo(__FUNCTION__, L"Sending message struct"); + //internal::LogInfo(__FUNCTION__, L"Sending message struct"); if (!ValidateUmp(message.Word0, wordCount)) { @@ -45,7 +45,7 @@ namespace MIDI_CPP_NAMESPACE::implementation winrt::Windows::Foundation::IMemoryBuffer const& buffer ) noexcept { - internal::LogInfo(__FUNCTION__, L"Sending message buffer"); + //internal::LogInfo(__FUNCTION__, L"Sending message buffer"); try { @@ -95,17 +95,23 @@ namespace MIDI_CPP_NAMESPACE::implementation // TODO: handle buffer full and other expected hresults return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception sending message"); + + return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; + } } _Use_decl_annotations_ - midi2::MidiSendMessageResults MidiEndpointConnection::SendSingleMessageWordArray( + midi2::MidiSendMessageResults MidiEndpointConnection::SendSingleMessageWordArray( internal::MidiTimestamp const timestamp, uint32_t const startIndex, uint8_t const wordCount, winrt::array_view words ) noexcept { - internal::LogInfo(__FUNCTION__, L"Sending message word array"); + //internal::LogInfo(__FUNCTION__, L"Sending message word array"); try { @@ -139,6 +145,12 @@ namespace MIDI_CPP_NAMESPACE::implementation // TODO: handle buffer full and other expected hresults return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception sending message"); + + return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; + } } _Use_decl_annotations_ @@ -146,7 +158,7 @@ namespace MIDI_CPP_NAMESPACE::implementation internal::MidiTimestamp const timestamp, uint32_t const word0) noexcept { - internal::LogInfo(__FUNCTION__, L"Sending message words (1)"); + //internal::LogInfo(__FUNCTION__, L"Sending message words (1)"); try { @@ -171,16 +183,22 @@ namespace MIDI_CPP_NAMESPACE::implementation // TODO: handle buffer full and other expected hresults return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception sending message"); + + return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; + } } _Use_decl_annotations_ - midi2::MidiSendMessageResults MidiEndpointConnection::SendSingleMessageWords( + midi2::MidiSendMessageResults MidiEndpointConnection::SendSingleMessageWords( internal::MidiTimestamp const timestamp, uint32_t const word0, uint32_t const word1) noexcept { - internal::LogInfo(__FUNCTION__, L"Sending message words (2)"); + //internal::LogInfo(__FUNCTION__, L"Sending message words (2)"); try { @@ -204,11 +222,17 @@ namespace MIDI_CPP_NAMESPACE::implementation } catch (winrt::hresult_error const& ex) { - internal::LogHresultError(__FUNCTION__, L" hresult exception sending message", ex); + internal::LogHresultError(__FUNCTION__, L"hresult exception sending message", ex); // TODO: handle buffer full and other expected hresults return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception sending message"); + + return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; + } } _Use_decl_annotations_ @@ -243,16 +267,22 @@ namespace MIDI_CPP_NAMESPACE::implementation } catch (winrt::hresult_error const& ex) { - internal::LogHresultError(__FUNCTION__, L" hresult exception sending message", ex); + internal::LogHresultError(__FUNCTION__, L"hresult exception sending message", ex); // TODO: handle buffer full and other expected hresults return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception sending message"); + + return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; + } } _Use_decl_annotations_ - midi2::MidiSendMessageResults MidiEndpointConnection::SendSingleMessageWords( + midi2::MidiSendMessageResults MidiEndpointConnection::SendSingleMessageWords( internal::MidiTimestamp const timestamp, uint32_t const word0, uint32_t const word1, @@ -289,6 +319,12 @@ namespace MIDI_CPP_NAMESPACE::implementation // TODO: handle buffer full and other expected hresults return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception sending message"); + + return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; + } } @@ -311,6 +347,12 @@ namespace MIDI_CPP_NAMESPACE::implementation // todo: handle buffer full and similar messages return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception sending message"); + + return midi2::MidiSendMessageResults::Failed | midi2::MidiSendMessageResults::Other; + } } diff --git a/src/api/Client/Midi2Client/MidiService.cpp b/src/api/Client/Midi2Client/MidiService.cpp index a5c86542d..3eef85b13 100644 --- a/src/api/Client/Midi2Client/MidiService.cpp +++ b/src/api/Client/Midi2Client/MidiService.cpp @@ -15,6 +15,8 @@ namespace MIDI_CPP_NAMESPACE::implementation // returns True if the MIDI Service is available on this PC bool MidiService::IsAvailable() { + internal::LogInfo(__FUNCTION__, L"Enter"); + // We may want other ways to check this in the future. Need to find the most robust approaches try @@ -25,25 +27,34 @@ namespace MIDI_CPP_NAMESPACE::implementation // winrt::try_create_instance indicates failure by returning an empty com ptr if (serviceAbstraction == nullptr) { + internal::LogGeneralError(__FUNCTION__, L"Error contacting service. Service abstraction is nullptr."); return false; } winrt::com_ptr tracker; auto sessionTrackerResult = serviceAbstraction->Activate(__uuidof(IMidiSessionTracker), (void**)&tracker); + if (FAILED(sessionTrackerResult)) + { + internal::LogHresultError(__FUNCTION__, L"Failure hresult received activating interface", sessionTrackerResult); + return false; + } - if (SUCCEEDED(sessionTrackerResult)) + auto verifyConnectivityResult = tracker->VerifyConnectivity(); + if (FAILED(verifyConnectivityResult)) { - if (SUCCEEDED(tracker->VerifyConnectivity())) - { - return true; - } + internal::LogHresultError(__FUNCTION__, L"Failure hresult received verifying connectivity", verifyConnectivityResult); + return false; } - return false; + internal::LogInfo(__FUNCTION__, L"Service connectivity verified"); + + return true; } catch (...) { + internal::LogGeneralError(__FUNCTION__, L"Error contacting service. It may be unavailable."); + // winrt::create_instance fails by throwing an exception return false; } @@ -73,6 +84,8 @@ namespace MIDI_CPP_NAMESPACE::implementation if (pingCount == 0) { + internal::LogGeneralError(__FUNCTION__, L"Ping count is zero"); + responseSummary->InternalSetFailed(L"Ping count is zero."); return *responseSummary; } @@ -82,6 +95,8 @@ namespace MIDI_CPP_NAMESPACE::implementation if (timeoutMilliseconds == 0) { + internal::LogGeneralError(__FUNCTION__, L"Timeout milliseconds is zero"); + responseSummary->InternalSetFailed(L"Timeout milliseconds is zero."); return *responseSummary; } @@ -92,6 +107,8 @@ namespace MIDI_CPP_NAMESPACE::implementation if (session == nullptr) { + internal::LogGeneralError(__FUNCTION__, L"Unable to create session"); + responseSummary->InternalSetFailed(L"Unable to create session."); return *responseSummary; } @@ -102,18 +119,19 @@ namespace MIDI_CPP_NAMESPACE::implementation if (endpoint == nullptr) { + internal::LogGeneralError(__FUNCTION__, L"Unable to create ping endpoint"); + responseSummary->InternalSetFailed(L"Unable to create ping endpoint."); return *responseSummary; } - // originally I was going to use a random number for this, but using the low bits of // the timestamp makes more sense, and will be unique enough for this. uint32_t pingSourceId = (uint32_t)(MidiClock::Now() & 0x00000000FFFFFFFF); wil::unique_event_nothrow allMessagesReceived; - allMessagesReceived.create(); + allMessagesReceived.create(wil::EventOptions::ManualReset); uint8_t receivedCount{ 0 }; @@ -126,6 +144,8 @@ namespace MIDI_CPP_NAMESPACE::implementation { internal::MidiTimestamp actualReceiveEventTimestamp = MidiClock::Now(); + internal::LogInfo(__FUNCTION__, L"Ping MessageReceivedHandler received message"); + uint32_t word0; uint32_t word1; uint32_t word2; @@ -146,17 +166,22 @@ namespace MIDI_CPP_NAMESPACE::implementation if (receivedCount == pingCount) { + internal::LogInfo(__FUNCTION__, L"Setting 'allMessagesReceived' event."); + allMessagesReceived.SetEvent(); } } else { // something really wrong happened. Our index has been messed up. + + internal::LogGeneralError(__FUNCTION__, L"Index of ping response is out of bounds"); } } else { // someone else is sending stuff to this ping service. Naughty if not another ping. + internal::LogGeneralError(__FUNCTION__, L"Another process is sending messages to this endpoint"); } }; @@ -177,6 +202,8 @@ namespace MIDI_CPP_NAMESPACE::implementation // send out the ping messages + internal::LogInfo(__FUNCTION__, L"Sending ping messages"); + for (uint32_t pingIndex = 0; pingIndex < pingCount; pingIndex++) { internal::PackedPingRequestUmp request; @@ -212,14 +239,28 @@ namespace MIDI_CPP_NAMESPACE::implementation //Sleep(0); } - // Wait for all responses to come in (receivedCount == pingCount). If not all responses come back, report the failure. - if (!allMessagesReceived.wait(timeoutMilliseconds)) + bool allReceivedFlag = allMessagesReceived.is_signaled(); + + if (!allReceivedFlag) { - responseSummary->InternalSetFailed(L"Not all ping responses received within appropriate time window."); - internal::LogGeneralError(__FUNCTION__, L"Not all ping responses received within appropriate time window."); + internal::LogInfo(__FUNCTION__, L"Waiting for responses to come in"); + + // Wait for all responses to come in (receivedCount == pingCount). If not all responses come back, report the failure. + allReceivedFlag = allMessagesReceived.wait(timeoutMilliseconds); + + if (!allReceivedFlag) + { + responseSummary->InternalSetFailed(L"Not all ping responses received within appropriate time window."); + internal::LogGeneralError(__FUNCTION__, L"Not all ping responses received within appropriate time window."); + } } - else + + allMessagesReceived.reset(); + + if (allReceivedFlag) { + internal::LogInfo(__FUNCTION__, L"All ping messages received"); + // all received responseSummary->InternalSetSucceeded(); @@ -227,8 +268,12 @@ namespace MIDI_CPP_NAMESPACE::implementation uint64_t totalPing{ 0 }; + internal::LogInfo(__FUNCTION__, L"Calculating delta timestamps"); + for (const auto& response : pings) { + // internal::LogInfo(__FUNCTION__, L"Calculating total ping"); + totalPing += response->ClientDeltaTimestamp(); responseSummary->InternalAddResponse(*response); @@ -236,6 +281,8 @@ namespace MIDI_CPP_NAMESPACE::implementation // does I need to remove the com_ptr ref or will going out of scope be sufficient? } + internal::LogInfo(__FUNCTION__, L"Calculating average ping"); + uint64_t averagePing = totalPing / responseSummary->Responses().Size(); responseSummary->InternalSetTotals(totalPing, averagePing); @@ -244,8 +291,25 @@ namespace MIDI_CPP_NAMESPACE::implementation session.DisconnectEndpointConnection(endpoint.ConnectionId()); session.Close(); + internal::LogInfo(__FUNCTION__, L"Returning response summary"); + return *responseSummary; } + catch (std::exception ex) + { + internal::LogStandardExceptionError(__FUNCTION__, L"Exception pinging service.", ex); + + if (responseSummary != nullptr) + { + responseSummary->InternalSetFailed(winrt::to_hstring(ex.what())); + + return *responseSummary; + } + else + { + return nullptr; + } + } catch (...) { internal::LogGeneralError(__FUNCTION__, L"Exception pinging service."); @@ -274,6 +338,8 @@ namespace MIDI_CPP_NAMESPACE::implementation foundation::Collections::IVector MidiService::GetInstalledTransportPlugins() { + internal::LogInfo(__FUNCTION__, L"Enter"); + auto transportList = winrt::single_threaded_vector(); try @@ -343,6 +409,8 @@ namespace MIDI_CPP_NAMESPACE::implementation foundation::Collections::IVector MidiService::GetInstalledMessageProcessingPlugins() { + internal::LogInfo(__FUNCTION__, L"Enter"); + // TODO: Need to implement GetInstalledMessageProcessingPlugins. For now, return an empty collection instead of throwing // This can be read from the registry, but the additional metadata requires calling into the objects themselves @@ -352,6 +420,8 @@ namespace MIDI_CPP_NAMESPACE::implementation foundation::Collections::IVector MidiService::GetActiveSessions() noexcept { + internal::LogInfo(__FUNCTION__, L"Enter"); + auto sessionList = winrt::single_threaded_vector(); try @@ -608,6 +678,9 @@ namespace MIDI_CPP_NAMESPACE::implementation bool const isFromConfigurationFile ) noexcept { + internal::LogInfo(__FUNCTION__, L"Enter"); + + auto iid = __uuidof(IMidiAbstractionConfigurationManager); winrt::com_ptr configManager; diff --git a/src/api/Client/Midi2Client/MidiSession.cpp b/src/api/Client/Midi2Client/MidiSession.cpp index 5fbb2042c..3d3e18e90 100644 --- a/src/api/Client/Midi2Client/MidiSession.cpp +++ b/src/api/Client/Midi2Client/MidiSession.cpp @@ -41,6 +41,9 @@ namespace MIDI_CPP_NAMESPACE::implementation else { // this can happen is service is not available or running + + internal::LogInfo(__FUNCTION__, L"Session start failed. Returning nullptr"); + return nullptr; } } @@ -114,11 +117,15 @@ namespace MIDI_CPP_NAMESPACE::implementation { // couldn't get the process name internal::LogGeneralError(__FUNCTION__, L"Unable to get current process name."); + + return false; } } else { internal::LogGeneralError(__FUNCTION__, L"Error activating IMidiSessionTracker."); + + return false; } } @@ -136,6 +143,12 @@ namespace MIDI_CPP_NAMESPACE::implementation return false; } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception starting session."); + + return false; + } return true; } @@ -168,6 +181,8 @@ namespace MIDI_CPP_NAMESPACE::implementation // generate internal endpoint Id auto connectionInstanceId = foundation::GuidHelper::CreateNewGuid(); + internal::LogInfo(__FUNCTION__, L"Initializing Endpoint Connection"); + if (endpointConnection->InternalInitialize(m_id, m_serviceAbstraction, connectionInstanceId, normalizedDeviceId, settings, autoReconnect)) { m_connections.Insert(connectionInstanceId, *endpointConnection); @@ -176,7 +191,7 @@ namespace MIDI_CPP_NAMESPACE::implementation } else { - internal::LogGeneralError(__FUNCTION__, L"WinRT Endpoint connection wouldn't start"); + internal::LogGeneralError(__FUNCTION__, L"WinRT Endpoint connection wouldn't initialize"); // TODO: Cleanup @@ -187,12 +202,18 @@ namespace MIDI_CPP_NAMESPACE::implementation { internal::LogHresultError(__FUNCTION__, L"hresult exception connecting to endpoint. Service or endpoint may be unavailable, or endpoint may not be the correct type.", ex); + return nullptr; + } + catch (...) + { + internal::LogGeneralError(__FUNCTION__, L"Exception creating endpoint connection"); + return nullptr; } } _Use_decl_annotations_ - midi2::MidiEndpointConnection MidiSession::CreateEndpointConnection( + midi2::MidiEndpointConnection MidiSession::CreateEndpointConnection( winrt::hstring const& endpointDeviceId, bool autoReconnect ) noexcept @@ -216,7 +237,7 @@ namespace MIDI_CPP_NAMESPACE::implementation midi2::MidiVirtualEndpointDeviceDefinition const& deviceDefinition ) noexcept { - internal::LogInfo(__FUNCTION__, L""); + internal::LogInfo(__FUNCTION__, L"Enter"); winrt::hstring endpointDeviceId{}; @@ -235,14 +256,14 @@ namespace MIDI_CPP_NAMESPACE::implementation json::JsonArray createVirtualDevicesArray; json::JsonObject endpointDefinitionObject; - internal::LogInfo(__FUNCTION__, L" creating association GUID"); + internal::LogInfo(__FUNCTION__, L"Creating association GUID"); // Create association guid auto associationGuid = foundation::GuidHelper::CreateNewGuid(); // set all of our properties. - internal::LogInfo(__FUNCTION__, L" setting json properties"); + internal::LogInfo(__FUNCTION__, L"Setting json properties"); internal::JsonSetGuidProperty( endpointDefinitionObject, @@ -265,18 +286,18 @@ namespace MIDI_CPP_NAMESPACE::implementation // TODO: Other props that have to be set at the service level and not in-protocol - internal::LogInfo(__FUNCTION__, L" appending endpoint definition"); + internal::LogInfo(__FUNCTION__, L"Appending endpoint definition"); internal::LogInfo(__FUNCTION__, endpointDefinitionObject.Stringify().c_str()); createVirtualDevicesArray.Append(endpointDefinitionObject); - internal::LogInfo(__FUNCTION__, L" adding virtual devices array to abstraction object"); + internal::LogInfo(__FUNCTION__, L"Adding virtual devices array to abstraction object"); abstractionObject.SetNamedValue( MIDI_CONFIG_JSON_ENDPOINT_COMMON_CREATE_KEY, createVirtualDevicesArray); - internal::LogInfo(__FUNCTION__, L" setting named value for virtual abstraction id"); + internal::LogInfo(__FUNCTION__, L"Setting named value for virtual abstraction id"); endpointCreationObject.SetNamedValue(virtualDeviceAbstractionId, abstractionObject); @@ -311,35 +332,35 @@ namespace MIDI_CPP_NAMESPACE::implementation auto activateConfigManagerResult = m_serviceAbstraction->Activate(iid, (void**)&configManager); - internal::LogInfo(__FUNCTION__, L"config manager activate call completed"); + internal::LogInfo(__FUNCTION__, L"Config manager activate call completed"); if (FAILED(activateConfigManagerResult) || configManager == nullptr) { - internal::LogGeneralError(__FUNCTION__, L" Failed to create device. Config manager is null."); + internal::LogGeneralError(__FUNCTION__, L"Failed to create device. Config manager is null."); return nullptr; } - internal::LogInfo(__FUNCTION__, L"config manager activate call SUCCESS"); + internal::LogInfo(__FUNCTION__, L"Config manager activate call SUCCESS"); auto initializeResult = configManager->Initialize(internal::StringToGuid(virtualDeviceAbstractionId.c_str()), nullptr, nullptr); if (FAILED(initializeResult)) { - internal::LogGeneralError(__FUNCTION__, L"failed to initialize config manager"); + internal::LogGeneralError(__FUNCTION__, L"Failed to initialize config manager"); return nullptr; } - internal::LogInfo(__FUNCTION__, L"config manager initialize call SUCCESS"); + internal::LogInfo(__FUNCTION__, L"Config manager initialize call SUCCESS"); CComBSTR response; response.Empty(); - internal::LogInfo(__FUNCTION__, L"sending json"); + internal::LogInfo(__FUNCTION__, L"Sending json to UpdateConfiguration"); internal::LogInfo(__FUNCTION__, topLevelTransportPluginSettingsObject.Stringify().c_str()); auto configUpdateResult = configManager->UpdateConfiguration(topLevelTransportPluginSettingsObject.Stringify().c_str(), false, &response); @@ -360,7 +381,7 @@ namespace MIDI_CPP_NAMESPACE::implementation if (!internal::JsonObjectFromBSTR(&response, responseObject)) { - internal::LogGeneralError(__FUNCTION__, L" Failed to read json response object from virtual device creation"); + internal::LogGeneralError(__FUNCTION__, L"Failed to read json response object from virtual device creation"); return nullptr; } @@ -395,7 +416,7 @@ namespace MIDI_CPP_NAMESPACE::implementation } else { - internal::LogGeneralError(__FUNCTION__, L" Virtual device creation failed (payload has false success value)"); + internal::LogGeneralError(__FUNCTION__, L"Virtual device creation failed (payload has false success value)"); return nullptr; } @@ -419,7 +440,7 @@ namespace MIDI_CPP_NAMESPACE::implementation } else { - internal::LogGeneralError(__FUNCTION__, L"Could not create connection"); + internal::LogGeneralError(__FUNCTION__, L"Could not add MidiVirtualEndpointDevice because connection was null"); } return connection; @@ -431,7 +452,9 @@ namespace MIDI_CPP_NAMESPACE::implementation { UNREFERENCED_PARAMETER(newName); - // TODO: + internal::LogInfo(__FUNCTION__, L"Enter"); + + // TODO: Implement UpdateName return false; diff --git a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj index 733058580..2ff8a3e2c 100644 --- a/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj +++ b/src/api/Client/Midi2Client/Windows.Devices.Midi2.vcxproj @@ -394,7 +394,6 @@ MidiEndpointConnection.idl - MidiEndpointConnection.idl @@ -407,9 +406,12 @@ MidiEndpointConnection.idl - - MidiEndpointDeviceInformation.idl - + + MidiEndpointConnection.idl + + + MidiEndpointDeviceInformation.idl + MidiEndpointDeviceInformationAddedEventArgs.idl diff --git a/src/api/Client/Midi2Client/trace_logging.cpp b/src/api/Client/Midi2Client/trace_logging.cpp index 231b3a7b7..f8e24ca15 100644 --- a/src/api/Client/Midi2Client/trace_logging.cpp +++ b/src/api/Client/Midi2Client/trace_logging.cpp @@ -87,9 +87,9 @@ namespace WindowsMidiServicesInternal "MIDI.HresultError", TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingKeyword(TRACE_KEYWORD_API_GENERAL), - TraceLoggingHResult(ex.code(), "HRESULT"), TraceLoggingString(location, "Location"), TraceLoggingWideString(message, "Message"), + TraceLoggingHResult(ex.code(), "HRESULT"), TraceLoggingWideString(ex.message().c_str(), "Error") ); } @@ -109,9 +109,28 @@ namespace WindowsMidiServicesInternal "MIDI.HresultError", TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingKeyword(TRACE_KEYWORD_API_GENERAL), - TraceLoggingHResult(hr, "HRESULT"), TraceLoggingString(location, "Location"), - TraceLoggingWideString(message, "Message") + TraceLoggingWideString(message, "Message"), + TraceLoggingHResult(hr, "HRESULT") + ); + } + + _Use_decl_annotations_ + void LogStandardExceptionError( + const char* location, + const wchar_t* message, + std::exception const& ex) noexcept + { + if (!g_traceLoggingRegistered) RegisterTraceLogging(); + + TraceLoggingWrite( + g_hLoggingProvider, + "MIDI.StandardExceptionError", + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingKeyword(TRACE_KEYWORD_API_GENERAL), + TraceLoggingString(location, "Location"), + TraceLoggingWideString(message, "Message"), + TraceLoggingString(ex.what(), "What") ); } @@ -128,7 +147,6 @@ namespace WindowsMidiServicesInternal TraceLoggingWrite( g_hLoggingProvider, "MIDI.GeneralError", - TraceLoggingOpcode(ERROR), TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingKeyword(TRACE_KEYWORD_API_GENERAL), TraceLoggingString(location, "Location"), @@ -168,10 +186,10 @@ namespace WindowsMidiServicesInternal "MIDI.UmpDataValidationError", TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingKeyword(TRACE_KEYWORD_API_DATA_VALIDATION), - TraceLoggingHexUInt32(firstWord, "UMPFirstWord"), - TraceLoggingUInt64(timestamp, "MIDITimestamp"), TraceLoggingString(location, "Location"), - TraceLoggingWideString(message, "Message") + TraceLoggingWideString(message, "Message"), + TraceLoggingHexUInt32(firstWord, "UMPFirstWord"), + TraceLoggingUInt64(timestamp, "MIDITimestamp") ); } @@ -191,10 +209,10 @@ namespace WindowsMidiServicesInternal "MIDI.UmpSizeValidationError", TraceLoggingLevel(WINEVENT_LEVEL_ERROR), TraceLoggingKeyword(TRACE_KEYWORD_API_DATA_VALIDATION), - TraceLoggingHexUInt32(providedSizeInWords, "ProvidedSizeInWords"), - TraceLoggingUInt64(timestamp, "MIDITimestamp"), TraceLoggingString(location, "Location"), - TraceLoggingWideString(message, "Message") + TraceLoggingWideString(message, "Message"), + TraceLoggingHexUInt32(providedSizeInWords, "ProvidedSizeInWords"), + TraceLoggingUInt64(timestamp, "MIDITimestamp") ); } diff --git a/src/api/Client/Midi2Client/trace_logging.h b/src/api/Client/Midi2Client/trace_logging.h index dd6f34562..ef7c87586 100644 --- a/src/api/Client/Midi2Client/trace_logging.h +++ b/src/api/Client/Midi2Client/trace_logging.h @@ -38,6 +38,7 @@ namespace WindowsMidiServicesInternal _In_z_ const char* location, _In_z_ const wchar_t* message) noexcept; + void LogHresultError( _In_z_ const char* location, _In_z_ const wchar_t* message, @@ -48,6 +49,11 @@ namespace WindowsMidiServicesInternal _In_z_ const wchar_t* message, _In_ HRESULT const hr) noexcept; + void LogStandardExceptionError( + _In_z_ const char* location, + _In_z_ const wchar_t* message, + _In_ std::exception const& ex) noexcept; + void LogGeneralError( _In_z_ const char* location, _In_z_ const wchar_t* message) noexcept; diff --git a/src/api/InBoxApps/mididmp/main.cpp b/src/api/InBoxApps/mididmp/main.cpp index b65cee97a..83e1b99b1 100644 --- a/src/api/InBoxApps/mididmp/main.cpp +++ b/src/api/InBoxApps/mididmp/main.cpp @@ -333,25 +333,32 @@ bool DoSectionMidi1ApiEndpoints(_In_ bool verbose) bool DoSectionPingTest(_In_ bool verbose, _In_ uint8_t pingCount) { + UNREFERENCED_PARAMETER(verbose); + try { - UNREFERENCED_PARAMETER(verbose); - OutputSectionHeader(MIDIDMP_SECTION_LABEL_PING_TEST); OutputNumericField(MIDIDMP_FIELD_LABEL_PING_ATTEMPT_COUNT, (uint32_t)pingCount); auto pingResult = midi2::MidiService::PingService(pingCount); + //std::cout << "DEBUG: PingService returned" << std::endl; + if (pingResult != nullptr) { + //std::cout << "DEBUG: pingresult != nullptr" << std::endl; + OutputNumericField(MIDIDMP_FIELD_LABEL_PING_RETURN_COUNT, pingResult.Responses().Size()); if (pingResult.Success()) { + //std::cout << "DEBUG: pingresult.Success()" << std::endl; + OutputTimestampField(MIDIDMP_FIELD_LABEL_PING_ROUND_TRIP_TOTAL_TICKS, pingResult.TotalPingRoundTripMidiClock()); OutputTimestampField(MIDIDMP_FIELD_LABEL_PING_ROUND_TRIP_AVERAGE_TICKS, pingResult.AveragePingRoundTripMidiClock()); + return true; } else { @@ -374,6 +381,7 @@ bool DoSectionPingTest(_In_ bool verbose, _In_ uint8_t pingCount) return false; } + return true; } @@ -533,6 +541,11 @@ int __cdecl main() DoSectionMidi1ApiEndpoints(verbose); // we don't bail if this fails + if (midiClock) + { + if (!DoSectionClock(verbose)) RETURN_FAIL; + } + if (transportsWorked) { // ping the service @@ -543,12 +556,6 @@ int __cdecl main() if (!DoSectionPingTest(verbose, pingCount)) RETURN_FAIL; } } - - if (midiClock) - { - if (!DoSectionClock(verbose)) RETURN_FAIL; - } - } catch (...) { diff --git a/src/api/Service/Exe/MidiEndpointProtocolManager.cpp b/src/api/Service/Exe/MidiEndpointProtocolManager.cpp index e5566c494..94f610f5d 100644 --- a/src/api/Service/Exe/MidiEndpointProtocolManager.cpp +++ b/src/api/Service/Exe/MidiEndpointProtocolManager.cpp @@ -454,7 +454,7 @@ CMidiEndpointProtocolManager::ProcessCurrentWorkEntry() // Create and open a connection to the endpoint, complete with metadata listeners DWORD mmcssTaskId{}; - ABSTRACTIONCREATIONPARAMS abstractionCreationParams{ MidiDataFormat_UMP }; + ABSTRACTIONCREATIONPARAMS abstractionCreationParams{ MidiDataFormat_UMP, nullptr }; RETURN_IF_FAILED(m_currentWorkItem.Endpoint->Initialize( m_currentWorkItem.EndpointInstanceId.c_str(), diff --git a/src/user-tools/midi-console/Midi/Midi.csproj b/src/user-tools/midi-console/Midi/Midi.csproj index bdadcac84..0e8dfef7d 100644 --- a/src/user-tools/midi-console/Midi/Midi.csproj +++ b/src/user-tools/midi-console/Midi/Midi.csproj @@ -27,11 +27,11 @@ - + - - + + diff --git a/src/user-tools/midi-console/Midi/Program.cs b/src/user-tools/midi-console/Midi/Program.cs index 6f23cd788..e51fb0d22 100644 --- a/src/user-tools/midi-console/Midi/Program.cs +++ b/src/user-tools/midi-console/Midi/Program.cs @@ -70,8 +70,8 @@ config.AddBranch("endpoint", endpoint => { - endpoint.AddExample("endpoint", "properties", "--verbose"); - //endpoint.AddExample("endpoint", "properties"); + //endpoint.AddExample("endpoint", "properties", "--verbose"); + endpoint.AddExample("endpoint", "properties"); endpoint.AddExample("endpoint", "send-message", "0x21234567"); endpoint.SetDescription(Strings.CommandEndpointDescription);