はつねの日記

Kinect, Windows 10 UWP, Windows Azure, IoT, 電子工作

Windowsストアアプリとリモートデバッグ、ときどきUIスレッド

Windowsストアアプリでの配置ターゲットとしては次の3つがあります。

  1. ローカルコンピューター
  2. シミュレーター
  3. リモートコンピューター

最初の2つはなじみがあると思いますが、最後の1つはあまり使う機会もないかもしれません。

しかし、Windows RT機のテストやデスクトップで開発していて手元の色々なタッチデバイスでテストしたい時などにはリモートコンピューターを配置先としてデバッグ実行を行う「リモートデバッグ」がとても便利です。

リモートデバッグの準備
前提条件
  • リモート デバイスと Visual Studio のコンピューターがネットワークに接続、またはイーサネット ケーブルを通じて直接接続する必要があります。 インターネットにデバッグはサポートされていません。

  • リモート デバイスは、リモート デバッグ コンポーネントを実行している必要があります。

  • リモート デバイスのリモート デバッガーをインストールする管理者である必要があります。 リモート デバッガーと通信するには、リモート デバイスへのユーザー アクセスできる必要があります。

ダウンロードとインストール

リモートデバッグを行うには、リモート側(つまりVisual Studioを実行していない側)に「Remote Tools for Visual Studio 2012」をインストールする必要があります。

Remote Toolsには、x64用、x86用、ARM用があるので、例えばSurface RT上でリモートデバッグしたいときは「Remote Tools for Visual Studio 2012 (ARM)」をインストールします。

Remote Tools for Visual Studio 2012はVisual Studio 2012のダウンロードサイトから入手できます。

http://www.microsoft.com/visualstudio/jpn/downloads

構成

インストールが終わったら構成が必要なのですが、ローカルとリモートで同じMicrosoftアカウントでログイン(管理者権限をつけておきます)しておき、それを同じサブネットのLAN内で接続してるならば特に難しい設定も不要です。

詳しい設定は「http://msdn.microsoft.com/ja-jp/library/vstudio/hh441469.aspx」を参照してください。

リモートデバッグの実行
  1. リモート側でRemote Debuggerを起動します。
  2. ローカル側で配置先を「リモートコンピューター」にしてデバッグ実行します。
  3. 初回のみリモートデバッガー接続ダイアログがでてくるのでリモート側のPCを選択します。
    image
  4. 自動的にリモート側にアプリが転送されて、Visual Studioとアプリが接続されてリモートデバッグ実行が開始されます。

リモートデバッグでもブレークポイントや変数の値の確認/変更などができます。

今回、リモートデバッグだけで発生した事象

次のようなコードを作成して、ローカルコンピューター、シミュレーター、リモートコンピューターをそれぞれ配置先にして実行してみました。

コンパス機能を取得するクラス

Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Imports Windows.Devices.Sensors
Public Class CompassModel
     Implements INotifyPropertyChanged
     Private WithEvents CompassWatcher As Compass
     Private _MagneticHeading As Double
     Public Property MagneticHeading As Double
         Get
             Return -Me._MagneticHeading
         End Get
         Set(value As Double)
             Me._MagneticHeading = value
             Call NotifyPropertyChanged()
         End Set
     End Property
     Public ReadOnly Property IsSupported As Boolean
         Get
             Return True
         End Get
     End Property
     Public Sub StartWatch()
         Try
             If Me.CompassWatcher Is Nothing Then
                 Me.CompassWatcher = Compass.GetDefault
                 Me.CompassWatcher.ReportInterval = 50
             End If
         Catch ex As Exception
         End Try
     End Sub
     Private Sub CompassWatcher_ReadingChanged(sender As Compass,
                                               e As CompassReadingChangedEventArgs) _
                             Handles CompassWatcher.ReadingChanged
         Me.MagneticHeading = e.Reading.HeadingMagneticNorth
     End Sub
     Public Sub StopWatch()
         Try
             If Me.CompassWatcher IsNot Nothing Then
                 Me.CompassWatcher = Nothing
             End If
         Catch ex As Exception
         End Try
     End Sub
     Public Event PropertyChanged(sender As Object,
                              e As PropertyChangedEventArgs) _
                  Implements INotifyPropertyChanged.PropertyChanged
     Protected Sub NotifyPropertyChanged(<CallerMemberName> Optional propertyName As String = Nothing)
         RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
     End Sub
End Class

コンパスクラスとXAMLクラスを接続するクラス

Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Imports Windows.Media.Capture
Public Class MainViewModel
     Implements INotifyPropertyChanged
     Private WithEvents VmCompassModel As New CompassModel
     Public ReadOnly Property MagneticHeading As Double
         Get
             Return Me.VmCompassModel.MagneticHeading
         End Get
     End Property
     Public Async Function Start() As System.Threading.Tasks.Task
         Me.VmCompassModel.StartWatch()
     End Function
     Public Sub [Stop]()
         Me.VmCompassModel.StopWatch()
     End Sub
     Public Event PropertyChanged(sender As Object,
                                  e As PropertyChangedEventArgs) _
                  Implements INotifyPropertyChanged.PropertyChanged
     Private Sub VmCompassModel_PropertyChanged(sender As Object,
                                       e As ComponentModel.PropertyChangedEventArgs) _
                                   Handles VmCompassModel.PropertyChanged
         RaiseEvent PropertyChanged(Me,
                                    New PropertyChangedEventArgs(e.PropertyName))
     End Sub
End 
Class

発生事象

リモートコンピューターを使っている時だけこのコードを実行時に次のようなエラーが発生します。

image

内容としては

型 'System.Runtime.InteropServices.COMException' の例外が System.dll で発生しましたが、ユーザー コード内ではハンドルされませんでした

追加情報: アプリケーションは、別のスレッドにマーシャリングされたインターフェイスを呼び出しました。 (HRESULT からの例外: 0x8001010E (RPC_E_WRONG_THREAD))

ということです。

もちろん、コンパス付の実機でローカル実行してもエラーになりません。ちょっと不思議な現象ですね。