[CakePHP] IDを指定して行を更新する

UPDATE を使って指定行を更新する。

■Model

Model はそのまま

■Controller

/Test/update?id=10 とか /Test/update/10 で更新できるようにしておく。

in Controller/TestController.php

	public function update($id=null) {
		if (isset($this->params['url']['id'])) $id = $this->params['url']['id'];
		if ( $id != null ) {
			$this->Test->read(null, $id);
			$this->Test->set('Age',30 );
			$this->Test->set('UpdateDate',date('c'));
			$this->Test->save();
		}
		$this->set('Test',$this->Test->find('all'));
	}

read した後に set して save する。これは、PK(ID) が入っているので read がないパターンも作れるハズ。

複数の set の場合は、array を使っても ok。

	public function update($id=null) {
		if (isset($this->params['url']['id'])) $id = $this->params['url']['id'];
		if ( $id != null ) {
			$this->Test->read(null, $id);
			$this->Test->set(
				array('Age'=>30,
					'UpdateDate',date('c')));
			$this->Test->save();
		}
		$this->set('Test',$this->Test->find('all'));
	}

■View

View は同じ

■結果

更新した結果が表示される。

※UPDATE のために COUNT が3回呼び出されているのがなんとも。たくさんのUPDATEをする場合には、パフォーマンスに影響を与えるので、これは調節が必要。

カテゴリー: CakePHP | [CakePHP] IDを指定して行を更新する はコメントを受け付けていません

[CakePHP] IDを指定して行を削除する

DELETE を使って指定行を削除する。

■Model

Model はそのまま

■Controller

/Test/delete?id=10 とか /Test/delete/10 で削除できるようにしておく。

in Controller/TestController.php

	public function delete($id=null) {
		if (isset($this->params['url']['id'])) $id = $this->params['url']['id'];
		if ( $id != null ) {
			// $this->Test->read(null, $id);
			$this->Test->delete( $id );
   		}
		$this->set('Test',$this->Test->find('all'));
	}

delete で削除する。read は必要ないみたい…あとでチェックする。

■View

View は同じ

■結果

削除した結果が表示される。

※DELETEする前に COUNT しているのが謎だが…後で調べる。マッチする行がない場合には、DELETE が呼び出されないみたい。

カテゴリー: CakePHP | [CakePHP] IDを指定して行を削除する はコメントを受け付けていません

[CakePHP] 新しい行を挿入する

INSERT を使って新しい行を追加する。
実際には、新しいIDを作成する、新しい行を追加する、2手順になる。なので、正確に作るならば新しいIDを作りながら insert をした後に update をしないといけない(競合が発生するため)のだが、まあ、ひとまず作ってみるということで。

■テーブル構造

CREATE TABLE `test` (
  `ID` int NOT NULL,
  `Name` varchar(100) NOT NULL,
  `Age` int DEFAULT NULL,
  `UpdateDate` datetime NOT NULL
);

ID は auto_incriment を使ってもいいのだが、ここはそのまま int で。
データ同期が発生する…ってのを考慮して、UpdateDate の列を作っておく。

■Model

新しい ID を作るための GetNewId メソッドだけ作っておく。

class Test extends AppModel
{
	var $useTable = 'Test';
	function GetNewId() {
		$sql = 	"SELECT max(id)+1 as ID FROM Test";
		$ret = $this->query($sql);
		return $ret[0][0]['ID'];
	}
}

そういえば、行数で苦労している人もいるみたいなのだが、$this->query をした後に RowCount すると遅くなる(無駄な行を検索する)ので、SQL の COUNT 関数を使う。

	function GetCount() {
		$sql = 	"SELECT count(id) as CNT FROM Test";
		$ret = $this->query($sql);
		return $ret[0][0]['CNT'];
	}

な感じにすれば OK。適宜、WHERE 句を付け加えることになるが…これはまた別途考える。

■Controller

確認用に index メソッドで検索できるようにしておく。
insert メソッドが挿入部分。

in Controller/TestController.php

