Quantcast
Channel: かずきのBlog@hatena
Viewing all 1388 articles
Browse latest View live

JavaScript(node.js)で非同期処理を扱いたい(coモジュール使ってみたよ)

$
0
0

1つ前のエントリで async モジュール使ってみました。

blog.okazuki.jp

関数の配列を渡してやれば、よしなに実行してくれるいいやつでした。次は co です!

www.npmjs.com

async が古き良き仕組みの上に非同期処理をいい感じに扱うという観点のライブラリという雰囲気を感じましたが co は async/await は無いけど Promise とジェネレーターを使って非同期処理をいい感じに書こうという雰囲気です。

ジェネレーターについては MDN がいい感じにまとまってます。

developer.mozilla.org

co

以下のコマンドで導入でいます。

npm i co

co は非常にシンプルで co の引数にジェネレーター関数を渡すだけです。そして戻り値に対してthenを呼び出して正常に完了したときの処理とエラーが起きたときの処理を渡します。 なので、基本的にこうなります。

co(function*() { ... })
.then(function(result) {}, function(error) {});

ということで試しにやってみましょう。

var co = require('co');

function main() {
    co(function*() {var process1 = yield Promise.resolve(1);
        var process2 = yield Promise.resolve(2);
        var process3 = yield Promise.resolve(3);
        return[process1, process2, process3];
    }).then(function(result) {
        console.log(JSON.stringify(result));
    }, function(error){
        console.log('error: ', error.message);
    });
}

main();

実行すると以下のように表示されます。

[1,2,3]

試しに例外を発生させてみます。

var co = require('co');

function main() {
    co(function*() {var process1 = yield Promise.resolve(1);
        var process2 = yield Promise.resolve(2);
        var process3 = yield Promise.resolve(3);
        thrownew Error('oops!!');
        return[process1, process2, process3];
    }).then(function(result) {
        console.log(JSON.stringify(result));
    }, function(error){
        console.log('error: ', error.message);
    });
}

main();

実行するとエラーのほうのフローにわたってることが確認できます。

error:  oops!!

実際に非同期処理っぽいことをしてみましょう。setTimeoutをラップした関数を用意して重たい処理をエミュレートします。

var co = require('co');

function asyncProcess(processName) {returnnew Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(`${processName} completed at ${newDate().toISOString()}`);
        }, 1000);
    });
}function main() {
    co(function*() {var process1 = yield asyncProcess('process1');
        var process2 = yield asyncProcess('process2');
        var process3 = yield asyncProcess('process3')
        return[process1, process2, process3];
    }).then(function(result) {
        console.log(JSON.stringify(result));
    }, function(error){
        console.log('error: ', error.message);
    });
}

main();

実行するとこうなります。

["process1 completed at 2017-08-23T06:36:50.560Z","process2 completed at 2017-08-23T06:36:51.560Z","process3 completed at 2017-08-23T06:36:52.561Z"]

Promise の配列に対しても co の中で yield 出来たりします。

var co = require('co');

function asyncProcess(processName) {returnnew Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(`${processName} completed at ${newDate().toISOString()}`);
        }, 1000);
    });
}function main() {
    co(function*() {return yield [
            asyncProcess('process1'),
            asyncProcess('process2'),
            asyncProcess('process3'),
        ];
    }).then(function(result) {
        console.log(JSON.stringify(result));
    }, function(error){
        console.log('error: ', error.message);
    });
}

main();

この場合は当然ですがprocess1 - 3 はパラレルに走りますね。

["process1 completed at 2017-08-23T06:39:23.346Z","process2 completed at 2017-08-23T06:39:23.347Z","process3 completed at 2017-08-23T06:39:23.347Z"]

さらにプロパティが Promise を含むオブジェクトに対しても yield 出来ます。

var co = require('co');

function asyncProcess(processName) {returnnew Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(`${processName} completed at ${newDate().toISOString()}`);
        }, 1000);
    });
}function main() {
    co(function*() {return yield {
            process1: asyncProcess('process1'),
            process2: asyncProcess('process2'),
            process3: asyncProcess('process3'),
            other: 'other value',
        };
    }).then(function(result) {
        console.log(JSON.stringify(result));
    }, function(error){
        console.log('error: ', error.message);
    });
}

main();

実行するとこんな感じです。

{"process1":"process1 completed at 2017-08-23T06:41:23.638Z","process2":"process2 completed at 2017-08-23T06:41:23.638Z","process3":"process3 completed at 2017-08-23T06:41:23.638Z","other":"other value"}

この他にもジェネレータ関数を Promise を返す関数にしてくれる co.wrap(function*(arg) {}) というのがあります。

var co = require('co');

function asyncProcess(processName) {returnnew Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(`${processName} completed at ${newDate().toISOString()}`);
        }, 1000);
    });
}// (number, number): Promise<number> みたいなvar asyncDiv = co.wrap(function* (x, y) {returnnew Promise(function(resolve, reject) {
        setTimeout(function() {if (y == 0) {return reject('divide by zero');
            }
            resolve(x / y);
        }, 1000);
    });
});

function main() {
    asyncDiv(10, 2).then(function(answer) { 
        console.log('answer: ', answer); 
    }, function(error) { 
        console.log('error: ', error); 
    });
    asyncDiv(10, 0).then(function(answer) { 
        console.log('answer: ', answer); 
    }, function(error) { 
        console.log('error: ', error); 
    });
}

main();

実行するとこうなります。

answer:  5
error:  divide by zero

実際に使ってみよう

ということで async のほうでもやった azure-storage を使った例を co でもやってみます。 こんな雰囲気ですかね?

var co = require('co');
var uuid = require('uuid/v4');
var azure = require('azure-storage');

function main() {
    co(function*() {var tableService = azure.createTableService('<accont name>', '<key>');
        var entGen = azure.TableUtilities.entityGenerator;
        yield new Promise(function(resolve, reject) {
            tableService.createTableIfNotExists('cosample', function(error, result) {if (error) {return reject(error);
                }
                resolve(result);
            });
        });
        console.log('table created');
        var entity = {
            PartitionKey: entGen.String('sample'),
            RowKey: entGen.String(uuid()),
            Message: entGen.String('Hello world at ' + newDate().toISOString())
        };
        return yield new Promise(function(resolve, reject) {
            tableService.insertEntity('cosample', entity, function(error, result, response) {if (error) {return reject(error);
                }
                resolve([result, response]);
            });
        });
    }).then(function(result) {
        console.log('result is ', JSON.stringify(result[0]));
        console.log('response is ', JSON.stringify(result[1]));
    }, function(error) {
        console.log('error is ', JSON.stringify(error));
    });
}

main();

実行すると、こんな感じになりました。

table created
result is  {".metadata":{"etag":"W/\"datetime'2017-08-23T07%3A03%3A01.8221368Z'\""}}
response is  {"isSuccessful":true,"statusCode":204,"body":"","headers":{"cache-control":"no-cache","content-length":"0","etag":"W/\"datetime'2017-08-23T07%3A03%3A01.8221368Z'\"","location":"https://funcrelationacf6.table.core.windows.net/cosample(PartitionKey='sample',RowKey='bd89259e-442c-4f20-b2cb-6180d15fcfbd')","server":"Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0","x-ms-request-id":"07a43392-0002-0002-70dd-1b9917000000","x-ms-version":"2017-04-17","x-content-type-options":"nosniff","preference-applied":"return-no-content","dataserviceid":"https://funcrelationacf6.table.core.windows.net/cosample(PartitionKey='sample',RowKey='bd89259e-442c-4f20-b2cb-6180d15fcfbd')","date":"Wed, 23 Aug 2017 07:03:01 GMT","connection":"close"}}

azure-storage モジュールの API が Promise ベースじゃないので幸福度が上がりませんね。 なので、自前で Promise が戻り値になるような API にラップしてみました。 ついでに、ここにきてアロー関数も使ってみました。

var co = require('co');
var uuid = require('uuid/v4');
var azure = require('azure-storage');

class MyTableService {
    get tableService() {returnthis._tableService ||
            (this._tableService = azure.createTableService('funcrelationacf6', 'Fg3DQ0pfmJGMKrpdd1IcIOQ8brUsz2/BBTHlctx/RClAlkwJYZkW+6c5m1GnC7+cKYIXhkbxQ1CMJSNwRE2fbg=='));
    }

