Xaudio2をC++/CXでevent通知

Xaudio2をC++/CXで作成のところに少々追加です。
現在の状態だと音の終了などが外部からはわからない状態です。
これを防ぐために下記のような形にします。


// イベントのデリゲート
public delegate void SoundEndedHandler(int index);

public ref class xaudiolib sealed
{
   public:
      // 追加
      event SoundEndedHandler^ OnSoundEndedHandler;
      void RaiseEndEvent(int soundIndex);
}

void xaudiolib::RaiseEndEvent(int soundIndex)
{
      OnSoundEndedHandler(soundIndex);
}

class AudioFilePlayerCallBack : public IXAudio2VoiceCallback
{
    public:
      AudioFilePlayerCallBack(xaudiolib^ parent, Microsoft::WRL::ComPtr<IXAudio2> pXAudio2, Platform::String^ filename ,int soundIndex);

   private:
	xaudiolib^ parentRef;
}

CallBackの中で「parentRef->RaiseEndEvent(index);」をコールする形でイベントを呼び出せます。
直接は呼べないので、関数を経由する形で実装すれば呼び出せます。

Xaudio2をC++/CXで作成

Windowsストアアプリで音を鳴らす場合、MediaElementが一般的ですが、画面の遷移がある場合などには少々向きません。
鳴らしたままなどであればやり方もあるんですが、デメリットとして、ボリューム変更などができなくなります。
SharpDXを使用すればC#でもXAudio2を使用可能ですが、今回のはC++/CXで作成したものです。
マイクロソフトのサンプルなどを参考にとりあえずのレベルですが、作成しました。
C#からもWindowsランタイムコンポーネントとして引き込むことで使用できます。

※各種サンプルを公開されている方々とマイクロソフトに感謝を

読み込んでいるヘッダーです

// Microsoft WRL(Windows ランタイム C++ テンプレート ライブラリ) 用ヘッダー
#include <wrl.h>
// XAudio2用ヘッダー
#include <xaudio2.h>
#include <xaudio2fx.h>
// マルチメディア用ヘッダー
#include <mmreg.h>
// media fusion 用ヘッダー
#include <Mfidl.h>
#include <Mfapi.h>
#include <Mfreadwrite.h>
/* 上記に必要なライブラリ
xaudio2.lib
mfcore.lib
mfplat.lib
mfreadwrite.lib
mfuuid.lib
*/
#include "pch.h"
#include "xaudiolib.h"

using namespace Microsoft::WRL;
using namespace Platform;