class TestController extends AppController {
	public function index() {
		$id = null;
		if (isset($this->params['url']['id'])) $id = $this->params['url']['id'];

		if ( $id != null ) {
			$this->set('Test',$this->Test->findAllById($id));
		} else {
			$this->set('Test',$this->Test->find('all'));
		}
	}
	public function insert() {
		$id = $this->Test->GetNewId();
		$this->set('ID',  $id );
		$data = array( 
			'ID' => $id,
			'Name' => 'tomoaki',
			'Age'  => 20,
			'UpdateDate' => date('c'));
			
		// $this->Test->create();
		$this->Test->save( $data );
		$this->set('Test',$this->Test->find('all'));
		// $this->redirect('.');
	}
}

ID を $this->set しているのは確認のため。
新しい行を、$data = array で作成して、save の順で呼び出し。ID を自分で作成しているので create は呼び出す必要はないかも…後で調べる。

$data の中身は、本来はクライアントから Web API で送るパターン。簡単な文字列ならば URL でよいのだが、ここは POST に統一しておくのが便利だと思う。
日付はサーバー日時で統一するために、PHP 側で生成。date(‘c’) すれば ok。

挿入後に index へリダイレクトしてもよいのだが、ここでは確認のために insert をそのまま表示。

■View

<h2>Test list</h2>
<?php 
	echo &quot;new id: $ID <br />&quot;;
?>

<table>
	<tr>
		<td>ID</td>
		<td>Name</td>
		<td>Age</td>
		<td>UpdateDate</td>
	</tr>

<?php foreach($Test as $item) : ?>
	<tr>
		<td><?php echo $item['Test']['ID'] ?></td>
		<td><?php echo $item['Test']['Name'] ?></td>
		<td><?php echo $item['Test']['Age'] ?></td>
		<td><?php echo $item['Test']['UpdateDate'] ?></td>
	</tr>
<?php endforeach ; ?>

■結果

挿入した行が表示される。

※この結果は GMT が間違っているので、後で直す。

カテゴリー: CakePHP | [CakePHP] 新しい行を挿入する はコメントを受け付けていません

[CakePHP] 検索IDをURLに埋め込んで検索させる

findAllById を使って、検索して取得するパターンだが、/Store?id=10 ではなくて、/Store/10 のように検索できるようにする。

■Model

find 系を使うので変更なし

■Controller

/Store/10 でアクセスできるようにコントローラを作成…したいのだが、できないので、/Store/index/10 でアクセスする

in Controller/StoreController.php

class StoreController extends AppController {
	public function index($id=null) {
		$group = null;
		if (isset($this->params['url']['id'])) $id = $this->params['url']['id'];
		if (isset($this->params['url']['group'])) $group = $this->params['url']['group'];

		if ( $id != null ) {
			$this->set('Store',$this->Store->findAllById($id));
		} else if ( $group != null ) {
			$this->set('Store',$this->Store->findAllByAreagroupid($group));
		} else {
			$this->set('Store', $this->Store->find('all',array('limit'=>'100')));
		}
	}
}

index メソッドの引き数に $id を追加するだけ。
指定されない場合にも対応できるように、デフォルトの値を null にしておく。

/Store/10 でアクセスしようとすると、「10 という名前のメソッドがない」というエラーが出るので、/Store/index/10 でアクセスをする。この場合、/Store/select/10 のようにメソッド名を変えるほうがベターかと。
今回は、Web API なので、/Store?id=10 形式でアクセスするようにしよう。

■View

戻り値は配列になるので、foreach のところはそのままで。

■結果

指定したIDが表示される。

カテゴリー: CakePHP | [CakePHP] 検索IDをURLに埋め込んで検索させる はコメントを受け付けていません

[CakePHP] 指定したグループを検索して表示する

findAllByAreaGroupId を使って、検索して取得するパターン

■Model

find 系を使うので特に変更なし

■Controller

/Store?group=10 でアクセスできるようにコントローラを作成

in Controller/StoreController.php

