Hatena::ブログ(Diary)

hishidaのblog このページをアンテナに追加 RSSフィード

プロフィール

hishida

hishida

EB series support page 管理人 ブログ

2017-07-17

[] Visual C++2010ランタイムインストーラに含める方法

EBWin4はVisual Studio 2010 のC#C++で開発しているため、実行には.Net Framework 4.0とVisual C++ 2010 ランタイムが要る。

.Net Framework 4はWindows Vista以降は標準でWindowsに含まれているため、通常はインストールの必要はない。

だがVisual C++ 2010 ランタイムWindows に含まれていないため、別途ユーザに「Visual C++ 2010 再配布可能パッケージ(x86)」をインストールしてもらわないといけない。やっかいなことに、Visual C++ はバージョンごとにランタイムが違い、さらに32bit(x86)版と64bit版(x64)が存在する。EBWin4のREADMEには対応するバージョンの再配布可能パッケージのリンクが書いてあるが、利用者にとって敷居が高いことは事実である。

これまでインストーラにはフリーの Inno Setup を使っていたが、Visual Studioインストーラを使用すれば、必須コンポーネントインターネットから自動インストールするようにできるので、今回からインストーラを変更することにした(来週リリース予定)。

Visual Studioインストーラの作り方

ここでインストールする必須コンポーネントVisual Studio 2010ランタイムライブラリ(x86)を追加する。(なお、必須コンポーネントインストーラに含めるか、インターネットからダウンロードするかを選ぶことができる。)

インストーラの本体は.msiだが、setup.exeはコンピュータに必須コンポーネントがあるかどうかを調べ、インストールされていなければ次のような画面が出て、インターネットからダウンロードしてインストールまで行う。

f:id:hishida:20170717162740p:image

これで起動トラブルは減ると思う。

ここで一つ問題があって、Visual Studio 2010にはSP1が提供されており、2010無印と 2010 SP1とでランタイムライブラリが異なる。再配布可能パッケージも2010無印と2010 SP1の2種類が存在する。

Download Microsoft Visual C++ 2010 再頒布可能パッケージ (x86) from Official Microsoft Download Center

Download Microsoft Visual C++ 2010 SP1 再頒布可能パッケージ (x86) from Official Microsoft Download Center

ユーザのコンピューターにVisual C++ 2010 SP1のランタイムが先に入っていた場合、 「より新しいバージョンのMicrosoft Visual C++ Redistributableがコンピューター上で検出されました」 と表示されてインストールが止まってしまう。

この場合は.msiを実行していただければ、EBWin4の本体だけインストールされる。

Visual Studio 2015 Installer Projects

Visual Studioインストーラはその後、Visual Studio 2012 で削除されてInstallShield LEだけになったが、 Visual Studio 2013から復活した。ただしVisual Studio Marketplaceから別途インストールする必要がある。現在のVisual Studio Community 2015でも使用できる。

Microsoft Visual Studio 2013 Installer Projects の設定方法について - マコーの日記

Microsoft Visual Studio 2015 Installer Projects をインストールする

2017-07-12

[] xdoc2txt の64bit版

ユーザからの要望があって、xdoc2txtおよびdllの64bit版を作成したので、忘備録を書いておく。

x64コンパイラおよびツールインストール

Visual Studio 2010を標準でインストールすると、64ビット用コンパイラインストールされないので、インストール時のオプションで「x64コンパイラおよびツール」を指定してインストールする。

f:id:hishida:20170712084244p:image

既存のプロジェクトの64bit化

32bit版の既存のプロジェクトを64bit化する場合の手順。

  • 32bit版の既存のプロジェクトを読み込んだ後、メニュー→ビルド(B)→構成マネージャー(O)を起動
  • アクティブソリュージョン構成(C)→新規作成...を選び、64bit用の新しいソリュージョン構成を作成する。名称は例えば"Release x64"とする。この時設定のコピー元に(32bitの)既存のソリュージョンを選択すると、ソリュージョンがコピーされる。
  • アクティブソリュージョンプラットフォーム(P)→新規作成

新しいプラットフォームを入力または選択してください(P) : x64

設定のコピー元 : Win32

EXE、LIB、DLLも全て同様の手順で64bit化される。

注意点は、32bitのDLLと64bitのDLLを混ぜて使うことはできないので、関連するDLLライブラリは全て64bitで揃える必要がある。xdoc2txtは内部でzlib1.2.3を使用しているので、zlib1.2.3もソースから64bitで再コンパイルした。

またVisual C++の64bit版を使用してコンパイルしたモジュールを実行する場合、Visual C++頒布可能パッケージもx64用をインストールする必要がある。

32bit版Download Microsoft Visual C++ 2010 再頒布可能パッケージ (x86) from Official Microsoft Download Center
64bit版Download Microsoft Visual C++ 2010 再頒布可能パッケージ (x64) from Official Microsoft Download Center

一般的な注意