namespace xaudio2lib
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// サウンドデータ
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	// BGM管理構造体
	struct xaudiolib::ImplSoundData
	{
		ImplSoundData::ImplSoundData(Platform::String^ filename,xaudiolib^ parent,int index) :
			sourceVoice(nullptr),
			//playData(data),
			isPlaying(false),
			isLoop(false),
			audioFilePlayerCallBack(parent->m_musicEngine, filename, index)
		{
			sourceVoice = audioFilePlayerCallBack.GetSourceVoice();

		}

		~ImplSoundData()
		{
			if (sourceVoice)
			{
				sourceVoice->DestroyVoice();
				sourceVoice = nullptr;
			}
		}

		IXAudio2SourceVoice*    sourceVoice;
		bool                    isPlaying;
		bool					isLoop;
		AudioFilePlayerCallBack			audioFilePlayerCallBack;
	};

	struct xaudiolib::ImplSeData
	{
		ImplSeData::ImplSeData(Platform::String^ filename,xaudiolib^ parent,int index) :
			sourceVoice(nullptr),
			//playData(data),
			isPlaying(false),
			isLoop(false),
			audioFilePlayerCallBack(parent->m_seEngine, filename, index)
		{
			sourceVoice = audioFilePlayerCallBack.GetSourceVoice();
		}

		~ImplSeData()
		{
			if (sourceVoice)
			{
				sourceVoice->DestroyVoice();
				sourceVoice = nullptr;
			}
		}

		IXAudio2SourceVoice*    sourceVoice;
		bool                    isPlaying;
		bool					isLoop;
		AudioFilePlayerCallBack			audioFilePlayerCallBack;
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	xaudiolib::xaudiolib(void):m_soudBgmDataList(),m_soudSeDataList()
	{
		XAudio2Initialize();
	}


	xaudiolib::~xaudiolib(void)
	{
		seList.clear();
		bgmList.clear();

		XAudio2Clear();
	}

	void xaudiolib::AddBgmFileName(Platform::String^ _fileName)
	{
		bgmList.push_back(_fileName);
	}

	void xaudiolib::AddSeFileName(Platform::String^ _fileName)
	{
		seList.push_back(_fileName);
	}

	void xaudiolib::Initialize()
	{
	}

	void xaudiolib::XAudio2Initialize()
	{
		m_musicMasteringVoice = nullptr;
		m_audioAvailable = false;

		UINT32 flags = 0;

		// BGM用XAudio2ポインタ作成
		DX::ThrowIfFailed(
			XAudio2Create(&m_musicEngine, flags)
			);

		// BGM用ミキサーの作成
		HRESULT hr = m_musicEngine->CreateMasteringVoice(&m_musicMasteringVoice);
		if (FAILED(hr))
		{
			m_audioAvailable = false;
			return;
		}

		// SE用XAudio2ポインタ作成
		DX::ThrowIfFailed(
			XAudio2Create(&m_seEngine, flags)
			);

		// SE用ミキサーの作成
		hr = m_seEngine->CreateMasteringVoice(&m_seMasteringVoice);
		if (FAILED(hr))
		{
			m_audioAvailable = false;
			return;
		}

		m_audioAvailable = true;
	}

	void xaudiolib::XAudio2Clear()
	{
		if(m_audioAvailable)
		{
		for (size_t index = 0; index < bgmList.size(); ++index)
		{
			m_soudBgmDataList[index]->audioFilePlayerCallBack.AudioFilePlayerEnd(index);
		}

		for (size_t index = 0; index < seList.size(); ++index)
		{
			m_soudSeDataList[index]->audioFilePlayerCallBack.AudioFilePlayerEnd(index);
		}

		// マスターVoiceの解放
			m_musicMasteringVoice->DestroyVoice();
			m_musicMasteringVoice = nullptr;

			m_seMasteringVoice->DestroyVoice();
			m_seMasteringVoice = nullptr;

			// m_musicEngine/m_seEngineは「Microsoft::WRL::ComPtr」で自動的に開放

			m_audioAvailable = false;
		}
	}

	// サウンドデータを読み込む
	void xaudiolib::XAudio2Load()
	{
		for (size_t index = 0; index < bgmList.size(); ++index)
		{
			AddBgmSound(bgmList[index], index);
		}

		for (size_t index = 0; index < seList.size(); ++index)
		{
			AddSeSound(seList[index], index);
		}
	}

	// BGMデータを登録する
	size_t xaudiolib::AddBgmSound(_In_ Platform::String^ soundFileName, _In_ int index)
	{
		std::shared_ptr<ImplSoundData> implSoundData(new ImplSoundData(soundFileName,this,index));

		m_soudBgmDataList.push_back(implSoundData);
		return (m_soudBgmDataList.size() - 1);
	}

	// SEデータを登録する
	size_t xaudiolib::AddSeSound(_In_ Platform::String^ seFileName, _In_ int index)
	{
		std::shared_ptr<ImplSeData> implSeData(new ImplSeData(seFileName,this,index));

		m_soudSeDataList.push_back(implSeData);
		return (m_soudSeDataList.size() - 1);
	}


	bool xaudiolib::IsBgmPlay(int bgmIndex, bool bLoop)
	{

		if(!m_audioAvailable)
		{
			return false;
		}

		m_soudBgmDataList[bgmIndex]->audioFilePlayerCallBack.IsPlay(bLoop, bgmIndex);

		return true;
	}

	bool xaudiolib::IsBgmStop(int bgmIndex)
	{

		if(!m_audioAvailable)
		{
			return false;
		}

		m_soudBgmDataList[bgmIndex]->audioFilePlayerCallBack.IsStop();

		return true;
	}

	bool xaudiolib::IsSePlay(int seIndex)
	{

		if(!m_audioAvailable)
		{
			return false;
		}

		m_soudSeDataList[seIndex]->audioFilePlayerCallBack.IsPlay(false, seIndex);

		return true;
	}

	bool xaudiolib::IsSeStop(int seIndex)
	{

		if(!m_audioAvailable)
		{
			return false;
		}

		m_soudSeDataList[seIndex]->audioFilePlayerCallBack.IsStop();

		return true;
	}

	bool xaudiolib::IsBgmPlaying(int bgmIndex)
	{
		if(!m_audioAvailable)
		{
			return false;
		}

		return m_soudBgmDataList[bgmIndex]->audioFilePlayerCallBack.bPlaying;
	}

	bool xaudiolib::IsSePlaying(int bgmIndex)
	{
		if(!m_audioAvailable)
		{
			return false;
		}

		return m_soudSeDataList[bgmIndex]->audioFilePlayerCallBack.bPlaying;
	}

	bool xaudiolib::SuspendBgm()
	{
		if(!m_audioAvailable)
		{
			return false;
		}

		this->m_musicEngine->StopEngine();

		return true;
	}

	bool xaudiolib::ResumeBgm()
	{
		if(!m_audioAvailable)
		{
			return false;
		}

		this->m_musicEngine->StartEngine();

		return true;
	}

	bool xaudiolib::SetBgmVolume(int bgmIndex, float volume)
	{
		if(!m_audioAvailable || m_soudBgmDataList[bgmIndex] == nullptr)
		{
			return false;
		}

		m_soudBgmDataList[bgmIndex]->sourceVoice->SetVolume(volume);

		return true;
	}
	
	bool xaudiolib::GetBgmVolume(int bgmIndex, float *volume)
	{
		if(!m_audioAvailable || m_soudBgmDataList[bgmIndex] == nullptr)
		{
			return false;
		}

		m_soudBgmDataList[bgmIndex]->sourceVoice->GetVolume(volume);

		return true;
	}

	bool xaudiolib::SetSeVolume(int seIndex, float volume)
	{
		if(!m_audioAvailable)
		{
			return false;
		}

	}
	
	bool xaudiolib::GetSeVolume(int seIndex, float* volume)
	{
		if(!m_audioAvailable)
		{
			return false;
		}

	}


	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


	AudioFilePlayerCallBack::AudioFilePlayerCallBack(Microsoft::WRL::ComPtr<IXAudio2> pXAudio2, Platform::String^ filename ,int soundIndex)
	{

		this->mfSourceReader = this->mediaReader.LoadMedia(filename);

		this->iSoundIndex = soundIndex;

		// Get the Media Foundation media type
		ComPtr<IMFMediaType> mfMediaType;
		HRESULT hresult = mfSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, &mfMediaType);

		if (FAILED(hresult))
			throw ref new COMException(hresult, "MFSourceReader->GetCurrentMediaType 取得に失敗");

		// Create a WAVEFORMATEX from the media type
		WAVEFORMATEX* pWaveFormat;
		unsigned int waveFormatLength;
		hresult = MFCreateWaveFormatExFromMFMediaType(mfMediaType.Get(), &pWaveFormat, &waveFormatLength);

		if (FAILED(hresult))
			throw ref new COMException(hresult, "MFCreateWaveFormatExFromMFMediaType 取得に失敗");

		// SoourceVoiceを作成
		hresult = pXAudio2->CreateSourceVoice(
			&pSourceVoice,
			pWaveFormat,
			XAUDIO2_VOICE_NOPITCH,
			1.0f,
			this);

		// WaveFomatを開放
		CoTaskMemFree(pWaveFormat);

		bPlaying = false;

	}

	void AudioFilePlayerCallBack::AudioFilePlayerEnd(int bgmIndex)
	{
		if(pSourceVoice!=nullptr)
		{
			this->IsStop();

			pSourceVoice->DestroyVoice();

			HRESULT hresult = MFShutdown();

			if (FAILED(hresult))
				throw ref new COMException(hresult, "MFShutdown failure");
		}
	}

	void AudioFilePlayerCallBack::SubmitBuffer()
	{
		// Get the next block of audio data
		int audioBufferLength;
		byte * pAudioBuffer = GetNextBlock(&audioBufferLength);

		if (pAudioBuffer != nullptr)
		{
			// Create an XAUDIO2_BUFFER for submitting audio data
			XAUDIO2_BUFFER buffer = {0};
			buffer.Flags = XAUDIO2_END_OF_STREAM;
			buffer.AudioBytes = audioBufferLength;
			buffer.pAudioData = pAudioBuffer;
			buffer.pContext = pAudioBuffer;
			HRESULT hresult = pSourceVoice->SubmitSourceBuffer(&buffer);

			if (FAILED(hresult))
				throw ref new COMException(hresult, "IXAudio2SourceVoice->SubmitSourceBuffer failure");
		}
	}

	void AudioFilePlayerCallBack::IsPlay(bool bLoop,int iSoundIndex)
	{
		this->bLoop = bLoop;
		this->soundIndex = iSoundIndex;

		// 停止処理
		this->IsStop();

		// 再生バッファ
		SubmitBuffer();
		SubmitBuffer();

		endOfFile = false;

		// 再生開始
		pSourceVoice->Start(0);

		bPlaying = true;
	}

	void AudioFilePlayerCallBack::IsStop()
	{
		bPlaying = false;

		pSourceVoice->Stop(0);
		pSourceVoice->FlushSourceBuffers();

		// 再生ポインタを先頭に
		this->ReStart();

	}

	void AudioFilePlayerCallBack::ReStart()
	{
		if (mfSourceReader == nullptr)
		{
			return;
		}

		PROPVARIANT var = {0};
		var.vt = VT_I8;

		DX::ThrowIfFailed(
			mfSourceReader->SetCurrentPosition(GUID_NULL, var)
			);
	}

	IXAudio2SourceVoice* AudioFilePlayerCallBack::GetSourceVoice()
	{
		return this->pSourceVoice;
	}

	byte * AudioFilePlayerCallBack::GetNextBlock(int * pAudioBufferLength)
	{
		// IMFSampleを取得
		ComPtr<IMFSample> mfSample;
		DWORD flags = 0;
		HRESULT hresult = mfSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 
			0, nullptr, &flags, nullptr, &mfSample);
		if (FAILED(hresult))
			throw ref new COMException(hresult, "MFSourceReader->ReadSample failure");

		// ファイルの終わりを確認する
		if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
		{
			endOfFile = true;
			*pAudioBufferLength = 0;
			return nullptr;
		}

		// データをバッファへ
		ComPtr<IMFMediaBuffer> mfMediaBuffer;
		hresult = mfSample->ConvertToContiguousBuffer(&mfMediaBuffer);

		if (FAILED(hresult))
			throw ref new COMException(hresult, "IMFSample->ConvertToContiguousBuffer failure");

		// バッファが勝手に開放されないようにロックしておく
		uint8 * pAudioData = nullptr;
		DWORD audioDataLength = 0;
		hresult = mfMediaBuffer->Lock(&pAudioData, nullptr, &audioDataLength); 

		if (FAILED(hresult))
			throw ref new COMException(hresult, "IMFMediaBuffer->Lock failure");

		byte * pAudioBuffer = new byte[audioDataLength];
		CopyMemory(pAudioBuffer, pAudioData, audioDataLength);
		hresult = mfMediaBuffer->Unlock();

		if (FAILED(hresult))
			throw ref new COMException(hresult, "IMFMediaBuffer->Unlock failure");

		*pAudioBufferLength = audioDataLength;
		return pAudioBuffer;
	}

	// IXAudio2VoiceCallback バッファの終わりに呼び出される
	void _stdcall AudioFilePlayerCallBack::OnBufferEnd(void* pContext)
	{
		// Remember to free the audio buffer!
		delete[] pContext;

		// Either submit a new buffer or clean up
		if(bPlaying)
		{
			if (!endOfFile)
			{
				SubmitBuffer();
			}
			else
			{
				if(!bLoop)
				{
					this->IsStop();
				}
				else
				{
					this->ReStart();

					SubmitBuffer();
					SubmitBuffer();

					endOfFile = false;
				}
			}
		}
	}
};