class StoreController extends AppController {
	public function index() {
		$id = null;
		$group = null;
		if (isset($this->params['url']['id'])) $id = $this->params['url']['id'];
		if (isset($this->params['url']['group'])) $group = $this->params['url']['group'];

		if ( $id != null ) {
			$this->set('Store',$this->Store->findAllById($id));
		} else if ( $group != null ) {
			$this->set('Store',$this->Store->findAllByAreagroupid($group));
		} else {
			$this->set('Store', $this->Store->find('all',array('limit'=>'100')));
		}
	}
}

パラメータで取得するので、$this->params[‘url’][‘group’] を使う。isset チェックを忘れずに。
PK ではないので、数百行返される可能性もあるので、limit をつけたほうがベターかも。

findAllByAreagroupid のところは、findAllByAreaGroupId のように書きたいところだが、このように書くと「area_group_id」のように展開されてしまうので「Areagroupid」のようにすべて小文字で書く。

limit を使おうとすると、conditions を指定しないとだめで、

$this->set('Store',
	$this->Store->find('all',
		array(
			'limit' => 10,
			'conditions' => array('AreaGroupId'=>$group))));

な風に長くなる。なんらかのヘルパー関数が必要かも…って慣れれば大丈夫?

■View

戻り値は配列になるので、foreach のところはそのままで。

■結果

指定したグループの最初の10件が表示されている(conditions を使ったパターン)。

カテゴリー: CakePHP | [CakePHP] 指定したグループを検索して表示する はコメントを受け付けていません

[CakePHP] 指定したIDの行だけ表示する

findById とか findAllById を使って PK の id を使って取得するパターン

■Model

find 系を使うので特に問題なし

■Controller

/Store?id=10 でアクセスできるようにコントローラを作成

in Controller/StoreController.php

class StoreController extends AppController {
	public function index() {
		$id = null;
		if (isset($this->params['url']['id'])) $id = $this->params['url']['id'];

		if ( $id != null ) {
			$this->set('Store',$this->Store->findAllById($id));
		} else {
			$this->set('Store', $this->Store->find('all',array('limit'=>'100')));
		}
	}
}

パラメータの部分は、$this->params[‘url’][‘id’] のように取得できる。
ただし、?id=10 の部分が指定されていない場合は、エラーになるので、isset でチェック。

findAllById を使っているのは、配列を返したいから。findById にすると単体になるんだけど、View の foreach を変更する必要があり「面倒なので」、1件の場合も配列にするため。

■View

findAllById を使っているので、foreach のところは書き換えずに済む。

■結果

指定した ID の 1件だけが表示される。

カテゴリー: CakePHP | [CakePHP] 指定したIDの行だけ表示する はコメントを受け付けていません

[CakePHP] 最初のひな形を作る

Store というテーブルがあるとして、

■Model

テーブル名を複数形にしたくない(既存のテーブル名を変えたくない)ので、$useTable で指定

in Model/Store.php

class Store extends AppModel
{
	var $useTable = 'Store';	// 対象テーブル名を指定
}

find 系の関数だけを使うのであれば、最初は空で ok 。

■Controller

/Store でアクセスできるようにコントローラを作成

in Controller/StoreController.php

class StoreController extends AppController {
	public function index() {
		$this->set('Store', $this->Store->find('all',array('limit'=>'100')));
	}
}

全件検索すると1万行ぐらいあるので、最初の100行で打ち切り。find でリミットを指定できるのだけど、ここは「安全」にコーディングするために、find100 とかいうメソッドでアクセスしたほうがよいかも。

class StoreController extends AppController {
	public function index() {
		$this->set('Store', $this->Store->find100());
	}
}

この場合は、Model/Store.php に find100 メソッドを追加する。

class Store extends AppModel
{
	var $useTable = 'Store';
	
	function find100()
	{
		$sql = <<< HERE
SELECT
	ID, AreaGroupID, Name
FROM Store
limit 0,100
HERE;
		return $this->query($sql);
	}
}

こうやって SQL 文を直書きする。

■View

Controller で設定 $this->set(‘Store’,…) したところが使えるので、そのまま foreach でループさせる。

in View/Store/Index.ctp

store index の表示
<h2>Store list</h2>
<table>
	<tr>
		<td>id</td>
		<td>areagroupid</td>
		<td>name</td>
	</tr>