    createTableIfNotExists() {returnnew Promise((resolve, reject) => {this.tableService.createTableIfNotExists('cosample', (error, result, response) => {if (error) {return reject(error);
                }
                resolve({
                    result: result,
                    response: response
                });
            });
        });
    }

    insertEntity(entity) {var entGen = azure.TableUtilities.entityGenerator;
        returnnew Promise((resolve, reject) => {var azureStorageEntity = {
                PartitionKey: entGen.String(entity.PartitionKey),
                RowKey: entGen.String(entity.RowKey),
                Message: entGen.String(entity.Message)
            };
            this.tableService.insertEntity('cosample', azureStorageEntity, (error, result, response) => {if (error) {return reject(error);
                }
                resolve({
                    result: result,
                    response: response
                });
            });
        });
    }}function main() {
    co(function* () {try{var tableService = new MyTableService();
            yield tableService.createTableIfNotExists();
            console.log('table created');
            var entity = {
                PartitionKey: 'sample',
                RowKey: uuid(),
                Message: `Hello world at ${newDate().toISOString()}`
            };
            var r = yield tableService.insertEntity(entity);
            console.log(`result is ${JSON.stringify(r.result)}`);
            console.log(`response is ${JSON.stringify(r.response)}`);
        }catch (error) {
            console.log(`error: ${error.message}`);
        }});
}

main();

ラップするのは苦行だけど悪くないね。

ラップの自動化

世の中には怠け者(誉め言葉)がいるみたいで、このラッピングを自動でしてくれるようなライブラリ作者がいるみたいです。 bluebird モジュールがそれになるみたいです。早速入れてみましょう。

npm i bluebird

これを使うと promisifyAll でさくっと Promise 化してくれます。

var co = require('co');
var uuid = require('uuid/v4');
var azure = require('azure-storage');
var Promise = require('bluebird');

function main() {
    co(function* () {try{var entGen = azure.TableUtilities.entityGenerator;
            var tableService = Promise.promisifyAll(azure.createTableService('funcrelationacf6', 'Fg3DQ0pfmJGMKrpdd1IcIOQ8brUsz2/BBTHlctx/RClAlkwJYZkW+6c5m1GnC7+cKYIXhkbxQ1CMJSNwRE2fbg=='));
            yield tableService.createTableIfNotExistsAsync('bluebirdsample');
            console.log('table created');
            var entity = {
                PartitionKey: entGen.String('sample'),
                RowKey: entGen.String(uuid()),
                Message: entGen.String(`Hello world at ${newDate().toISOString()}`)
            };
            var r = yield tableService.insertEntityAsync('bluebirdsample', entity);
            console.log(`result is ${JSON.stringify(r)}`);
        }catch (error) {
            console.log(`error: ${error.message}`);
        }});
}

main();

ただ、azure-storage のコールバックは(error, result, response)という3つの引数を受け取るものが多い印象なのですが、こういうインターフェースだと response がロストしてるように感じます。う~む。

preview 版を使う??

まぁ、こういう不満は皆持ってるみたいで azure-storage 2.0.0-preview で Promise 対応が入ってます。

github.com

github.com

ただ、4月に preview が出てから4か月たってるけど正式リリースはまだみたいですね? そして、これは azure という超巨大モジュールの preview には適用されてるけど azure-storage モジュールには適用されてなさそう?

次は、これを調べてみたいと思います。今回は co がメインだしね!


Azure Redis Cache を使ってみた

$
0
0

Azure Redis Cache

そういえばコレ触ってみたいと思ってたんですよね。

Redis Cache

作り方

ポータルからぽちぽちでいけました。ハマりどころはないでしょう多分。 今回は検証用なのでプランはBASICにしました。本番ではStandard以上を使うことになるんでしょうね。

node.js から使ってみよう

最近、不本意?ながら JavaScript(node.jsです。というかnode.jsまた分裂したけど大丈夫なんですかね…) やってるので、試しに node.js でやってみようと思います。

手順は、以下のサイトを参考にしてます。

Node.js で Azure Redis Cache を使用する方法

ということで

npm install redis

で node_redis を入れます。

接続に必要な情報の取得

ポータルから、Redis Cacheのアクセスキーからプライマリキーとプロパティからホスト名とSSLポートをメモっておきます。 node的には、環境変数あたりにセットしておいて process.env.*** からアクセスするのがコード内に埋め込まなくて済むのでいいんでしょうね。私はFunction Appを使うので、アプリケーション設定に埋め込んでおきます。

情報を書き込んでみる

では、先ほどの情報を元にアクセスしてみましょう。 私はFunction Appで試してみてるので適当に TimerTrigger の関数を作って関数が動いた時間の情報をキャッシュに書き込むようにしました。

const redis = require('redis');

module.exports = function (context, myTimer) {var client = redis.createClient(process.env.REDIS_PORT, process.env.REDIS_HOSTNAME, {
        auth_pass: process.env.REDIS_KEY,
        tls: {
            servername: process.env.REDIS_HOSTNAME
        }});

    client.set('timestamp', newDate().toISOString(), (err, reply) => {
        console.log('updated timestamp');
        client.quit();
        context.done();
    });
};

こいつを10秒間隔くらいで動かしてみるように function.json を構成します。