まだ作り掛&未整理のため読みにくいですがご了承ください。
ファイルの読み込みにはマイクロソフトのサンプルに有った「MediaReader」に少々手を入れて使用しています。
「MediaReader」は転載禁止のため掲載できないため、MSのサンプルをご確認ください。

StreamSoketのSampleコード

非同期化していないところがあったりするのであまりいいサンプルではないですが、StreamSoketでの通信コードです。
WriteAsync、LoadAsyncで書き込み/読み込みを行うようにしてあります。
DataReader、DataWriterを使用した際にうまくいかなかったので、このような形で実装しています。

///////////////////////////////////////////////////////////////////////////////////////////////
// サーバへ接続等

        #region サーバへ接続
        /// <summary>
        /// TCPでサーバへ接続
        /// </summary>
        /// <param name="hostname"></param>
        /// <param name="serverport"></param>
        public async void TcpSocketConnect(string hostname, string serverport)
        {
            SocketStatusEventArgs socketevent = new SocketStatusEventArgs();

            // 接続済み
            if (SocketDatacontext.Connected)
            {
                socketevent.SocketStatus = StringResourceLoader.GetString("Already");
                OnSocketStatus(socketevent);
                cryearthlib.DebugLoging.DebugPrint(socketevent.SocketStatus);
                await Task.Run(() => { throw new PopClassException(SocketDatacontext.SocketMessage); });
                return;
            }

            if (SocketDatacontext.TcpSocket == null)
            {
                SocketDatacontext.TcpSocket = new Windows.Networking.Sockets.StreamSocket();

                bool currentSetting = SocketDatacontext.TcpSocket.Control.NoDelay;
                SocketDatacontext.TcpSocket.Control.KeepAlive = true;
                SocketDatacontext.TcpSocket.Control.NoDelay = false;
            }

            try
            {
                /// タイムアウト用
                CancellationTokenSource cts = new CancellationTokenSource();

                socketevent.SocketStatus = StringResourceLoader.GetString("Trying");

                OnSocketStatus(socketevent);

                cryearthlib.DebugLoging.DebugPrint(socketevent.SocketStatus); 
                
                var serverHost = new HostName(hostname);

                // タイムアウトの設定
                cts.CancelAfter(10000);

                await SocketDatacontext.TcpSocket.ConnectAsync(serverHost, serverport).AsTask(cts.Token);

                // DataReaderの作成
                SocketDatacontext.PeerReader = new DataReader(SocketDatacontext.TcpSocket.InputStream);
                SocketDatacontext.PeerReader.InputStreamOptions = InputStreamOptions.Partial;

                SocketDatacontext.Connected = true;
                SocketDatacontext.Closing = false;
                socketevent.SocketStatus = StringResourceLoader.GetString("Connection");
                OnSocketStatus(socketevent);
                cryearthlib.DebugLoging.DebugPrint(socketevent.SocketStatus);
            }
            catch (Exception ex)
            {
                socketevent.SocketStatus = StringResourceLoader.GetString("Connecterror") + ex.Message;
                OnSocketStatus(socketevent);

                SocketDatacontext.Closing = true;
                SocketDatacontext.TcpSocket.Dispose();
                SocketDatacontext.TcpSocket = null;
                SocketDatacontext.Connected = false;

                OnSocketStatus(socketevent);
                cryearthlib.DebugLoging.DebugPrint(socketevent.SocketStatus);
                throw new PopClassException(SocketDatacontext.SocketMessage);
            }
        }
        #endregion