<!--
<?php print_r( $Store ); ?>
-->

<?php foreach($Store as $item) : ?>
	<tr>
		<td><?php echo $item['Store']['ID'] ?></td>
		<td><?php echo $item['Store']['AreaGroupID'] ?></td>
		<td><?php echo $item['Store']['Name'] ?></td>
	</tr>
<?php endforeach ; ?>

■結果

先頭の 100 件だけ表示する。

カテゴリー: CakePHP | [CakePHP] 最初のひな形を作る はコメントを受け付けていません

[CakePHP] CakePHP 再々入門

とある業務のフレームワーク選定として、
・独自MVC
・CakePHP
・ActiveRecord
を考えていたのですが、ひとまず、CakePHP で試験的に実装をしてみることに「決断」。まあ、試験的なので、業務として使えるパーツを集めるということで、

  • CRUD をひと通り
  • Web API として URL からパラメータを取得
  • 複雑なクエリ
  • トランザクションの制御
  • フォーム認証関係(ACL)、セッション情報

をがつがつと試していきます。

http://cakephp.jp/ の 2.3.4 を使ってチェック。

CakePHP 再入門(1) – Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1711

からの一連の記事は、SELECT 関係しか調べていないので、ちょっと今回の業務ロジックには足りない…ということでスピードをあげて調べ直し。システム構成としては、クライアント側に C# で Windows アプリ、サーバーに PHP+MySQL のレンタルサーバーという構成で、おそらく平均的な C/S 構成を作ったときに「ランニングコスト&保守を含めて」コストパフォーマンスが良いパターンかと。最近は仮想サーバーにしても IIS で安くするものですが、「プログラマ単価」的に PHP が安いとか、クライアントプログラミング的に C# が融通が利くとか、そのパターンです。まあ、今回は「プログラマ単価」は関係ないのですが、Linux サーバーに PHP で Web API を作って貰えるパターンがあると流用できると思います。

# 4年ぐらい前だったら、Silverlight+PHP のパターンを考えるのですが、Windows 8 を見ると、ちょっとね…という具合なので、WPF(C#)+PHP の組み合わせ。

C/S 通信には、SOAP, Web API(REST), XML-RPC というパターンがあります。Wordpress との連携を考えると、XML-RPC のほうがベターなのですが、C# での XML-RPC のサポートがいまいちなのと(xml-rpc.net しかない)、生 PHP でアクセスする場合には、そのメリットが少なくなってしまうので、REST で。C# 寄りにして SOAP を使う方法もあるのですが、今回は PHP に負担を掛けない方法で。

■目次

そんな訳で、以下は CakePHP 再々入門の目次(場所の予約)

カテゴリー: CakePHP | [CakePHP] CakePHP 再々入門 はコメントを受け付けていません