Visual C++ の64bit版の一般的な注意は、

  • 64 ビット Windows OS上ではポインタは64bitだが、int と long は相変わらず32bit ( 64bit整数は__int64 または long long ! )。したがってポインタをintやlongに代入してはいけない。
  • size_t、time_t、 ptrdiff_t は、64 ビット Windows OS上では 64 ビット。したがってtime_tをlongに代入してはいけない。

C# で64bit版のdllを使用する場合の注意点

C#のプロジェクトのプラットフォームターゲットはデフォルトではx86になっているため、デフォルトのままだと32ビット版のdllしか読み込まない。C#から64bitのdllを呼び出す場合は、プラットフォームターゲットをAnyCPUに変える。

2017-07-11

[][] 読書尚友 Text-to-speech対応

家電量販店で現在発売中のAndroidスマホ最新機種を調べたところ、どうやら現行の機種は全て標準で日本語読み上げエンジンに対応しているようだ。

(設定→「言語と入力」→「テキスト読み上げの出力」→「優先するエンジン」→「Googleテキスト読み上げエンジン」→「言語」 に日本語があり、音声データがインストール済みになっている。)

そこで安心して読書尚友もText-to-speech対応することにした。

  • 青空文庫形式のルビ《》に対応。
  • 。、の句読点の単位で読み上げを行い、ポーズボタンで中断/再開 ができる。現在読み上げ中の位置は下線で表示する。
  • 頁をまたがると自動的に頁送りを行う。
  • CJKなら日本語読み上げエンジン、英語なら英語のエンジンを使う。青空文庫の「アーサー王物語」のように英語と日本語が混じる文章でもそれぞれのエンジンで読み上げを行う。

機能的にはほぼこれでいいと思うが、肝心の日本語読み上げエンジンの抑揚のない機械音が不自然で、とても長く聞いていられない。日本語読み上げエンジンが進化しないと、実用性は今ひとつかもしれない。

2017-07-04

[][] EBPocket for Android pro Text-to-speech サポート

Text-to-speechはテキストの読み上げ機能で、Androidでは1.6から早くもAPIが提供されていた。だがAndroidの標準のテキスト読み上げエンジンは、英語、イタリア語スペイン語ドイツ語フランス語しかサポートしておらず、日本語の読み上げを行うためにはサードパーティの日本語読み上げエンジンを導入する必要があった。このため、EBPocketも読書尚友も、Text-to-speechをサポートしてこなかった。

ところが、昨年購入したASUS Zenfone 3 laserは、標準で日本語読み上げエンジンを搭載していることがわかり、Text-to-speechをサポートする意欲が湧いてきた。

とりあえずEBPocket for Android proで対応してみた。

  • テキストの範囲を選択してコンテキストメニューでTTSを選択すると、範囲指定したテキストを読み上げる
  • 範囲指定せずにメニューからTTSを選択すると、本文全てを読み上げる
  • 画面にタッチすると読み上げを止める
  • 文字列中にCJK統合漢字、ひらがな、カタカナを含んでいた場合は、日本語読み上げエンジンを使う。含まれていない場合は英語の読み上げエンジンを使う。
  • CJK統合漢字に使用する読み上げエンジンは、日本語と中国語から選択できる。

日本語読み上げエンジンが使えない機種でも、少なくとも英語の読み上げはできるので、英和や和英で発音を確かめたい場合には、それなりに有用だと思う。

次は当然、読書尚友でサポートしなければいけないと思っている。

その他仕掛り中のプロジェクト

  1. xdoc2txt の64bit 版。(→できました。)
  2. EBStudioのUnicodeベースでの再開発。EBStudioのソースはVisual Studio6.0時代のもので、今日のVisual Studioではコンパイル自体が不可能。こちらは大きな作業になるので、1年以上かかりそうだし、途中で投げ出すかもしれない。

2017-06-19

[] EBPocket for iOS のApp Extension対応について

iOSアプリケーション間でテキストを受け渡したい場合、以前はURL Schemeクリップボード、OpenInぐらいしか方法がなかった。

iOS8からは App Extension という新たなアプリケーション連携の方法が提供されており、Androidのintentのように、任意のアプリケーション間でテキストやイメージが受け渡せるようになった。

iOS8の登場からもう3年も経っているが、遅ればせながらEBPocket Pro for iOSにもApp Extensionを実装してみた。

App Extensionには種類があるが、EBPocketではShare Extensionに対応した。

共有の画面例

Safariやメモで文字列を範囲選択し、[共有...]を実行すると、App Extensionに対応したアプリの一覧が表示される。

f:id:hishida:20170619100117p:image

f:id:hishida:20170619100211p:image

ここでEBPocketを選択し、Postを押すとEBPoketが起動して受け渡された文字列で検索を実行する。

f:id:hishida:20170619100302p:image

f:id:hishida:20170619100330p:image

左上の戻るボタンを押すと呼び出し元のアプリに戻る。