///////////////////////////////////////////////////////////////////////////////////////////////
// サーバへデータを送る

        #region サーバへデータを送る
        public async void ResultSend(string req)
        {
            SocketStatusEventArgs socketevent = new SocketStatusEventArgs();

            if (!SocketDatacontext.Connected)
            {
                socketevent.SocketStatus = StringResourceLoader.GetString("Mustbeconnectedtosend");
                OnSocketStatus(socketevent);

                cryearthlib.DebugLoging.DebugPrint(socketevent.SocketStatus);
                return;
            }

            try
            {
                // サーバへリクエスト送信
                if (req != "")
                {
                    StreamSocketLib writeSokect = new StreamSocketLib();

                    socketevent.SocketStatus = StringResourceLoader.GetString("Tryingtosenddata");
                    OnSocketStatus(socketevent);

                    writeSokect.DataSend(SocketDatacontext.TcpSocket, req);

                    cryearthlib.DebugLoging.DebugPrint("Data Writer End");
                }
            }
            catch (Exception exception)
            {
                socketevent.SocketStatus = "Send data or receive failed with error: " + exception.Message;
                OnSocketStatus(socketevent);
            }
        }
        #endregion
        /// <summary>
        /// データ書き込みフロント
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="senddata"></param>
        public void DataSend(StreamSocket socket, string req)
        {
            _socket = socket;

            Encoding encoding = Encoding.UTF8;

            byte[] bydata = encoding.GetBytes(req + Environment.NewLine);

            if (socket == null)
            {
                cryearthlib.DebugLoging.DebugPrint("socket Error : socket null");
                throw new Exception("socket Error : socket null");
            }

            try
            {
                var task = SocketDataWrite(bydata);
                if (!task.IsCompleted)
                {
                    var ret = task.Wait(timeout);
                    if (!ret)
                    {
                        cryearthlib.DebugLoging.DebugPrint("Write Time Out");
                    }
                }

                if (!task.Result)
                {
                    cryearthlib.DebugLoging.DebugPrint("Write Time Out");
                }
            }
            catch (Exception ex)
            {
                cryearthlib.DebugLoging.DebugPrint("DataWrite Error : " + ex.Message);
                throw new Exception("DataWrite Error : " + ex.Message);
            }
        }

        /// <summary>
        /// データ書き込み本体
        /// </summary>
        /// <param name="senddata"></param>
        /// <returns></returns>
        public async Task<bool> SocketDataWrite(byte[] senddata)
        {
            var writeTask = _socket.OutputStream.WriteAsync(senddata.AsBuffer()).AsTask();

            if (!writeTask.IsCompleted)
            {
                var writeret = writeTask.Wait(timeout);
                if (!writeret)
                {
                    return false;
                }
            }

            var flushTask = _socket.OutputStream.FlushAsync().AsTask();

            if (!flushTask.IsCompleted)
            {
                var flushret = flushTask.Wait(timeout);
                if (!flushret)
                {
                    return false;
                }
            }
            return true;
        }