{"disabled": false,
  "bindings": [{"name": "myTimer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "*/10 * * * * *"}]}

func host start すると以下のようなログが延々と出続けるのでエラーなく動いてるっぽいですね。 (タイマーが動き出すまでに少し時間がかかります)

Function started (Id=44896ed1-2114-4697-950b-1514548e00ed)
Executing 'Functions.UpdateCache' (Reason='Timer fired at 2017-08-29T10:27:10.0229356+09:00', Id=44896ed1-2114-4697-950b-1514548e00ed)
updated timestamp
Function completed (Success, Id=44896ed1-2114-4697-950b-1514548e00ed, Duration=2342ms)
Executed 'Functions.UpdateCache' (Succeeded, Id=44896ed1-2114-4697-950b-1514548e00ed)

因みに、Redisの管理ツールは何を使うのがデファクトか知らないのですが Redis Desktop Managerを使って覗いてみたらちゃんと値が入っていることが確認できました。

参考: Redis Desktop Manager を使って Azure Redis Cache の管理を行ってみた

SSLでつなぐ方法はどうやるんだろう?

情報を読み込んでみる

ということで書けてることが確認できたので次は読んでみます。

redis の client を作って get するだけですね。

const redis = require('redis');

module.exports = function (context, req) {var client = redis.createClient(process.env.REDIS_PORT, process.env.REDIS_HOSTNAME, {
        auth_pass: process.env.REDIS_KEY,
        tls: {
            servername: process.env.REDIS_HOSTNAME
        }});

    client.get('timestamp', (err, reply) => {
        context.res = {status: 200,
            body: reply
        };
        client.quit();
        context.done();
    });
};

適当に実行して URL をたたくと以下のような結果が返ってきました。

"2017-08-29T01:39:10.016Z"

暫く時間をあけて URL をたたくとちゃんと更新された値が取れてるみたいです。

"2017-08-29T01:41:10.010Z"

まとめ

キャッシュのゲットとセットだけなら簡単でした。 なんか他にもいろんな機能があるみたいなので見てみたいと思います。

Reactで開発するときに使えるかっこよさそうなコンポーネント ANT DESIGN

$
0
0

知らなかったんですが ANT DESIGN というのがあるんですね。

ant.design

React のコンポーネントも実装されてていい感じかも。

今までは、Material-UIを何かあったら使おうと思ってたけど、今度何かやるときには ANT DESIGN も評価してみてもいいかもというメモでした。

中国の方が作ってるのかな?GitHubを見る限り。

React Native でプロジェクトを作って実機で画面確認まで

$
0
0

最近ご無沙汰してたReact Nativeですが、久しぶりにインストールしてみました!

node.js を入れる

これがないと始まらないですよね。最近のJavaScript界隈。

Node.js

create-react-native-app を入れる

npm i -g create-react-native-app

これでプロジェクトのひな形作ってくれるコマンドが入ります。

expo を入れる

なんか、気軽にアプリをデバッグ実行するためのものみたいですね。ありがたや。

expo.io

開発母艦にアプリを入れたらデバッグ用端末にもストアから expo のクライアントアプリを入れます。

yarn を入れる(入れなくてもいいかもしれない?)

2017/09/02 時点では npm 5 では create-react-native-app がうまく動きません。なのでかわりにyarnを入れます。

npm i -g yarn

プロジェクトを作る

ではさくっとプロジェクトを作ってみましょう。

create-react-native-app hello-react-native

のようにコマンドのあとにプロジェクト名をつけてうつとサクッと作ってくれます。

yarn start

これで expo 使っていつでもアクセスできるようになります。

Expo XDEを立ち上げるとこんな感じに表示されます。

f:id:okazuki:20170902205111p:plain

Shareを押すとQRコードとか出てくる。これをモバイルのexpoアプリから読み込めば動き始めます。 ただ、注意点としては同じネットワークにいないとダメってことですね。

あと、このQRコードのIPアドレスはどういうルールでとってこられてるのかわからないのですが、ローカルのネットワークにつながってるやつじゃないのが出てくることもあるみたいです。Hyper-Vのインターナルな奴?のネットワークのIP拾ってきてました。

なので ipconfig /all あたりでIPアドレスを確認して直打ちしました。

実行結果

こんな感じに画面が出ればとりあえず開発環境は整いました!あとはやっていくだけや!

f:id:okazuki:20170902210810p:plain

Xamarin.Forms のプロジェクトでフォルダパスが長すぎてエラーになってしまうときの対処法

$
0
0

最近 Xamarin.Forms のプロジェクトを新規作成したあとにビルドエラーになるというかプロジェクト作成すらままならないという状況に出会うことが増えました。

まぁ本質的にはパスの長さの問題なので別にXamarinに限った話ではないのですが辛いところですね。 (Windows 10の最新バージョンではグループポリシーエディターでパス長の制限解除できるけど、それがVS2017で有効になるのかは知らない)

とまぁTwitterで愚痴ったところ、私以外にも起きてるという人がいるということなので共有しておきます。

なんか色々なパターンがあるのですが、例えばちょっと長めのプロジェクト名をつけてデフォルトのC:\Users\<username>\Documents\Visual Studio 2017\Projects以下に作成しようとするとこんな感じのメッセージが出たりします。

---------------------------
Microsoft Visual Studio
---------------------------
サブ プロジェクト 'LongLongPathApplicationSample.Client.UWP' の作成中にエラーが発生しました。ファイル名または拡張子が長すぎます。 (HRESULT からの例外:0x800700CE)
---------------------------
OK   
---------------------------

対処法1

とりあえず短いパスのフォルダに作るようにする。

まぁパスが長いのが問題ですからね…。でもデフォルトのパスを出来れば使いたい。

対処法2

ということなので以下のようなコマンドをうってみました。

subst d: C:\Users\kaota\Documents

これでDドライブがドキュメントフォルダになります。そして、Visual Studio 2017でDドライブの下のVisual Studio 2017\Projectsにプロジェクトを作ると無事成功!!

とはなりませんでした。

重大度レベル   コード   説明  プロジェクト  ファイル    行 抑制状態
エラー       "ResolveLibraryProjectImports" タスクが予期せずに失敗しました。
System.IO.PathTooLongException: 指定されたパス、ファイル名、またはその両方が長すぎます。完全限定型名は 260 文字未満で指定し、ディレクトリ名は 248 未満で指定してください。
   場所 System.IO.Directory.InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, Boolean checkHost)
   場所 System.IO.Directory.InternalCreateDirectoryHelper(String path, Boolean checkHost)
   場所 System.IO.Directory.CreateDirectory(String path)
   場所 Xamarin.Android.Tasks.ResolveLibraryProjectImports.Extract(DirectoryAssemblyResolver res, ICollection`1 jars, ICollection`1 resolvedResourceDirectories, ICollection`1 resolvedAssetDirectories, ICollection`1 resolvedEnvironments)
   場所 Xamarin.Android.Tasks.ResolveLibraryProjectImports.Execute()
   場所 Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
   場所 Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__26.MoveNext()   LongLongPathApplicationSample.Client.Android            

まだ長いのか…。ならばこうだ

subst /D d:
subst d: "C:\Users\kaota\Documents\Visual Studio 2017\Projects"

開きなおして実行すると、やったね!!

f:id:okazuki:20170908175733p:plain

まとめ

緩募。もっといい方法とかあれば。

Emotion API でビデオ分析機能を呼ぶ方法

$
0
0

画像を投げ込むパターンはよくあったのですがビデオの情報がなかなか見つからなかったのでメモ。

var emotionClient = new EmotionServiceClient("key");

var result = await emotionClient.RecognizeInVideoAsync(<ここにstreamを渡す>);
// 分析をポーリングする
VideoOperationResult operationResult;
while (true)
{
    operationResult = await emotionClient.GetOperationResultAsync(result);
    if (operationResult.Status == VideoOperationStatus.Succeeded || operationResult.Status == VideoOperationS
    {
        break;
    }

    await Task.Delay(15000);
}

// 成否を確認if (operationResult.Status == VideoOperationStatus.Failed)
{
    task.Message = $"感情分析に失敗しました: {operationResult.Message}";
    return;
}

// 結果をVideoOperationInfoResult<VideoAggregateRecoginitionResult>にして色々やる
var r = (VideoOperationInfoResult<VideoAggregateRecognitionResult>)operationResult;
// Fragmentsで認識結果を確認できる
r.ProcessingResult.Fragments...

blog の引っ越しを考えてるのだけど何処がいいんだろう?

$
0
0

さて、私が blog を書き始めた頃はどの blog サービスがデファクトだ!!というのは意識していませんでした。

わんくま同盟というコミュニティで blog を作ってもらえるということで申し込んで作ってもらったのが私の blog 人生のはじまりになります。その後、hatena ダイアリーに移動して、hatena blog といった感じで流れて来ました。ということで基本的に世間一般で言われる blog サービスというものは hatena さんが運営するもの以外触ったことのがありません。

Hatena blog(現在の巣)

可もなく不可もなく。触っていて不満を感じることはありません。 最近のアップデートで何故か自分の記事に対して bookmark をはるためのショートカットが blog 記事投稿後に表示されたりと謎の進化を遂げている点と、https 対応がまだいつリリースされるのかわからないという点が気になります。

個人的に以下の点が気に入っています。

  • markdown が使える
  • なんだかんだいって規模はあるよね
  • プログラムのコードのシンタックスハイライトもあるので good
  • pro になると(課金すると)独自ドメインを割り当てたりできる
  • blog 書きに専念できる(プラットフォームのアップデートとか気にしなくてもいい)

気になってる点は以下です

  • 引っ越しって簡単にできないんじゃなかろうか
    • 画像まで含めて綺麗に引っ越しできるの?
  • https …
  • 最近のイケてるプラットフォームだという認識はない
  • 日本に閉じてる
    • あっ、私英語できないので別に困ってはないんですけどね

最近、使用者目線から見てイケイケな雰囲気を感じないのが気になってます。世間的にはそういうことはないのかな?気になる。

Qiita

コンセプトがそもそもプログラマのための技術情報共有サービスという点という時点で心強いです。個人的な印象でしかないですが、最近の若い人は、だいたい Qiita なのでは?と思ってしまうくらい色々なところで見かけます。

Qiita

記事の記述も markdown でいけるのでとてもいい感じだと思います。個人的に今プログラミング関連の blog をはじめるのだったらここなのだろうか?という雰囲気を感じてます。

シンタックスハイライトもあっていい感じですね。

medium

Qiita の他にもう1つ気になってるのが medium です。

medium

最近、ちらほら medium で blog 書いてる人を見かけます。なので気になったから調べて見たら、こういう記事も見つけてしまって「ん?」となっているところではあります。

Mediumがビジネス立て直しのため、日本でのオペレーションを終了

ただ、最近 Windows 10 Mobile を個人的にメイン機として使うのをやめようと思って iOS, Android に引っ越ししつつ iPad Pro(第1世代の小さいほう)を手に入れて何を思ったか開発環境を構築しようとして、色々頑張った(諦めました)ときにテキストエディタを探し回ったのですが Qiita や hatena は対応してるようなエディタは有名どころであまり見ない気がしたのですが medium はチラホラ見かけたような気がします。結局最終的に iA Writer を買ったのですが、こちらは medium への投稿機能があるんですよね。

ワールドワイドというか英語圏のサービスは、こういう世界的に有名なアプリでサポートされてたりするあたりが強いなぁと思いました。

ただ、組み込みでのシンタックスハイライトは対応していないみたいなので、そこらへんちょっとがっかりしました。gist とかの URL 貼ればいい感じにしてくれるみたいですが、餅は餅屋という発想なんですかね。

How to display code blocks in Medium

自前の wordpress

個人的には一番無いなと思ってる選択肢です。 ただ、ガチでセルフブランディングを考えてて他の人と差別化した blog サイトを作ることが目的の人には合致するんだろうなと思います。

私が blog を書く目的は

  • 自分用の備忘録
  • 他の人が同じことで困ってたときの助けになればいいな

というくらいなので、あまり blog のメンテナンス作業に時間を割かれるのは好きじゃ無いとうことなので wordpress を自分で建てたときに発生する wordpress のバージョンアップだとかサーバーのお守りだとか(Microsoft Azure App Serviceに立てればサーバーのお守りはしなくていいけど)そういう類のものに時間を割きたく無い!!

まとめ

結局どこに行くのがいいんだろうか?? という記事を試しに hatena blog, qiita, medium に投稿して使い勝手を試してみようと思ったのですが qiita はポエムを置く場所ではないということを思い出したので hatena blog と medium に書いて置くのでした。

Xamarin.Forms のプロジェクトでフォルダパスが長すぎてエラーになってしまうときの対処法 その2

$
0
0

ちょっと前にこんな記事を書きました。

blog.okazuki.jp

コメントでジャンクション使ってみたらどうですか?と言われたので調べてみたら mklinkコマンドってので作れるみたいですね。今回は、mklinkコマンドを使って適当にシンボリックリンクを張る方法で試してみたいと思います。

ということで以下のようなコマンドをうってみました。

c:\>mklink /J Projects "c:\Users\kaota\Documents\Visual Studio 2017\Projects"
Projects <<===>> c:\Users\kaota\Documents\Visual Studio 2017\Projects のジャンクションが作成されました

c:\>mklink /J Repos c"\Users\kaota\Documents\Repos"
Repos <<===>> c\Users\kaota\Documents\Repos のジャンクションが作成されました

私は、上記2か所でリポジトリに突っ込まないコードとリポジトリに突っ込んでるコードを分けて管理してるのでこんな感じのコマンドになります。

プロジェクトを作ると無事作ってビルドできました。めでたしめでたし。

f:id:okazuki:20170917130542p:plain

ちなみに前に紹介した方法で作ったドライブは再起動したら消えてしまって悲しい気持ちになったのと UWPアプリケーションを起動が出来なかったりというのがあったのですが、こっちだと UWPアプリの起動もできたので、こっちのほうがいい感じかもしれない。


Visual Studio Emulator for Android でネットワークにつなぐ

$
0
0

まぁ普通は標準のエミュレータ使いますよね。

でも世の中には Hyper-Vが必須な人もいるでしょう。 そんな人は Visual Studio Emulator for Androidを使うことになると思います。

ただ Visual Studio Emulator for Androidでは Marshmallowまでしかエミュレータが用意されてないので悲しい気持ちになります。 Nが来てないので Oは個人的に絶望的なのではと思ってます。なので Hyper-Vを常時 ON にしないといけないという制約がない人以外は標準の Android SDK についてくるエミュレータを使う方が幸せになれると思います。

Visual Studio 2017のインストール時に個別のコンポーネントからチェックを入れることでインストールできます。

f:id:okazuki:20170917132423p:plain

さて、このままインストールして Visual Studio Emulator fro Androidを立ち上げて Mashmallowの仮想マシンを適当に入れて起動したのですが、これだとインターネットにつながってませんでした。

ネットにつなごう

Hyper-Vマネージャーを起動します。仮想スイッチマネージャーをぽちっとします。 新しい仮想ネットワークスイッチを選んで外部で適当に作ります。

f:id:okazuki:20170917133736p:plain

で、VS Emulator 5-inch Marshmallow (6.0.0) XXHDPI Phone kaotaみたいな感じの仮想マシンを右クリックして設定を押します。ハードウェアの追加で ネットワーク アダプターを選んで先ほど追加したネットワークスイッチを選択します。

これでエミュレータがネットワークにつながってくれるはずです。

f:id:okazuki:20170917134359p:plain

Visual Studio Mobile Center の Push を試してみよう Android編

$
0
0

プッシュ通知ってめんどいですよね。 ということで Visual Studio Mobile Center の Push を試してみようと思います。Android, iOS, UWP あたりに対応してるらしいですが、とりあえず私の好きな Xamarin.Forms を使って Android に対して対応してみようと思います。

VSMS にプロジェクトを作成

これがないと始まらないのでさくっと作りましょう。

Android で Xamarin でさくっとね。

f:id:okazuki:20170918164931p:plain

設定とか

Push の項目を見てみると Firebase Console でアプリ作ってねっていう感じの説明があるので Firebase Console に移動します。

https://console.firebase.google.com/?hl=ja

とりあえずプロジェクトがないと始まらないみたいなので Firebase Console でプロジェクトを作成しましょう。

f:id:okazuki:20170918165510p:plain

作成後、左側のメニューから 成長 のところにある Notifications を選びます。そこから Android のアプリを作るボタンがあるのでさくっと作っちゃいましょう。

f:id:okazuki:20170918165727p:plain

Android パッケージ名は、これから作るアプリのパッケージと合わせておきます。ここでは jp.okazuki.pushlab.androidにしました。

次の画面で google-services.json をダウンロードするように言われるので、これをダウンロードしておきます。あとで使うので。

f:id:okazuki:20170918170003p:plain

そして、Mobile Center と Firebase の間をつなぐ設定をします。これは、Firebase Console の左のメニューの上の方にある歯車マークを押してプロジェクトの設定を選択します。そして、画面上部のタブにあるクラウドメッセージングを選択して、そこにあるサーバーキーというものを使います。

f:id:okazuki:20170918170352p:plain

サーバーキーをゲットしたら、Mobile Center の画面に戻って Next を選択します。するとサーバーキーを入れる画面になるので、先ほど Firebase Console でゲットしたサーバーキーを入力します。Done を押すと完了です。

プッシュ通知が送れそうな雰囲気の画面になります。

f:id:okazuki:20170918170609p:plain

アプリの作成

では、サーバー側の構成が終わったのでクライアント側を作っていきます。 Xamarin.Forms のアプリケーションをサクッと作りましょう。

ここでは、PushLabApp という名前で作りました。

作ったら、ソリューションのNuGetパッケージの管理で Microsoft.Azure.Mobile.Push パッケージを PCL, Android, iOS, UWP のプロジェクトに追加しましょう。

f:id:okazuki:20170918172514p:plain

次に、先ほどダウンロードした google-services.json を Android プロジェクトに追加します。そしてビルドアクションを GoogleServicesJson にします。

Tips

GoogleServicesJson のビルドアクションが出ない場合は Visual Studio を再起動してみましょう。こういうの多いですよね。

アプリの作成続き

Android プロジェクトのプロパティで Android マニフェストで、パッケージ名に先ほど作成した Firebase Console のアプリで指定したパッケージ名と同じものを設定しましょう。

f:id:okazuki:20170918193737p:plain

f:id:okazuki:20170918185601p:plain

App.xaml.cs の OnStart メソッドに Push を有効化させるコードを書きます。

protectedoverridevoid OnStart()
{
    MobileCenter.Start("android=a58a4c6f-1093-4bfd-a47b-2b3d877a56c6;" +
        "uwp={Your UWP App secret here};" +
        "ios={Your iOS App secret here}",
        typeof(Push));
}

uwp や ios は今後有効化させるとして置いときましょう。

AndroidManifest.xml の application タグの下に2つほど receiver タグを追加します。

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"><uses-sdk android:minSdkVersion="15" /><application android:label="PushLabApp.Android"><!-- ここから --><receiverandroid:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"android:exported="false" /><receiverandroid:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"android:exported="true"android:permission="com.google.android.c2dm.permission.SEND"><intent-filter><action android:name="com.google.android.c2dm.intent.RECEIVE" /><action android:name="com.google.android.c2dm.intent.REGISTRATION" /><category android:name="${applicationId}" /></intent-filter></receiver><!-- ここまで --></application></manifest>

${applicationId} あたりは何か固定値入れるのかなと思わせつつそのままでいいみたいですね。

ビルドして動作確認

では、ビルドしましょう!ちなみにビルドエラー(java.exeが2を返すみたいな雰囲気のメッセージ)が出たら Android プロジェクトのプロパティの Android オプションにある Multi-Dex を有効にするをチェック入れましょう。

んで、実行してホームボタンを押してアプリをバックグラウンドに回します。

Send notification を押します。

f:id:okazuki:20170918192130p:plain

あとは、適当に項目を埋めて

f:id:okazuki:20170918192337p:plain

送信対象者を選びます。

f:id:okazuki:20170918192458p:plain

最後にプレビューが出るので Send notification を押しましょう。

f:id:okazuki:20170918192709p:plain

送信するとこんな感じでリスト形式で表示されます。

f:id:okazuki:20170918194118p:plain

アプリ側には、このような感じで通知がきます。(フォアグランドにアプリがあると来ないので気を付けてね)

f:id:okazuki:20170918194433p:plain

フォアグランドにあるときはどうするの?

Push きても通知が出るのがバックグラウンドだけだったらフォアグランドにあるときはどうしよう?って感じですよね。 その時は、Push.PushNotificationReceived イベントが発火します。ということなので、こんな感じにすればいいじゃん?ってことになりますよね。

protectedoverridevoid OnStart()
{
    Debug.WriteLine("App.OnStart() called");
    MobileCenter.Start("android=a58a4c6f-1093-4bfd-a47b-2b3d877a56c6;" +
        "uwp={Your UWP App secret here};" +
        "ios={Your iOS App secret here}",
        typeof(Push));
    Push.PushNotificationReceived += this.Push_PushNotificationReceived;
}

private async void Push_PushNotificationReceived(object sender, PushNotificationReceivedEventArgs e)
{
    Debug.WriteLine(
        string.Join("\n", e.CustomData?.Select(x => $"{x.Key}, {x.Value}") ?? new[] { "" }));
    if (string.IsNullOrEmpty(e.Message))
    {
        return;
    }

    await this.MainPage.DisplayAlert(e.Title,
        e.Message,
        "OK");
}

protectedoverridevoid OnSleep()
{
    Push.PushNotificationReceived -= this.Push_PushNotificationReceived;
}

ちなみに、Send notification の時に Custom data が設定できる画面がありましたが、これはイベント引数の CustomData プロパティにディクショナリで格納されています。とりあえずあったら適当にデバッグコンソールに出しておきましょう。

f:id:okazuki:20170918223413p:plain

あと、これは個人的に解せぬ動きなんですが、この PushNotificationReceived イベントはバックグラウンドにアプリがあるときに通知がきて、それをタップしたときにも呼ばれるんですよね。その時は、何故か CustomData は値が入っていて Title, Message が null みたいになってます。ちょっとびっくりした。

よくわからないんだけどこれも必要みたい

なんか、Android アプリ開発にそんなに明るくないせいか不勉強のせいかはじめて OnNewIntent なるメソッドを見ました。 なんだか Activity の launchMode が singleTop, singleInstance, singleTask の時は OnNewIntent メソッドを以下のようにしないといけないみたいです。ふーん。

protectedoverridevoid OnNewIntent(Android.Content.Intent intent)
{
    base.OnNewIntent(intent);
    Push.CheckLaunchedFromNotification(this, intent);
}

あとは試してないんだけど

有効無効を切り替えれるらしい。

Push.SetEnabledAsync(false); // 無効
Push.SetEnabledAsync(true); // 有効

配信先の制御

今まで無差別に全員に対して Push 通知を送り込んできました。 ここらへんは、ある程度絞ることができるようになっています。まずは、MobileCenter.getInstallId();でとってこれるインストールID指定で送る方法です。20人まで一気に遅れるみたいですね。当選者発表みたいなのとかに使うんだろうか?

f:id:okazuki:20170918224841p:plain

あとは Push の下にある Audiences でオーディエンスというものを作っておくとユーザーを条件つけて絞り込めるという機能もあります。

f:id:okazuki:20170918225815p:plain

ドキュメント上は Custom Properties を使ってきめ細やかな設定ができるということも書いてありますが UI 上設定の仕方がわかりませんでした。REST API を使ってやってる人がいたので、もしかしたらまだ UI が出来てないんですかね?

blog.cellenza.com

まとめ

ということで、割と簡単にプッシュ通知できるみたいですね。 そして興味を持った人はドキュメントを見てみましょう!

docs.microsoft.com

あと、Mobile Center 自体に興味を持った人で、がんがん使いこなしてみたいって人は、私はまだ見てないけど REST API も公開されてるようなので、それを使うと楽しいことが出来そうな気がしてます。

https://docs.mobile.azure.com/api/

ちなみに、こういう便利系サービス割と好きです。今回 Push をやるにあたって Firebase Console を見たけどこいつも色々出来そうですね。というか Push だけに限ると Firebase そのまま使ってもいいんじゃ?って思ったんですがどうでしょう。Firebase についてはこの資料好きです。

speakerdeck.com

CoreML を Xamarin.Forms で使ってみよう

$
0
0

なんか iOS 11 から CoreML ってのが使えて簡単にいうと機械学習の学習結果を iOS ローカルで動かせるぜ!っていう感じのものらしいですね。強い。

ということで、Apple Developer と Xamarin のドキュメントを見ながら試してみたいと思います。

Core ML | Apple Developer Documentation

developer.xamarin.com

ちなみに、CoreML で使える学習結果のファイルは、Cognitive Services の Custo Vision API で作れるということなので、今回はこれも使ってみたいと思います。

azure.microsoft.com

Custom Vision API でいい感じの画像データを用意するのがめんどくさかったので、Drew さんの作ってくれた、このハンズオンにある画像をそのまま使いたいと思います。

github.com

customvision.ai

ではさくっと Custom Visoin を使えるようにしましょう。本題じゃないので注意点だけを。

Custom Vision のサイトでプロジェクトを作るときに General (compact) を使うことです。これをしないと CoreML で使えるファイルをエクスポートできません。

f:id:okazuki:20170919203915p:plain

あとは、Drew さんのリポジトリにある画像を適当に投げ込んで Fries / Not Fries のカテゴリを作って学習させます。

学習させたら下の青で囲ったエクスポートボタンを押します。

f:id:okazuki:20170919210929p:plain

こんな画面になるので、あとは支持に従うだけです。mlmodel という拡張子のファイルが取得できます。

f:id:okazuki:20170919211037p:plain

次の作業環境は Mac です。以下のコマンドをターミナルでうちます。

xcrun coremlcompiler compile さっきダウンロードしたファイル.mlmodel 出力先フォルダ

出来上がったファイルをどうにかして Windows にもっていきましょう。

Xamarin.iOS で頑張る

とりあえず、Xamarin.Forms でプロジェクトを作って iOS プロジェクトだけで動くように作りたいと思います。先ほどのコマンドでできた一連のファイルを iOS プロジェクトの Resources フォルダに移動させます。

f:id:okazuki:20170919223451p:plain

ClreML を使って認識をしてくれるであろう処理のためのインターフェースを PCL プロジェクトに作ります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoreMLLabApp
{
    publicinterface IFriesOrNotFriesService
    {
        Task<string> DetectAsync(byte[] image);
    }
}

そして、MainPage.xaml を以下のような感じにします。

<?xml version="1.0" encoding="utf-8"?><ContentPage xmlns="http://xamarin.com/schemas/2014/forms"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:local="clr-namespace:CoreMLLabApp"x:Class="CoreMLLabApp.MainPage"><ContentPage.Padding><OnPlatform x:TypeArguments="Thickness"><On Platform="iOS">0,20,0,0</On></OnPlatform></ContentPage.Padding><Grid><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition /></Grid.RowDefinitions><Button Text="Take picture"Clicked="Button_Clicked" /><Image x:Name="image"HorizontalOptions="Fill"VerticalOptions="Fill"Grid.Row="1" /></Grid></ContentPage>

そして、カメラから画像をとりたいので Xam.Plugin.Media を導入して表示される readme.txt の内容に従って info.plist に設定を追加したら MainPage.xaml.cs を以下のようにします。

using Plugin.Media;
using Plugin.Media.Abstractions;
using System;
using System.IO;
using Xamarin.Forms;

namespace CoreMLLabApp
{
    publicpartialclass MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private async void Button_Clicked(object sender, EventArgs e)
        {
            await CrossMedia.Current.Initialize();

            var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions());
            if (file == null) { return; }

            this.image.Source = ImageSource.FromStream(() => file.GetStream());

            using (var fs = file.GetStream())
            using (var ms = new MemoryStream())
            {
                await fs.CopyToAsync(ms);
                var d = DependencyService.Get<IFriesOrNotFriesService>();
                var result = await d.DetectAsync(ms.ToArray());
                await this.DisplayAlert("Result", result, "OK");
            }
        }
    }
}

ここまでは、ただの Xamarin.Forms ですね。

CoreML を使ってみよう

では、iOS プロジェクトに FriesOrNotFriesService.cs を追加して処理を書いていきます。サンプルプロジェクトとかを参考に以下のように書いてみました。

using System;
using System.Linq;
using System.Threading.Tasks;
using Foundation;
using Vision;
using CoreML;
using CoreImage;
using CoreFoundation;

[assembly: Xamarin.Forms.Dependency(typeof(CoreMLLabApp.iOS.FriesOrNotFriesService))]
namespace CoreMLLabApp.iOS
{
    publicclass FriesOrNotFriesService : IFriesOrNotFriesService
    {
        privatestatic VNCoreMLModel VModel { get; }

        static FriesOrNotFriesService()
        {
            // Load the ML model
            var assetPath = NSBundle.MainBundle.GetUrlForResource("e3e4e645c0944c6ca84f9a000e501b22", "mlmodelc");
            var friedOrNotFriedModel = MLModel.Create(assetPath, out _);
            VModel = VNCoreMLModel.FromMLModel(friedOrNotFriedModel, out _);
        }

        public Task<string> DetectAsync(byte[] image)
        {
            var taskSource = new TaskCompletionSource<string>();
            void handleClassification(VNRequest request, NSError error)
            {
                var observations = request.GetResults<VNClassificationObservation>();
                if (observations == null)
                {
                    taskSource.SetException(new Exception("Unexpected result type from VNCoreMLRequest"));
                    return;
                }

                if (observations.Length == 0)
                {
                    taskSource.SetResult(null);
                    return;
                }

                var best = observations.First();
                taskSource.SetResult(best.Identifier);
            }

            using (var data = NSData.FromArray(image))
            {
                var ciImage = new CIImage(data);
                var handler = new VNImageRequestHandler(ciImage, new VNImageOptions());
                DispatchQueue.DefaultGlobalQueue.DispatchAsync(() =>
                {
                    handler.Perform(new VNRequest[] { new VNCoreMLRequest(VModel, handleClassification) }, out _);
                });
            }

            return taskSource.Task;
        }

    }
}

byte[] から CoreML の入力として渡すための CIImage に変換して認識処理を呼び出しています。

動かして動作確認

ふむ。とりあえず動くっぽい。

f:id:okazuki:20170919224454p:plain

f:id:okazuki:20170919224507p:plain

あとは、DependencyService で Android や UWP の時には、CustomVision の API をたたくように仕込めば iOS だけ CoreML で他は REST API みたいなことが出来ますね。

ソースコードは以下に置いています。

github.com

python から Cognitive Services の API をたたいてみよう

$
0
0

Cognitive Services は、ある程度でしたら無料で試せます。

azure.microsoft.com

ここでは、Emotion API を試しにたたいてみようと思います。

API キーの取得

Emotion API の API キーの取得 ボタンを押します。 使用条件が表示されるので、よく読んで国を選択して、同意できる場合は同意して次へ進みます。

f:id:okazuki:20170920111740p:plain

次にサインインを求められます。これは Microsoft Account, Facebook, LinkedIn, Github が使用できます。

f:id:okazuki:20170920111832p:plain

私は Github を選択しました。 Github のサインイン画面が出てくるのでサインインしましょう。 サインインすると以下のように呼び出しに必要なエンドポイントとキーが表示されます。とりあえずキー1があれば問題ありません。

f:id:okazuki:20170920112143p:plain

呼んでみよう

基本こちらにやり方が書いてあります。

docs.microsoft.com

Python 2.7 で試してみてます。 とりあえず Emotion API のサンプルがインターネット上の画像の URL を送って結果を返すものだったので、ここではローカルの画像ファイルを読み込んで POST するものにしました。

import httplib
import urllib
import base64

headers = {
    'Content-Type': 'application/octet-stream',
    'Ocp-Apim-Subscription-Key': 'ポータルから取得したキー'
}

params = urllib.urlencode({

})

try:
    withopen("emotionsample.jpeg", "rb") as f:
        conn = httplib.HTTPSConnection('westus.api.cognitive.microsoft.com')
        conn.request('POST', '/emotion/v1.0/recognize?%s' % params, f, headers)
        response = conn.getresponse()
        data = response.read()
        print(data)
        conn.close()
exceptExceptionas e:
    print("[Errno {0}] {1}".format(e.errno, e.strerror))

実行すると以下のように JSON が返ってきます。

[{"faceRectangle": {"height": 162,
            "left": 130,
            "top": 141,
            "width": 162
        },
        "scores": {"anger": 9.29041E-06,
            "contempt": 0.000118981574,
            "disgust": 3.15619363E-05,
            "fear": 0.000589638,
            "happiness": 0.06630674,
            "neutral": 0.00555004273,
            "sadness": 7.44669524E-06,
            "surprise": 0.9273863
        }}]

コードは以下の Github リポジトリに格納しています。

github.com

Prism.Forms.Unity を .NET Standard プロジェクトにしてみよう(無理やり感)

$
0
0

Xamarin.Forms 2.4 が .NET Standard 2.0 化したこともあり .NET Standard の機運が高まってまいりました。 Prism のほうも 7.0.0 で .NET Standard 対応っぽいのでいい流れです。

最近のライブラリは .NET Standard 前提で作られてるものもチラホラ出てきてるので対応が進んでくれるのは嬉しいですね。

Prism.Forms のプロジェクトの .NET Standard への変換

ということで、現状の Prism Template Pack で新規作成されたプロジェクトとベースに .NET Standard にしてみようと思います。 まず、Prism Template Pack でプロジェクトを新規作成します(NETStdPrismApp という名前でここでは作りました)。現時点では Android, iOS をターゲットにします。何故かというと Xamarin.Forms が .NET Standard 2.0 を対象としているためです。UWP は Fall Creators Update 以降で .NET Standard 2.0 対応ということなので、現時点ではまだ使えないためです。しょんぼり。

新規作成したら、さっそく .NET Standard のクラスライブラリのプロジェクトを追加しましょう。NETStdPrismApp.NETStandard という感じの名前で作りました。Class1.cs をさくっと消して NETStdPrismApp.NETStandard のプロパティを開きます。ターゲットフレームワークを .NET Standard 2.0 にして規定の名前空間を NETStdPrismApp あたりにしておきます。

f:id:okazuki:20171003130613p:plain

移植可能のプロジェクトから packages.config 以外のファイルをまるっとコピーします。 そして、移植可能のプロジェクトを右クリックからアンロードしておきましょう(消すのはうまく動くようになってからのほうが精神衛生上いいので)Droid と iOS プロジェクトから .NET Standard のプロジェクトを参照に追加します。ついでに、移植可能のプロジェクトを Droid と iOS プロジェクトから削除しておきましょう。

ソリューションの NuGet パッケージの管理を開きます。開いたらプレリリースを含めるにチェックを入れて Prism.Unity.Forms v7.0.0-pre1 を選択して Droid, iOS, .NET Standard のプロジェクトに追加します。私はここで Visual Studio の再起動を求められたので再起動をしました。

NuGet パッケージマネージャーからプレリリースを含めるのチェックを外して Xamarin.Forms の最新版(現時点で 2.4.0.282 でした)を全プロジェクトに追加します。私はここでまた Visual Studio の再起動を求められました。

App.xaml, MainPage.xaml が EmbeddedResource として csproj ファイルに追加されてるのをさくっと消します。

<ItemGroup><EmbeddedResource Include="App.xaml"><Generator>MSBuild:UpdateDesignTimeXaml</Generator></EmbeddedResource><EmbeddedResource Include="Views\MainPage.xaml"><Generator>MSBuild:UpdateDesignTimeXaml</Generator></EmbeddedResource></ItemGroup>

あと、プロジェクトの下にあるファイルは一定のルールで勝手に追加してくれるみたいなので、MainPage.xaml.cs や App.xaml.cs に関する記述も消してOKっぽいですね。

ここで、やっとビルドが通るようになります。 実行すると…

f:id:okazuki:20171003143236p:plain

動いた!!やったね!

警告!!(未解決)

動いたけど警告が出てますね。 こういうのは .NET Portability Analyzer を使ってチェックすることが出来ます。

docs.microsoft.com

とりあえず exe をダウンロードして適当な場所に配置します。ソリューションエクスプローラを見る限りだと Unity と CommonServiceLocator が警告のおおもとなんですかね?C:\Users\[ユーザー名]\.nuget\packages\commonservicelocator\1.3.0\lib\portable-net4+sl5+netcore45+wpa81+wp8あたりに dll があるので、そこで以下のコマンドを打ち込みました。

apiport.exe analyze -f Microsoft.Practices.ServiceLocation.dll
Microsoft (R) API Portability Analyzer v1.4.0.alpha.00199
Copyright (C) Microsoft Corporation. All rights reserved.

This tool analyzes .NET assemblies to determine possible problems moving between .NET platforms (such as
Windows Store, desktop, Mono, .NET Core, etc) as well as between .NET Framework versions (ie 4.x->4.y).

To learn more about how this tool works, including the data we are collecting,
go here - http://go.microsoft.com/fwlink/?LinkId=397652

Detecting assembly references                     [Done]
Analyzing compatibility                           [Done]
Writing Excel report                              [Done]

Wrote output to file:

Excel 形式のレポートが出てくるので見てみると大丈夫そうですね。

f:id:okazuki:20171003160612p:plain

Unity も同じ感じでレポートを出してみると、こっちはダメな感じっぽいですね。

f:id:okazuki:20171003161608p:plain

まぁ動いたし警告気にしないで無視るかと思って Prism.Unity.Forms の NoWarn の項目に NU1701 を入れたのに警告が消えない。ふむ。Prism.Unity.Forms じゃなくて、その依存先の警告は消してくれないのかな?消し方がわからん…。プロジェクトレベルで警告を無視することはできるみたいだけど、それはやりすぎ感ですしね。

Windows ストアへ自分の Win32 アプリを上げる Desktop Bridge

$
0
0

WPF とかで作ったアプリは、そのままではストアから配布できません。 ストア対応したかったら UWP でアプリを作りましょう!!というのが一応基本スタンスです。 (UWP が現状流行ってるのかは置いておいて、Windows 10 ネイティブなアプリケーション開発は UWP でやるのが一応第一選択肢です)

まぁそうはいっても…という状況で活躍するのが Desktop Bridge ですね。ということで見てみましょう。

Desktop Bridge

ドキュメントがしっかりしてるので、そこを見るのが正です。

デスクトップ ブリッジ - UWP app developer | Microsoft Docs

まだ、にわかなので間違ってること書いてるかもしれませんが、登場当初は msi 形式のインストーラを appx に変換するぜ!みたいな雰囲気で登場したと思います。まぁでも、実態は AppxManifest.xml がちゃんとあって、exe を同梱して appx にパッケージングすればストアに上げることが出来るっぽいですね。

ちなみに、Desktop Bridge で変換したものをストアに上げたい!って思ってる人はここのフォームから申請するとプロセスがスタートします。

Desktop Bridge Sign Up Form - Windows app development

Desktop App Converter

Desktop App Converter を使うと、簡単に msi 形式のインストーラーや実行形式の exe を appx にパッケージングしてくれたり AppxManifest.xml を生成してくれます。便利なので、まずはこれを足掛かりにやってみるのがいいのかなと感じています。

セットアップは Desktop App Converter をストアから仕入れて自分の OS のバージョンとあった BaseImage をダウンロードしてきてコマンドを叩くという感じです。ここらへんに書いてあります。(BaseImage のダウンロードは msi 形式のインストーラーを Desktop App Converter で変換するときに使うので msi じゃない場合はいらないですね)

Desktop App Converter を使用してアプリをパッケージ化する (デスクトップ ブリッジ) - UWP app developer | Microsoft Docs

注意点としてアップデートをきちんとあてている Creators Update の Windows 10 を使ってると対応する BaseImage-15063 を入れるのではなく BaseImage-15063-UPDATE というのを入れないとダメってところですかね。

zip で配ってる感じのアプリを変換してみよう

つまり、よくあるフリーソフトみたいな感じのやつですね。 インストーラーがない形式です。zip をダウンロードして任意の場所に展開して、その中にある exe を起動する感じで使うアプリになります。

例えば DacApp フォルダ以下に DacApp.exe というファイルがあって、それを実行すれば動くアプリの場合は以下のようなコマンドで appx にできます。

DesktopAppConverter.exe -Installer .\DacApp -AppExecutable DacApp.exe -Destination c:\Work\dacsample-noinstaller\Output -PackageName "DacApp" -Publisher "CN=okazuki" -Version 0.0.0.1 -MakeAppx -Sign -Verify

パラメータの意味は以下を参照するといいです。

Desktop App Converter を使用してアプリをパッケージ化する (デスクトップ ブリッジ) - UWP app developer | Microsoft Docs

インストーラー形式のアプリを変換してみよう

とりあえず WPF のアプリを作って Microsoft Visual Stduio 2017 Instalelr Projectsを入れてセットアッププロジェクトを作って msi 形式にパッケージングしてみました。別に msi 形式じゃなくてもサイレントインストール可能なら何でもいいです。

この Installer Projects で作った msi 形式のインストーラーは /qn 引数でサイレントインストールできるみたいなので、Desktop App Converter のマニュアルに従い以下のようなコマンドをぱちぱちと叩くことで appx にパッケージングして適当な証明書で署名してくれます。

DesktopAppConverter -Installer .\DacApp.Setup.msi -InstallerArguments "/qn" -Destination .\Output
-PackageName DacApp -Publisher "CN=okazuki" -Version 0.0.0.2 -MakeAppx -Sign -Verify

マニュアルはこちら。(再掲)

Desktop App Converter を使用してアプリをパッケージ化する (デスクトップ ブリッジ) - UWP app developer | Microsoft Docs

インストールして実行してみよう

こんな感じのものが Output フォルダの下に作られます。

f:id:okazuki:20171005224615p:plain

生成された appx はダブルクリックすることでさくっとインストールできそうな雰囲気を醸し出しつつ失敗します。これはローカル コンピューターの信頼されたルート証明機関に auto-generated.pfx をインストールしてやる必要があります。やですね。 インストールの過程でパスワードを求められるのですが、これは 123456 になります。

証明書をインストールしたら気を取り直して appx を起動するとこんな感じの画面が出ます。

f:id:okazuki:20171005224924p:plain

インストール後起動するとこんな感じ。

f:id:okazuki:20171005225043p:plain

いいね!!

再パッケージ

Desktop App Converter での変換は毎回しなくてもよくて、なんかインストール時に書き込むレジストリが変わっただとかいう影響のでかい変更しない限りは必要ないみたいです。

では、どうやって appx を作るのか?ですが makeappx.exe を使ってやります。

docs.microsoft.com

出力先として指定したフォルダに PackageFiles というフォルダが出来てるので、そこのファイルを更新したら makeappx.exe で appx に固めるといった感じです。makeappx.exe は以下のフォルダにあるのでパスを通しておくとスムーズかもしれません。

  • x86: C:\Program Files (x86)\Windows Kits\10\bin\x86\makeappx.exe
  • x64: C:\Program Files (x86)\Windows Kits\10\bin\x64\makeappx.exe

こんなコマンドで再パッケージングできます。

makeappx.exe pack /d .\PackageFiles /p DacApp.appx /l

appx が出来たらインストールできるように署名しましょう。署名はここにドキュメントがあります。

docs.microsoft.com

Desktop App Converter が生成してくれる auto-generated.pfx を使う場合は以下のようなコマンドで OK です。

signtool sign /fd SHA256 /a /f .\auto-generated.pfx /p 123456 .\DacApp.appx

Windows 10 S でのテストも忘れずに

これは Desktop Bridge の要件ではなく Windows ストアの要件なんですが Windows 10 S で動くことが必須みたいですね。

デスクトップ ブリッジ - UWP app developer | Microsoft Docs

Windows ストアにアプリを公開する場合は、Windows 10 S を実行するデバイスでアプリが正しく動作することを確認してください。これは、ストア要件です。 「Windows アプリの Windows 10 S 対応をテストする」をご覧ください。

Windows 10 S ですがサイドローディング禁止です。じゃぁどうやってテストするの?ということになりますが、Windows 10 Pro になんかポリシーみたいなのあてて疑似的にやる感じみたいですね。

ちなみに仮想マシンでやることを強くおすすめされてます。(まぁ普通のアプリインストールできなくなるしね…)

Test your Windows app for Windows 10 S - UWP app developer | Microsoft Docs

具多的にはダウンロードできる zip の SiPolicy_DevModeEx_Enforced.p7b を SiPolicy.p7b にリネームして C:\Windows\System32\CodeIntegrity において再起動するだけみたいですね。

そうすると、zip に同梱されてる AppxTestRootAgency.pfx で署名した appx がインストールできるようになるみたいです。署名するにあたり AppxManifest.xml の書き換えが必要です。

Identity の Publisher を CN=Appx Test Root Agency Ex にする必要があります。例えば私の場合 Desktop App Converter で okazuki としたので CN=okazuki と記載されています。

<Identity Name="DacApp"ProcessorArchitecture="x64"Publisher="CN=okazuki"Version="0.0.0.1" />

こんな感じにします。

<Identity Name="DacApp"ProcessorArchitecture="x64"Publisher="CN=Appx Test Root Agency Ex"Version="0.0.0.1" />

そしたら makeappx.exe で appx にパッケージングします。そして signtool で署名しておきます。

makeappx.exe pack /d .\PackageFiles /p DacApp.appx /l
signtool sign /fd SHA256 /a /f .\AppxTestRootAgency.pfx .\DacApp.appx

ポリシーあてた Windows 10 Pro でも無事インストールして起動できました。

f:id:okazuki:20171005233953p:plain

ちなみに、AppxTestRootAgency.pfx 以外の証明書で署名した appx を用意して pfx を信頼されたルート証明機関に追加するとインストールまでは成功しますが起動しようとすると以下のような感じになります。

f:id:okazuki:20171005234916p:plain

まとめ

とりあえず Desktop Bridge の入り口である変換の部分を紹介しました。Desktop Bridge 自体は秀丸エディタさんがストアで公開されたりとかされてて熱い感じですね。

秀まるおのホームページ(サイトー企画)−秀丸エディタ(ストアアプリ版)

長い目で見ると Pure な UWP なんでしょうが、とりあえず現実問題として exe 形式のアプリを抱えてる人は UWP 化へのとっかかりとして Desktop Bridge で appx にしてしまうのもありじゃないでしょうか。

あ、因みにもとが exe なので Windows.Desktop でしか動かないです。

追記

Paint.net もいつの間にかストアで配布されてた!!すごい!

blog.getpaint.net

Microsoft Edge の iOS 版と Android 版が!?

$
0
0

なんかできてる。

blogs.windows.com

今朝、手元の Android の Arrows Launcher が Microsoft Lanucher に置き換わってたのですよ。まぁそれは置いといて、Microsoft Edge の iOS 版と Android 版が出るとな!?

これは、設定同期とかされると嬉しい。

個人的にはカスタマイズが嫌いなので、Android では Chrome, iOS では Safari, Windows では Edge を使ってたのですが、Edge に統一して便利なら乗り換えてみようかなぁ。

あ、ステータスはまだ Preview です。


一番簡単な DesktopBridge のやり方(Visual Studio 2017 15.4 の Windows アプリケーション パッケージ プロジェクト)

$
0
0

先日リリースされた Visual Studio 2017 15.4 で追加された Windows アプリケーション パッケージ プロジェクト を使うと手元に exe を出力する形式のプロジェクトがあって xcopy でインストールが終わるような類のアプリケーションを凄く簡単に appx 形式にパッケージングしてくれます!!

これはいい。これまでも Visual Studio を使って appx にパッケージングする方法とかはありましたが…

Visual Studio を使ったアプリのパッケージ化 (デスクトップ ブリッジ) - UWP app developer | Microsoft Docs

これは、楽っちゃぁ楽だけど、まぁ無理やり感というかなんというかという感じでした。

Windows アプリケーション パッケージ プロジェクト

そこで Windows アプリケーション パッケージ プロジェクトです。 こいつは、Windows ユニバーサルのプロジェクトテンプレートの中にひっそりと追加されています。

f:id:okazuki:20171013235142p:plain

プロジェクトを新規作成すると、こんな感じになります。

f:id:okazuki:20171014085412p:plain

あとは、同じソリューションに exe 形式を出力するプロジェクトを追加して、Windows アプリケーション パッケージ プロジェクトの中のアプリケーションに追加します。試しに Visual C++ の Win32 アプリケーションを追加してみました。

f:id:okazuki:20171014090337p:plain

あとは、ビルドして実行すると appx が作られてインストールされて起動してきます。(.NET のアプリだと大丈夫なのかもだけど、C++ なのでターゲットプラットフォームは x86 or x64 を選んでから実行)

ちゃんとデバッガがアタッチされてブレークポイントも止まります。素晴らしい。

f:id:okazuki:20171014090806p:plain

ストア関連の機能も…

ストアのアプリとの紐づけやパッケージの作成ができます。

f:id:okazuki:20171014091156p:plain

Windows アプリ 認定キット(WACK)ももちろんかけることができます。

f:id:okazuki:20171014091353p:plain

まとめ

インストーラーが不要なアプリケーションは大体いけそうな気がしますね。。

New Surface Pro で Windows 10 Fall Creators Update をあてて Mixed Reality ポータルを起動してみよう

$
0
0

まだヘッドマウントディスプレイ持ってません。はい。なので試せてませんが Windows 10 Fall Creators Update を当てた New Surface Pro で Mixed Reality ポータルをあてるとこうなりました。

f:id:okazuki:20171018113753p:plain

なんだと… WinMR が試せるから Surface Book から New Surface Pro にしたっていうのに!?

対処法

ということで、デバイスマネージャーからグラフィックドライバを更新しましょう。

f:id:okazuki:20171018113915p:plain

f:id:okazuki:20171018113931p:plain

そうすると無事に起動しました!やったね!!

f:id:okazuki:20171018113955p:plain

まとめ

ヘッドマウントディスプレイ買わなきゃ…。

New Surface Pro で Windows MR が動くまで(結構苦戦した)

$
0
0

ヘッドマウントディスプレイをさしても動かなかったんですよね…セットアップが終わってもこんな感じで。

チカチカチカチカしてます。しばらくするとこんなエラーになってます。

C0001160-101 が出ちゃうんですよねぇ。ぐぐっても情報なし。

Duet Diaplay との相性が悪かったです

iPad をサブディスプレイにするために Duet Diaplay をインストールしてたのですが、こいつがいるとこのエラーになるみたいです。停止だけではだめでアンインストールが必要でした。

アンインストールしたらOKでした。

2017/10/19 時点の情報なので Duet Diaplay のバージョンアップにより改善される可能性があります。

DI コンテナの Unity の v5.0.0 がリリースされています!

$
0
0

ゲームエンジンじゃないほうの Unity がバージョンアップしてました!!

Added support for .NET 4.0, 4.5, 4.7, .NET Core 1.0+ and .NET Standard 1.0+ where available.ということなので .NET Standard 1.0+ というとても幅広い環境で使えるように仕上がっています。

github.com

www.nuget.org

あと、しれっと v4.x 系は End of life になってますね…。ふむ…。悩ましい。

Windows Mixed Reality で普通の UWP を作成「何故か写真を撮らせない方法まで」

$
0
0

まぁ出来ますよね。

UWP アプリを新規作成して普通に作ってデプロイしちゃえばいいです。 めんどくさいのがデバッグです。そのままローカルコンピューターに対して実行するとデスクトップで立ち上がってくるんですよね…。なんかいい方法ないものだろうか。

デバッグ方法

Cliff House でスタートメニューからデバッグ対象のアプリケーションを立ち上げます。そしてデスクトップの世界に戻ってデバッガのアタッチをします。 これでデバッグが出来るようになります。

写真撮らせないようにしてみた

試しにこれをやってみました。写真ってスクリーンショットと同じ扱いなのかなぁと思って。

blog.okazuki.jp

ということで撮影してみたら真っ黒画面になりました!!スクリーンショットと同じなんですねぇ。

f:id:okazuki:20171020232129j:plain

Viewing all 1388 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>