[C#] Log4netを使うサンプル

ざっと書き下しておきます。

Apache log4net: Home
http://logging.apache.org/log4net/
Log4NET – TSCWiki
http://www.tokyosoft.co.jp/tscwiki/index.php/Log4NET
【C#】【VS2010】Visual Studio 2010で Log4net を使う – プログラム の個人的なメモ – Yahoo!ブログ
http://blogs.yahoo.co.jp/dk521123/20264938.html

■環境構築

Visual Studio 2012 と 64bit という環境なので、これで。

Download Apache log4net
http://logging.apache.org/log4net/download_log4net.cgi
から、log4net-1.2.11-src.zip のほうをダウンロードして、展開。
D:toolslog4net-1.2.11 へコピー。
D:toolslog4net-1.2.11srclog4net.vs2010.sln を開く。

対象のフレームワークを「.NET Framework 4.5」に変更

image

ビルドをすると

D:toolslog4net-1.2.11buildbinnet2.0debuglog4net.dll

に作成される。

■ログ出力のサンプル

適当なプロジェクトを作って、参照設定で D:toolslog4net-1.2.11buildbinnet2.0debuglog4net.dll を追加。

image

app.config を開いて、Log4NET – TSCWiki にある「プロジェクトの組み込み」の部分をそのまま組み入れ。
作成するログファイル名だけを

<param name=&quot;File&quot; value=&quot;D:toolslog4net-1.2.11outlog-file.txt&quot; />

のように書き換えておく。

出力設定自体は、

    <root>
      <!-- ここではINFOログのレベルを指定 -->
      <level value=&quot;DEBUG&quot; />
      <!-- 下記の何れかを使用
      <level value=&quot;OFF &quot; />
      <level value=&quot;FATAL&quot; />
      <level value=&quot;ERROR&quot; />
      <level value=&quot;WARN&quot; />
      <level value=&quot;INFO&quot; />
      <level value=&quot;ALL&quot; />
      -->
      <!-- どのログ出力先を使用するか -->
      <appender-ref ref=&quot;LogToFile&quot; />
      <appender-ref ref=&quot;DayRollingLogToFile&quot; />
      <!-- 複数指定可能 -->
    </root>

のように、通常のログ「LogToFile」と、日付でローリング「DayRollingLogToFile」で出力。

ソリューションエクスプローラーで「AssemblyInfo.cs」を開いて、

image

[assembly: log4net.Config.XmlConfigurator(Watch = true)] の記述を追加する。

// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
// アセンブリに関連付けられている情報を変更するには、
// これらの属性値を変更してください。
[assembly: AssemblyTitle("SampleLog4Net")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SampleLog4Net")]
[assembly: AssemblyCopyright("Copyright c  2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: log4net.Config.XmlConfigurator(Watch = true)]

適当に画面を作って、

image

namespace SampleLog4Net
{
	public partial class Form1 : Form
	{
		private static readonly log4net.ILog log =
		log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

		public Form1()
		{
			InitializeComponent();
		}

		private void button1_Click(object sender, EventArgs e)
		{
			log.Info("INFOで出力");
		}
	}
}

な風に書く。log4net.ILog log なところをプライベートフィールドで。

■サンプルを実行してみる

こんな風に時刻とクラス名、ログが出ます。

image

出力自体は、app.config に書いた

      <level value=&quot;ERROR&quot; />
      <level value=&quot;WARN&quot; />
      <level value=&quot;INFO&quot; />
      <level value=&quot;DEBUG&quot; />

のところで切り替えます。通常のデバッグ時には、DEBUG を有効にしておいて、リリース時には、ERROR だけを有効にするとか規約で決めればよいでしょう。

■動的にログ出力レベルを変更する

[assembly: log4net.Config.XmlConfigurator(Watch = true)]

を設定することで、実行時にも *.config を変えることができる…という記事があるのですが、.NET 4.5 だと動きませんでした。なので、直接ソースの中から操作します。

var rep = log4net.LogManager.GetRepository() as log4net.Repository.Hierarchy.Hierarchy;
rep.Threshold = log4net.Core.Level.Info;

ソースを見ると、出力レベルの判断は、Hierarchy プロパティで判断しているのでこれを書き換えます。この例では、Info 以上の出力(DEBUG出力は出ない)設定にしています。これはグローバル変数なので、全体に影響しますね。

■出力時のファイル名と行番号を出力する

こんな感じで拡張メソッドを作っておけばOK。

VS2012 の場合は、CallerMemberName 属性を使って生成、VS2010 の場合は StackFrame クラスを使って生成します。ここでは、Debug しか作っていませんが、同じように Info とかを作ればよいし、適当にフォーマットすれば使えるかと。

public static class ILogEx
{
    /// <summary>
    /// VS2012の場合
    /// </summary>
    /// <param name=&quot;log&quot;></param>
    /// <param name=&quot;msg&quot;></param>
    /// <param name=&quot;name&quot;></param>
    /// <param name=&quot;path&quot;></param>
    /// <param name=&quot;num&quot;></param>
    public static void DebugL(this log4net.ILog log, string msg,
        [CallerMemberName] string name = &quot;&quot;,
        [CallerFilePath] string path = &quot;&quot;,
        [CallerLineNumber] int num = 0)
    {
        log.Debug(string.Format(&quot;{0} {1} {2} {3}&quot;, name, path, num, msg));
    }
    /// <summary>
    /// VS2010の場合
    /// </summary>
    /// <param name=&quot;log&quot;></param>
    /// <param name=&quot;msg&quot;></param>
    public static void DebugN(this log4net.ILog log, string msg)
    {
        var sf = new StackFrame(1, true);
        string name = sf.GetMethod().ToString();
        string path = sf.GetFileName();
        int num = sf.GetFileLineNumber();
        log.Debug(string.Format(&quot;{0} {1} {2} {3}&quot;, name, path, num, msg));
    }
}

~ 追記
と思ったものの、もともと log4net にはログの形式で %location を使えばファイル名などが出力できるので、これで良いかと。
連載:VBで実践! 外部コンポーネント活用術:オープンソースのロギング・サービス「log4net」を使う (3/4) – @IT あたりを参考に

        <param name=&quot;ConversionPattern&quot; value=&quot;%date [%thread] %-5level %logger - %location %message%newline&quot; />

■サンプル

サンプルコードはこちら
SampleLog4Net-v0.1-src.zip をダウンロードしてください。

カテゴリー: C# | [C#] Log4netを使うサンプル はコメントを受け付けていません

[WinRT] Surfaceのフォトで選択した画像を、デスクトップに送る

[WinRT] デスクトップで閲覧しているURLをSurface RTに送る – Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/4718
[WinRT] Surface RT で閲覧しているURLをデスクトップに送る – Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/4723

送るシリーズの第3弾…って訳ではないですが、メモ的に。

Surface RTからURLをデスクトップに送る仕組みを応用して、画像ファイルを送ります。ここではWindows RT標準装備の「フォト」で共有した場合。他にも SkyDrive をやってみたいのですが、これは方式が違うのでまた別途ソースを書きおこします。内部動作が違っても、表面的な UI は同じほうがいいですからね。

■Surface から共有させる

マニフェストの宣言で「サポートされるファイルの種類」を増やしておきます。ここでは、画像ファイルしか扱わないので、まめに「.jpeg」とか書いていきます。「すべてのファイルの種類をサポートする」にチェックを入れてもokなんですが、なんかの理由付けがないと審査で落ちそうな感じがするし、まあ用途としては制限させたほうがいいので。

共有しているところの、ShareTargetPage.xaml.cs を書き換えます。
フォトからは StandardDataFormats.StorageItems を使って送られてくるので、これで受け取り。複数のファイルが送れるのですが、今回は最初の1枚だけを対象に。

public async void Activate(ShareTargetActivatedEventArgs args)
{
    this._shareOperation = args.ShareOperation;
...略
    if (this._shareOperation.Data.Contains(StandardDataFormats.StorageItems))
    {
        Debug.WriteLine("contain StorageItems");
        var files = await this._shareOperation.Data.GetStorageItemsAsync();
        foreach (StorageFile f in files)
        {
            Debug.WriteLine("name: {0}", f.Path);
        }
    }
}

画像転送は、HTTPのPUTを使ってBASE64で送ります。このあたりは先行きXMLシリアライズを使うつもりなので、ざっと書き下し。バイト配列とのコンバートは、慣れると…とはいえ慣れても面倒なのですが、この通りに各と動きます。

    private async void ShareButton_Click(object sender, RoutedEventArgs e)
    {
        this.DefaultViewModel["Sharing"] = true;
        this._shareOperation.ReportStarted();
        // TODO: this._shareOperation.Data を使用して共有シナリオに適した
        //       作業を実行します。通常は、カスタム ユーザー インターフェイス要素を介して
        //       このページに追加されたカスタム ユーザー インターフェイス要素を介して
        //       this.DefaultViewModel["Comment"]
...略
        if (this._shareOperation.Data.Contains(StandardDataFormats.StorageItems))
        {
            var files = await this._shareOperation.Data.GetStorageItemsAsync();
            var file = files[0] as StorageFile;
            // 画像データを転送
            var stream = await file.OpenReadAsync();
            using (var mem = new MemoryStream())
            {
                var rs = stream.AsStreamForRead();
                await rs.CopyToAsync(mem);
                byte[] data = mem.ToArray();
                string url = "http://mars:8090/PutImage";
                HttpClient cl = new HttpClient();
                string b64 = System.Convert.ToBase64String(data);
                StringContent cont = new StringContent(b64);
                var res = await cl.PutAsync( url, cont );
            }
        }
        this._shareOperation.ReportCompleted();
    }
}

c# – WinRT image handling – Stack Overflow
http://stackoverflow.com/questions/10013799/winrt-image-handling
DataPackageView.GetStorageItemsAsync | getStorageItemsAsync Method (Windows)
http://msdn.microsoft.com/ja-jp/library/windows/apps/windows.applicationmodel.datatransfer.datapackageview.getstorageitemsasync

このあたりを読んで、まめに実装するがよいかと。

■デスクトップで画像を受ける

Listener のタスクが長くなってきたので、別関数にくくりだし。Task の中からは GUI コントロールを直接さわれないので、Invoke を使います。いろいろやってこれが簡単な方法かと。
画像ファイルは、BASE64 からバイナリ配列に直して Bitmap を作るという手順です。このあたりの流れは、適度に隠蔽化してしまうと楽になると思います。

private void Form1_Load(object sender, EventArgs e)
{
    listener = new HttpListener();
    listener.Prefixes.Add(&quot;http://*:8090/&quot;);
    listener.Start();

    // 受信待ちタスク
    Task tsk = new Task( ListenLoop );
    tsk.Start();
}

async void ListenLoop()
{
    while (true)
    {
        var cont = listener.GetContext();
        var req = cont.Request;
        var res = cont.Response;
        if (req.RawUrl.StartsWith(&quot;/GetUrl&quot;))
        {
            Debug.WriteLine(string.Format(&quot;req {0}&quot;, req.RawUrl));
            var tw = new StreamWriter(res.OutputStream);
            tw.Write(_text);
            tw.Close();
            res.Close();
        }
        else if (req.RawUrl.StartsWith(&quot;/PutUrl&quot;) == true)
        {
            Debug.WriteLine(string.Format(&quot;req {0}&quot;, req.RawUrl));
            var text = req.RawUrl.Replace(&quot;/PutUrl/&quot;, &quot;&quot;);
            text = System.Uri.UnescapeDataString(text);
            // textBox1.Text = text;
            textBox1.Invoke((MethodInvoker)(() => textBox1.Text = text));
            var tw = new StreamWriter(res.OutputStream);
            tw.Write(&quot;OK&quot;);
            tw.Close();
            res.Close();
        }
        else if (req.RawUrl.StartsWith(&quot;/PutImage&quot;) == true)
        {
            // 画像をBASE64で受け取る
            Debug.WriteLine(string.Format(&quot;req {0}&quot;, req.RawUrl));
            var st = req.InputStream;
            var sr = new StreamReader(st);
            string b64 = await sr.ReadToEndAsync();
            byte[] data =  System.Convert.FromBase64String(b64);
            var mem = new MemoryStream(data);
            Bitmap bmp = new Bitmap(mem);
            // pictureBox1.Image = bmp;
            pictureBox1.Invoke((MethodInvoker)(() => pictureBox1.Image =bmp ));

            var tw = new StreamWriter(res.OutputStream);
            tw.Write(&quot;OK&quot;);
            tw.Close();
            res.Close();
        }
        else
        {
            var tw = new StreamWriter(res.OutputStream);
            tw.Write(&quot;ERROR&quot;);
            tw.Close();
            res.Close();
        }
    }
}

■動かしてみる

フォトから共有させて、

フォームアプリに転送するだけです。

そのままファイルに保存してもいいし、Clipboard.SetImage(pictureBox1.Image) な風にしてい、クリップボードに送るのもいいでしょう。

■サンプルソース

サンプルソースはこちら。
http://sdrv.ms/10mK1ch

カテゴリー: C#, WinRT | [WinRT] Surfaceのフォトで選択した画像を、デスクトップに送る はコメントを受け付けていません