///////////////////////////////////////////////////////////////////////////////////////////////
// サーバからのデータを読み込む

        #region サーバからのデータを読み込む
        public async Task<string> ResultRead()
        {
            SocketStatusEventArgs socketevent = new SocketStatusEventArgs();

            if (!SocketDatacontext.Connected)
            {
                socketevent.SocketStatus = StringResourceLoader.GetString("Mustbeconnectedtosend");
                OnSocketStatus(socketevent);

                return null;
            }

            try
            {
                var lastChar = string.Empty;
                var fullString = string.Empty;

                cryearthlib.Sleep(200);
                
                socketevent.SocketStatus = StringResourceLoader.GetString("Tryingtosenddata");
                OnSocketStatus(socketevent);

                CancellationTokenSource cts = new CancellationTokenSource(1000);
                cts.Token.ThrowIfCancellationRequested();
                var bytesRead = SocketDatacontext.PeerReader.LoadAsync(1024);

                Debug.WriteLine("StreamWriteAndRead : reader.LoadAsync : " + bytesRead.Status.ToString());

                if (bytesRead.Status != Windows.Foundation.AsyncStatus.Completed)
                {
                    cts.Cancel();

                    Debug.WriteLine("StreamWriteAndRead : cts.Cancel");
                    Debug.WriteLine("StreamWriteAndRead : " + bytesRead.Status.ToString());
                }
                else
                {
                    Debug.WriteLine("StreamWriteAndRead : ReadString : " + bytesRead.Status.ToString());

                    while (SocketDatacontext.PeerReader.UnconsumedBufferLength > 0)
                    {
                        fullString += SocketDatacontext.PeerReader.ReadString(SocketDatacontext.PeerReader.UnconsumedBufferLength);
                    }
                }

                SocketDatacontext.SocketMessage = socketevent.SocketStatus = fullString;
                OnSocketStatus(socketevent);

                cryearthlib.DebugLoging.DebugPrint(socketevent.SocketStatus);
                cryearthlib.DebugLoging.DebugPrint("reader.End");
                return fullString;
            }
            catch (Exception exception)
            {
                socketevent.SocketStatus = "Receive failed with error: " + exception.Message;
                OnSocketStatus(socketevent);
                cryearthlib.DebugLoging.DebugPrint(socketevent.SocketStatus);
                return socketevent.SocketStatus;
            }
        }