スプリットビューが使用できるiPadでは、SafariとEBPocketを両方開いたままにして、Safariで文字選択した文字列でEBPocketで検索できる。

f:id:hishida:20170619100453p:image

f:id:hishida:20170619100444p:image

実装の方法

App Extensionのプログラミングについては、Appleから日本語訳が出ている.

App Extensionプログラミングガイド:App Extensionはアプリケーションの効用を拡大する

App Extensionは単独で配布することはできず、必ず収容アプリと同時に配布しないといけない。今回はEBPocketが収容アプリということになる。

App Exensionと収容アプリとは別のバイナリであり、開発言語が異なっていてもいい。今回は勉強のため、Swift3で書くことにした。

ただし、実行できるiOSのバージョンの条件がiOS7.0以上になったが、現在では困る人はほとんどいないと思う。

Xcodeで 既存のアプリにApp Extensionを追加するのは簡単で、

[File]->[New]->[Target...]を押してApp Extensionの種類からShare Extensionを選択し、Product Nameを入力する。

収容アプリの Bundle Identifire にProduct Nameを連結したものが、App ExtensionのBundle Identifireになる。

収容アプリのBundle Identifire : 
        info.ebstudio.EBPocketPro
App ExtensionのBundle Identifire : 
        info.ebstudio.EBPocketPro.shareExtension

ここで次の3つのファイルが生成されるので、これを雛形としてコードを追加していく。

  • ShareViewController.swift
  • Maininterface.storyboard
  • info.plist

info.plistでは:

  1. Bundle display name にextensionの表示名を設定(この場合EBPocket)
  2. 共有でテキストのみ選択できるようにNSExtensionActivationRuleを修正する
    NSExtension
        NSExtensionAttributes
            NSExtensionActivationRule
                NSExtensionActivationSupportsText	Boolean	YES

収容アプリと同じアイコンを使用するには、Targetに収容アプリの .xcassets のアイコン名を設定する:

Build Phases
    Copy Bundle Resources
        + 収容アプリの .xcassets   を追加する
Build Settings
    Asset Catalog App Icon Set Name     ->  AppIcon (.xcassetsのアイコン名)

ここで一つ困ったことは、Share Extension から収容アプリを起動する方法が URL Scheme しかないのだが、公式の方法である openURL:completionHandler: を実行できるExtensionの種類がToday Extensionだけという問題である。

これは世界中でみんな困っていて、ネットで検索するとさまざまな回避方法が出ている。

最終的に、iOS10でも実行できるSwift3の最終的なソースは次のようになった。

//
//  ShareViewController.swift
//  shareExtension
//
//  Created by hishida on 2017/06/14.
//
//

import UIKit
import Social

class ShareViewController: SLComposeServiceViewController {
    
    override func isContentValid() -> Bool {
        // Do validation of contentText and/or NSExtensionContext attachments here

        let canPost: Bool = self.contentText.characters.count > 0
        if canPost {
            return true
        }
        
        return false
    }

    override func didSelectPost() {
        // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
        
        let inputItem: NSExtensionItem = self.extensionContext?.inputItems[0] as! NSExtensionItem
        let itemProvider = inputItem.attachments![0] as! NSItemProvider
        
        if (itemProvider.hasItemConformingToTypeIdentifier("public.plain-text")) {
            itemProvider.loadItem(forTypeIdentifier: "public.plain-text", options: nil, completionHandler: {
                (item, error) in
                
                let URLSCHEME:String = "ebpocket://search?text="

                let encodedString:String = self.contentText.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!
                
                let myUrlStr:String = URLSCHEME + encodedString

                let url = NSURL( string:myUrlStr )
                let context = NSExtensionContext()
                context.open(url! as URL, completionHandler: nil)
                
                var responder = self as UIResponder?
                
                while (responder != nil){
                    if responder?.responds(to: Selector("openURL:")) == true{
                        responder?.perform(Selector("openURL:"), with: url)
                    }
                    responder = responder!.next
                }
                
            })
        }

        // Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
        self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
    }

    override func configurationItems() -> [Any]! {
        // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
        return []
    }

}

開発作業以上に手間取ったのが、App Storeに提出するための証明書の問題だった。

わかってみるとなんでもないが、App Extensionと本体の収容アプリは別のバイナリのため、Application Identifier、Provisioning profileは全て収容アプリとは別にする必要がある。

  1. iOS Dev Centerで Extension用の Application IdentifierとProvisioning profileを作成する
  2. 収容アプリのEntitlements.plistとは別に、App Extension用のEntitlements.plistを作成し、ここでApp ExtensionのApplication Identifierを定義する

ちなみApplication Identifierとは、開発者を表す10桁のprefix + Bundle Identifierのこと:

 XXXXXXXXXX.info.ebstudio.EBPocketPro.shareExtension 

あと、収容アプリと App Extensionのバージョン番号が食い違っていると提出時にワーニングが出るが、これは無視していいらしい。