#endregion

GridのRow、Columnに合わせてドラッグ移動

とりあえずドラッグの方向に合わせて移動
これだけだと過敏すぎるので移動量か移動先のRow、Column数値をとって行うとかしないとだめだと思う。

GridViewItemでDragItemThemeAnimationなんかの組み合わせのほうがいいような気もする

またはこれで行ってる「SetValue(Grid.ColumnProperty,~)」を利用してオセロや囲碁などのほうが現実的かもしれない。

        private void MainChara_ManipulationDelta(object sender, Windows.UI.Xaml.Input.ManipulationDeltaRoutedEventArgs e)
        {
        	// TODO: ここにイベント ハンドラーのコードを追加します。
            // MainChara.SetValue(Grid.RowProperty, 3);
            var radian = Math.Atan2(e.Delta.Translation.Y, e.Delta.Translation.X);
            double degree = radian * 180.0 / 3.141592653589793;

            if (degree < 0)
            {
                degree += 360;
            }

            if (degree > 315 && degree < 380)
            { 
                // 右
                var value = (int)MainChara.GetValue(Grid.ColumnProperty);
                if (value != 6)
                {
                    value++;
                    MainChara.SetValue(Grid.ColumnProperty, value);
                }
            }
            else if (degree > 0 && degree < 45)
            {
                // 右
                var value = (int)MainChara.GetValue(Grid.ColumnProperty);
                if (value != 6)
                {
                    value++;
                    MainChara.SetValue(Grid.ColumnProperty, value++);
                }
            }
            else if (degree > 45 && degree < 135)
            { 
                // 下
                var value = (int)MainChara.GetValue(Grid.RowProperty);
                if (value != 6)
                {
                    value++;
                    MainChara.SetValue(Grid.RowProperty, value);
                }
            }
            else if (degree > 135 && degree < 225)
            {
                // 左
                var value = (int)MainChara.GetValue(Grid.ColumnProperty);
                if (value != 0)
                {
                    value--;
                    MainChara.SetValue(Grid.ColumnProperty, value);
                }
            }
            else
            {
                // 上
                var value = (int)MainChara.GetValue(Grid.RowProperty);
                if (value != 0)
                {
                    value--;
                    MainChara.SetValue(Grid.RowProperty, value);
                }
            }
        }

Windowsストアアプリでの物理キーボードの有無

物理キーボードの有無を確認することで、「キーボード操作」とタブレットでの「コントローラーUI操作」の表示切替を実装することができる。

「KeyboardCapabilities Class」を使用して確認
「KeyboardPresent」が読み取り専用であるので、これを確認すること。

下記のページで確認したもの
http://msdn.microsoft.com/ja-jp/library/windows/apps/windows.devices.input.keyboardcapabilities.aspx

デスクトップでWindowsRuntimeAPIを使う場合の忘備録

MSエヴァンジェリストのVS魂100連発から
http://www.youtube.com/watch?v=xmwTbfWKDyA

・最初の設定
デスクトップアプリのプロジェクトをいったん「プロジェクトのアンロード」を行う
アンロードしたプロジェクトを右クリックして「.csprojの編集」をクリック
「PropertyGroup」内に「TargetPlatformVersion」を追加して「8.0」とする
上書き保存したらプロジェクトを再読み込みする
「参照設定」から「参照の追加」をクリックすると左側に「Windows」が増えているのでコアから「Windows」を追加
「ブラウズ」から「参照」で「\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5」から「System.Runtime.dll」を追加する。
「using Windows.Foundation;」を追加する。
「System.Runtime.dll」は.NETのCollectionとRTのCollectionに互換性がないため、その互換変換のために必要になる。

・async/awaitの非同期に対応する
「参照設定」から「参照の追加」をクリック
「ブラウズ」から「参照」で「\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5」から「System.Runtime.WindowsRuntime.dll」「System.Threading.Tasks.dll」の二つを追加
これを追加することで「async/await」が使用可能になる。
使わない場合「IAsyncOperation」をもとに書くようにする。

今更ながらの忘備録、これでストアアプリ管理ツールの作成などにもWindowsRuntimeAPIを使える。

デバッグ用のメモ

TDDを徹底すればこれはあまり必要ないかもしれないが、あって困るものではないので、メモ代わりに書いておく
公式サンプルなどから集めたものなので、あまりきれいではない。
機能を追加するとすれば、ローカルストレージ(またはSQL)への保存機能を追加することで、追いかけやすくなるのではないかと思われる。

using System.Runtime.CompilerServices;
public static class DebugLoging
{
// WinRTコアイベントのDispatcher
// 使用する際は「Window.Current.Dispatcher;」を渡すこと
// debug情報ページをInFreamに読み込んでいる
public static CoreDispatcher coreDispatcher;
// 書き出しに使用するTextBlock
public static TextBlock debugOutputTextBlock;

// Cでのマクロ、__LINE__ , __FILE__ や
// C#(WindowsPhone)でのStackFram は使用できないので、
// C#5.0の「System.Runtime.CompilerServices」を使用する
public static async void DebugPrint(string msg,
[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0,
[CallerMemberName] string member = "")
{
var fullMessage = string.Format("{0}:{1}\r\n\t{2}: {3}", file, line, member, msg);

Debug.WriteLine(fullMessage);

if (coreDispatcher != null)
{
await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
debugOutputTextBlock.Text =
debugOutputTextBlock.Text +
DateTime.Now.ToString(@"M/d/yyyy hh:mm:ss tt") + " " + fullMessage + "\r\n";
});
}
}
}

PageのFrame読み込みに関するメモ

PageをNavigationでそれぞれ個別ページ遷移呼び出しではなくFrameを宣言して、その中に読み込むことができる。
所謂HTMLでいうところの「iframe」のような形が可能

・元PageでのXAML宣言

<Frame x:name="otherPageFrame" />

・元ページでの呼び出し

otherPageFrame.Navigate(typeof(OtherPage), this);

PageをFrameとして入れ込んでいるため、各ページでコードを管理できるが、呼び出し順(Loadedイベントで特に)などで問題が起きやすいものと思われる
UIコントロールが有効になっていない時に読み込むことはできないので、当たり前とは思われる。
・直接LoadedイベントをXAMLに宣言せず、「InitializeComponent()」の後、または「OnNavigatedTo」でイベントを有効化する
・「OnNavigatedFrom」でイベントを解除するのを忘れないこと

//まだまとまっていないので、掘り下げること