2016年4月13日水曜日

[WPF C#] リソースについて(まとめ)

.NETやWPFのリソースについてです。わかっている人にはどってことないの話です。どちらかと言うと初心者向けです。ここでいうリソースというのは、文字列や画像、ファイルなどを実行モジュールに埋め込んで、実行に読み込むための仕組みということです。
Visual Studio 2015の操作方法で説明します。

Win32リソース

おそらく、.NETでの開発では、この仕組みは利用することがないと思いますが、念のため。Win32リソースとは、実行モジュール(.exeや.dll)にリソースを埋め込むもので、かつての方法では、.rcファイルから、.resファイルをリソースコンパイラでコンパイルして、リンク時に埋め込みます。詳細については割愛します。

アセンブリマニフェストリソース

この仕組みは、アセンブリ(.NETで開発した.exeや.dll)にファイルを埋め込むものです。ただし、一般的にはこの方法を利用することはあまりないと思います。なぜなら、もっと便利な方法があるからです。

定義方法
ソリューションエクスプローラーで、埋め込もうとするファイルのプロパティを表示します。プロパティにビルドアクションの項目を「埋め込みリソース」に設定します。この方法で埋め込めるのはファイルだけです。あと、出力ディレクトリにコピーの項目は「なし」でよいです。
コードからの利用方法
まず、System.Reflection.Assembly assembly = this.GetType().Assembly;などの方法で、実行中のアセンブリを取得します。アセンブリから、埋め込まれているリソースのストリームを取得します。
System.IO.Stream stream = assembly.GetManifestResourceStream(resouceName);
ここでresouceNameというのは、<名前空間>.<アセンブリモジュールからの相対パス名(区切りは".")>です。
例えば、MyAppという名前空間でプロジェクトの中にResoucesというフォルダを作って、そのなかにMyApp.icoというファイルを埋め込んだ時には、リソース名は、"MyApp.Resources.MyApp.ico"となります。

.resxファイルを使ったリソース

おそらく、.NETでの開発でのリソースといえば、この方法のことをいうことが多いようです。Visual Stuioで.NETのプロジェクトを作成すると、Propertiesというフォルダの中に、Resources.resxというファイルが作成されるはずです。
リソースとしては、ファイル以外にも文字列や画像なども組み込むことができます。.resxファイルはビルド時に.resourcesファイルに変換されて、アセンブリのファイルに組み込まれます。これは、Win32リソースに似ていますが、全くの別物です。また、この仕組みは、アセンブリマニフェストリソースによって実現されており、上記のResources.resxの場合、アセンブリマニフェストリソースでのリソース名は、<名前空間>.Properties.Resources.resourcesとなります。なお、Resources.resx以外のファイルをプロジェクトに追加して利用することも可能です。

定義方法
通常、ソリューションエクスプローラーで、.resxファイルを開くと定義画面が表示されます。もし表示されなかったら、ファイルのプロパティを開いて、カスタムツールの項目が"ResXFileCodeGenerator"になっているか確認してください。この画面の操作方法については、ヘルプ等を参照してください。
コードから利用方法
同一名前空間からの利用であれば、Properties.Resources.<プロパティ名>というオブジェクトが利用できます。型は、定義された適切なものになっています(例:文字列→string)。場合によっては、global::<名前空間>.Properties.Resources.<プロパティ名>で参照しないと見れないかもしれません。

WPF用のリソース

WPFのプロジェクトでは、アセンブリマニフェストリソースに似たリソースが利用できます。これで定義できるのはファイルのみです。ちなみにこの仕組みもアセンブリマニフェストリソースで実現されており、アセンブリマニフェストでのリソース名は、"<アセンブリ名>.g.resources"となります。

定義方法
ソリューションエクスプローラーで、プロパティを開くとビルドアクションに"Resource"という項目があるので、それを選びます。ただし、この項目はWPF専用ということになっています。なお、この場合も、出力ディレクトリにコピーは「なし」です。
コードから利用方法
WPFのAPIからのファイルへのアクセスは基本Uriオブジェクトから参照するので、Uri uri = new Uri(pathname, UriKind.Relative);のようにUriオブジェクトを作成します。pathnameは、アセンブリファイルの相対パスになるのですが、先頭に"/"をつけるのが普通なようです。例えば、Resourcesのフォルダの中にMyApp.icoというファイルを追加して、ビルドアクションを"Resource"にした時には、pathnameは"/Resources/MyApp.ico"となります。
XAMLからの利用方法
XAMLから参照する場合には、上記でいうところのpathnameを設定すればよいです。
余談かもしれませんが、WPFのアプリケーションでアイコンを設定する場合、この方法が良いかと思います。上記の例でアイコンをリソースとして組み込んだ場合、プロジェクトのプロパティでアイコンの設定のところで、コンボボックスにこのファイルが選べるようになっているので、それを選べばよいです。

ビルドアクションの「コンテンツ」

この方法は、リソースとは言わないようですが、実行時にファイルを読み込めるという意味では、いわゆるリソースとして使えます。これは、実行モジュールとともにファイルをコピーするものです。そのため、利用者に普通に見られてしまう可能性があります。

定義方法
ビルドアクションを「コンテンツ」に設定します。この場合には、出力ディレクトリにコピーは「なし」以外を選んでください。
コードから利用方法
WPFアプリケーションででなくても、利用できるのですがWPFだと簡単にできるので、その方法を書きます。
WPFのリソースと同様にUriでファイルを指定するのですが、通常Uri uri = new Uri(uri, UriKind.Absolute);のように指定します。uriは"pack://application:,,,/<実行モジュールからの相対パス>"で指定します。先程と同じように"Resources/MyApp.ico"というファイルをプロジェクトに追加して、ビルドアクションをコンテンツにした場合には、"pack://application:,,,/Resources/MyApp.ico"となります。ただし、場合によっては、"pack://application:,,,/<アセンブリ名>;component/<実行モジュールからの相対パス>"というようにしないと参照できない場合があるようです。上記の例で、アセンブリ名が"MyApp"とすれば、"pack://application:,,,/MyApp;component/Resources/MyApp.ico"となります。
XAMLからの利用方法
WPFのリソースと同様に、上記でいうところのuriを設定すればよいです。

XAMLのリソース

これは、いわゆるリソースではありません。XAMLのリソースというのは、ResourceDirectoryに追加されるものをいうようです。よく使われるのは、Application.ResourceやWindow.Resourcesを使ってキーとともになんだかのオブジェクトと追加して、キーからそのオブジェクトを利用するパターンです。このリソースの詳細については、"Static Resource"や"Dynamic Resource"というキーワードで検索してください。

2014年12月1日月曜日

Android Studioでエミュレータが起動しないとき

小ねたですが、Android Studioをインストールしたてで、ためしにデバッグしようとしたのですが、うまくいかなかったので、それの対処方法です。

とりあえず、ToolsメニューにあるAndroid→AVD Managerを開いてください。その中にデバイスの一覧があるのですが、Actionの項目のところにエラーが出ていたら、ロードできていないので、項目と右クリックしてEditでSystem Imageが表示されるので、Nextボタンが押せるのであれば、押してデバイスの設定をしてください。
押せなかったら、InstallLatestVersionを押して、最新のものすれば、押せるようになるはずです。

設定がうまくいくと、デバイス一覧のActionの項目が実行ボタンと編集ボタンになっているはずです。

あと、起動時に「HAX kernel module is not installed!」というエラーが出たときには、HAXMは、Android SDK Managerでインストールしてだけではだめで、手動でインストールする必要があります。SDKのフォルダの中のextras/intel/Hardware_Accelerated_Execution_Managerにインストーラーがあるので、それを起動します。

あとは、環境にもよると思いますが、起動がやたらに遅いので、OSのプロセス一覧にemulatorがあればたぶん何かやっているので、待ってるしかないです。

2013年9月29日日曜日

[.NET] WebBrowserをDisposeしても、メモリが開放されない件について

そもそも、WebBrowserがメモリリークを起こす件は、随分前からいわれ続けている気がしますが、未だに解決されていないようです。
とりあえず、試しにWebBrowserを貼り付けたフォームを作って、適当なコンテンツを表示するようにしたものを、アプリケーションのメインウィンドウから、Show()で表示させて、そのフォームを閉じるようにしてみましたが、やはりリークが発生しているようです。

いろいろ検索したところによると、Win32APIのSetProcessWorkingSetSize()やEmptyWorkingSet()の利用が効果があるとのことですが、どうやら、これらも機能しないようです。ただ、OSやIEのバージョン、WebBrowserに表示した中味などの条件で機能することもあるようです。
この手の検索をしても、かなりの量がヒットしているところを見ると、やはり、今現在でも決定的な解決方法はないのでは、と思っています。

何がメモリに留まり続けているかというのを調べてみたのですが、パフォーマンスモニタで見てみると、.NET CLR Interopの# of CCWsという項目が、増え続けています。これによって、Bytes in all HeapsやTotal committed Bytesなども増え続けているように見えます。CCWというのは、COMのラッパーのことでなので、CLRから見るとCOMが開放されない状態になっているのではないかと思われます。
WebBrowserがIEの機能をCOMとして利用しているというのは、容易に想像できますが、具体的に何をどう使っているのかはわかりません(本気で追求すればわかりかもしれませんが、とりあえずなので、すみませんw)。
この現象からすると、根本的解決を試みるのであれば、開放されないCOMに対して何か操作するということぐらいしか思いつきません。

そういう状況から考えると、WebBrowserはDisposeするような使い方はしないというのが、無難な解決方法(というか、回避方法)ではないかと、思います。例えば、上記の書いたようなフォームにWebBrowserを貼り付けて表示するのであれば、Show()で表示するのではなく、別プロセスでフォームを表示させるという方法です。プロセスが終了すれば、無条件にメモリは開放されるので、これならリークしようがないはずです。
もし、メインのウィンドウとなんだかの情報のやり取りが必要であれば、IPCが使えます(と、あっさりいいましたが、IPCも曲者でタイムアウトしたり、やたらに遅かったりする場合がある)。

というわけで、もし、WebBrowserのメモリリークでお悩みの方は、どちらかというと残念な解決方法ですが、上記の方法を検討してみたらいかがでしょうか。

2013年2月18日月曜日

[.NET] TcpListenerのよさげな使い方

小ネタですが、TcpListenerのおそらくよさげな使い方についてです。ポイントは、TcpListenerはAccept要求をキューに溜め込む仕様のため、Acceptの処理をすばやく実行しないと、新しい要求が失敗する可能性があるということです。

とりあえずコード(C#)

// TcpListenerの使用例
// クライアントとのデータのやり取りはここでやる(Thread)
void ConnectionProc(object target)
{
    TcpClient tcpClient = (TcpClient)target;
    // ここでtcpClientからストリームを取得してReadとかWriteとかCloseとかを実行する
    // ちなみにクライアント側から接続が切断されると、Readでサイズ0が返ってくる
}
// 接続を受け入れる。
void AcceptConnection(TcpListener tcpListener)
{
    TcpClient tcpClient = tcpListener.AcceptTcpClient();
    Thread thread = new Thread(ConnectionProc);
    thread.IsBackground = true;
    thread.Start(tcpClient);
}
// Acceptを受け入れる処理(Thread)
void AcceptProc(object target)
{
    TcpListener tcpListener = (TcpListener)target;
    while (true)
    {
        try
        {
            while (tcpListener.Pending())
            {
                AcceptConnection(tcpListener);
            }
            AcceptConnection(tcpListener);
        }
        catch (Exception)
        {
            // TcpListenter.Stop()が呼び出されるとSocketExceptionがThrowされる
            break;
        }
    }
}

//  Acceptを開始する処理
// tcpListenerはどこかで作成されているとする
tcpListener.Start();
Thread thread = new Thread(AcceptProc);
thread.IsBackground = true;
thread.Start(tcpListener);

正直言ってwhile (tcpListener.Pending()){…}のコードはロジック的にはなくても同じに思えるのですが、どうやらこのように書いておくと、Acceptを処理しているときに直ぐに別な接続が来た場合、早く応答するようです。
もし、TcpListenerを使っていて、クライアントからの接続に失敗するようだったら、このような方法を試してみるのがいいかもしれません。

2012年7月2日月曜日

[C# .NET] System.Speech.Synthesisを使って音声合成を試してみた

.NETにSystem.Speech.Synthesisという名前空間があり、これを利用するとテキストを音声で出力することができます。OSによって異なるかもしれませんが、SAPI5を使用しているようです。そのため、SAPI5に対応した合成エンジンをインストールしないと、音声合成を利用することができないということになります。

現在のところ、MSからはMicrosoft Speech Platformのエンジンが提供されているのですが、このままだとSAPIには対応していないようです。ただ、レジストリを書けばSAPIで利用できるようになるようですが、ここではその方法は紹介しません。

エンジンが使えるかどうかチェック

まず、音声合成エンジンが使えるかどうかということですが、
SpeechSynthesizer.GetInstalledVoices(CultureInfo);を使って、インストールされている声を取得します。指定するCultureInfoは、基本的にはアプリケーションの現在のカルチャでいいと思います。その場合、Formアプリケーションであれば、System.Windows.Forms.Application.CurrentCulture プロパティを使います。このプロパティはstaticなので、どこからでも参照できます。また、カルチャを決め打ちしたいのであれば、CultureInfoのコンストラクタで適切な値を渡して、オブジェクトを作成すればよいと思います。

そうすると、ReadOnlyCollection<InstalledVoice>が得られるので、このコレクションを見て中に何もなければ、指定したカルチャでは音声合成が使えないということになります。

音声合成を出力する

通常、文章を音声に変えてサウンドデバイスから出力できればよいと思うので、その方法を説明します。以下がC#のコードです(名前空間への参照をusingを使って指定しておいてください)。

SpeechSynthesizer speechSynthesizer = new SpeechSynthesizer();
CultureInfo cultureInfo = Application.CurrentCulture;
ReadOnlyCollection<InstalledVoice> voices = speechSynthesizer.GetInstalledVoices(cultureInfo);
//ここでvoices.Countが0なら使える声がない。
InstalledVoice voice = voices[0];
speechSynthesizer.SelectVoice(voice.VoiceInfo.Name);
string text; //音声合成させたい文章
speechSynthesizer.Speak(text);
というのが、簡単な方法だと思います。あと、非同期のSpeechSynthesizer.SpeakAsyncというメソッドがあるので、必要であればこちらを使ったほうがいいと思います。

SSMLを使って出力する

SpeechSynthesizer.SpeechSynthesizer.SpeakSsmlまたは、SpeechSynthesizer.SpeakSsmlAsyncというメソッドが用意されており、これを使うと音声を制御しながら、出力することができます。SSMLについては、W3Cで勧告されているので、詳細はそちらを参照していただきたいのですが、基本的にはspeak要素の中に、出力したい文章をいれておけば大丈夫です。ただし、言語の指定もSSMLの中で指定しないとダメなようです。

C#のコードについては、上記のコードのspeechSynthesizer.Speak(text)をspeechSynthesizer.SpeakSsml(text)に置き換えればいいのですが、SelectVoiceメソッドの呼び出しは意味がないようです。

以下が単純なSSMLです。

<?xml version="1.0" encoding="utf-8" ?>
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.w3.org/2001/10/synthesis
                   http://www.w3.org/TR/speech-synthesis/synthesis.xsd"
         xml:lang="en-US">
  <voice xml:lang="ja">
  <!-- ここに文章を書く -->
  </voice>
</speak>
文章は、HTMLのようにp要素でくくっておいたほうが、それらしいと思います。ただ、なくても出力はされます。

あと、ピッチ(声の高さ)レート(話速)ですが、SSMLを使う場合には、prosody要素を使って指定すればよいです。あと、読みをがうまくいかなくて、発音を明示的に指定する場合には、phoneme要素で設定することがきます。このとき、ph属性で発音を指定するのですが、いわゆるローマ字表記で指定できます。発音記号でもできるようですが、試してはいません。

2011年10月30日日曜日

[C# .NET] ウエイトカーソルを表示する方法

小ネタですか、.NETフォームアプリケーションで、いわゆるウエイトカーソルを表示させたいときの処理についてです。

Application.UseWaitCursorとControl.UseWaitCursorのプロパティですが、これらはいわゆるメインスレッドの制御を止めてしまいます。そのため、メインスレッドからUseWaitCursorを変更するのは適切ではありません。別スレッドからメインスレッドをどうしても止めたいときに使うことを想定しているようです。

そのため、単純にメインスレッドで、どうしても時間がかかってしまう処理をする場合には、Form.Cursorプロパティを変更するが適切でしょう。
処理の前にForm.CursorにCursors.WaitCursorを設定して、終了後にCursors.Defaultに変更するという処理になります。

そもそも、メインスレッドで処理をしているので、アプリケーションはフリーズ状態なので、カーソルを変更するだけでよいということになります。

2010年10月9日土曜日

[.NET] C#ジェネリック IEnumerableの実装

小ネタですが、IEnumeratable(T)の実装についてです。
とりあえず、ソースコードです。

using System.Collections.Generic;
using System.Collections;

class MyClass<T> : IEnumerable<T>
{
    public int Count { get { return ...; } } //データの数
    public T this[int index]
    {
        get { return ...; } //indexを使ってデータを返す
    }
    public IEnumerator<T> GetEnumerator()
    {
        for (int i = 0; i < Count; i++)
        {
            yield return this[i];
        }
    }
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}

IEnumeratorがジェネリックにも存在するので、変な記述に見えるのかもしれません。
混乱をさせるためにはusingを使わずにIEnumeratorを書けばよいかもしれません。

2010年4月20日火曜日

[PHP]ZendDebuggerからXdebugに乗り換えてみた

EclipsePDTと古いZendDebuggerの相性がよろしくなさそうなので、Xdebugに乗り換えてみました。Windows上でPHPのバージョンは都合により5.2を使ってます。

Xdebugのインストールは、xdebug.orgより適切なモジュール(DLL)をダウンロードします。DLLは、適当なフォルダにコピーした場合には、php.iniに「zend_extension_ts=<DLLのフルパス>」と指定し、PHPのextフォルダに入れたときには「DLLのファイル名」と指定すればよいです。

php.iniの設定はリモートデバッグを有効にするのと、リモートホストの設定だけでよいです。リモートホストの設定は1つだけしかできないようです。基本的にはローカルにあるサーバーにPHPとXdebugを入れてデバッグするという運用が想定されているものだと考えられます。

xdebug.remote_enable = On
xdebug.remote_host = localhost

リモートホストの設定は、XdebugとEclipseとの間の設定なので、Webサーバーがバーチャルホストでホスト名で参照してあっても、ローカルにあるならば「localhost」と書けばよいようです。
あとは、Eclipseのデバッグの設定で、サーバーのデバッガの設定を「XDebug」にして、「Break at First Line」はオフ(チェックしない)に設定したほうがよいようです。

基本的にはこれでデバッグできますが、Eclipseのデバッグの設定で、ファイルのmappingが正しくないとブレイクポイントで停止などの操作ができません。PHPサーバーの設定にあるPath Mappingのところにプロジェクトとサーバー上のファイルパスを設定すればよいです。
EclipseのプロジェクトがローカルWebサーバーのDocument Rootの配下あるならば、プロジェクトのファイルパスと同じになるはずので、Path on Serverのところはプロジェクトのファイルパスを手で入力すればよいでしょう。
もし、Path Mappingのところがうまくいかないときは、php.iniに「xdebug.remote_log=<ログファイルのフルパス>」を設定して、ブレイクポイントをどう設定しているかを確認してみるとよいようです。

あと、ZendDebuggerと違うところは、Xdebugの場合にはデバッグを開始するとセッションを張りっぱなしになります。そのため、1度デバッグを開始すればセッションを見失わない限りEclipseのデバッガと連動し続けます。
動きもXdebugのほうが軽いのですし、いまのところ安定して使えてます。ただ、変数の中身の表示され方がちょっと違ったりするのですが、慣れれば問題ないでしょう。

2010年3月28日日曜日

[PHP]とりあえず、CakePHPを使ってみる

CakePHPを使ってそれなりのものが作れるようになったので、CakePHPの基本的なことをまとめてみます。CakePHPのマニュアル(The Cookbook)を読んでいるというを前提に、ある程度スキルがある人向けな内容かもしれません。

インストール

マニュアル通りの手順でいけるはずです。強いていえば、apacheのディレクトリの設定で「AllowOverride All」にするのを忘れないことです。あと、app/webrootをドキュメントルートにせずにどこかのフォルダにマップして使いたいと思うかもしれませんが、できなくはないですが少々面倒なことになると思います。

Eclipse PDTで開発

最初はあまり凝ったことを考えずに、まず適当なフォルダにワークスペースを作って、そのなかにCakePHPに含まれているフォルダ(app,cake,venders)と同じ名前のPHPプロジェクトを作って、そのなかにそれぞれをインポートします。appプロジェクトにあるwebrootのディレクトリをドキュメントルートに設定されたバーチャルホストを作成すればよいでしょう。
なお、配布されているアーカイブの直下にあるファイルは見る必要はあるかもしれませんが、開発には必要ではありません。

開発を始める前に

ここで、開発に取りかかりたいところですが、まずはマニュアルの規約のところを読んだほうがよいです。CakePHPですばやく開発できるカラクリには、命名規約が大きく関わっているからです。

CakePHPのMVC

マニュアルのどこかにMVCの概念を使っていると記述されていたと思いますが、ターゲットとなるアプリケーションはデータベースを利用するWebアプリとなるはずなので、MVCのそれぞれは以下のようなものになると思います。一応説明しますが、軽く流してもらってよいです。

モデル
CakePHPのモデルは、テーブルにあるレコードにアクセスするためのクラスを意味します。もし、テーブルの仕様が規約にそっているならば、$nameがあれば十分ですが、カラムのルールとリレーションの定義もします。ルールは「Data Validation」の項、リレーションは「モデルを結びつける」の項を参照すればよいでしょう。
ビュー
ビューは、出力される形式(通常はHTML)を出力するPHPコード(拡張子ctp)を意味します。このコードではHTML全体を出力するのではなく、bodyに含まれる一部を出力します。外側はレイアウトで定義します。
コントローラ
コントローラは、ウェブサーバから来たリクエストを受け取って処理するところになります。基本的にはモデルからデータを取り出してビューに渡したり、フォームで入力されたデータをモデルを使って書き込むといった流れになります。

それぞれの関係について

あとは、規約にそってファイルを作成し、クラス定義もしくはコードを書いていくわけですが、それぞれの関係がわかってないとうまく動きません。それらを簡単に説明します。

コントローラとモデルの関係
モデルに対応するコントローラのオブジェクトには、モデルのオブジェクトが存在します($this-><モデル名>;)もしリレーションを定義しているのであれば、そのオブジェクトの中に関連付けされたモデルが存在します($this-><モデル名>-><関連しているモデル名>;)。それらのモデルのオブジェクトを使ってデータ(テーブルにあるレコード)を操作します。
コントローラとビューの関係
コントローラに定義するメソッドとビューのファイル名は規約により結びついていて、コントローラからビューにデータを渡すには、$this->set();を使って変数に設定してビューで参照ます。
ビューとモデルの関係
モデルでValidationを定義して、ビューでフォームヘルパー($form)を使って$form->input();で入力させるHTMLフォームを定義すると、ルールに合わないデータが入力されるとエラーが表示されるようになります。
リクエストとコントローラの関係
CakePHPでは、Webサーバ上でのリクエストは「/<コントロール名>[/<アクション名>[/<パラメタ>]]」となります。コントロール名はコントローラのファイル、クラス名に対応し、アクション名は、コントローラのメソッド名、ビューのファイル名に対応します。また、デフォルトのアクション名はindexです。
また、フォームで入力されたデータは$this->dataに格納されます。
コントローラとレイアウトの関係
コントローラの中で$this->layout変数かsetLayout();にレイアウト名を設定するとそれに対応するレイアウトのファイルが利用されます。
ビューとレイアウトの関係
レイアウトの中にレイアウト用に使われる変数($content_for_layoutなど)を出力するように定義します。ビューの出力などはそららの変数に入っています。詳しくはマニュアルを参照してください。

基本的な手順

開発の手順としては、まずテーブルを定義してモデルを作成します。既存テーブルを利用する場合には、モデルでカラムの定義が必要になるかもしれません。ただし、idとなるPRIMARYKEYがないとCakePHPでは操作できないようです。
あとは、操作しようとするモデルのコントローラとアクションとなるコントローラのメソッドとビューを作成します。まずはデフォルトのindex();メソッドとindex.ctpを作成するでしょう。
レイアウトは、必要最小限のものを/app/views/layouts/defualt.ctpに書いておいたほうがいいかもしれません。このとき、$html->css();で外部ファイルを読み込む場合には、webrootのcssフォルダを参照します。

2010年3月18日木曜日

[VC#] ClickOnceのマニフェストに署名するためのテスト用の証明書を作成する

VC#のプロジェクトのプロパティでClickOnceのテスト用証明書を作成することができるのですが、証明書の内容が気に入らない(発行者とか期限など)場合、別な方法で証明書を作成して読み込ますという話です。

まず、証明書を作成するためにmakecert.exeというツールが必要なのですが、Expressをインストールした場合には、存在しないようです。Windows SDKに含まれているのでインストールします。開発環境によりますが、現在のところ「Windows SDK for Windows Server 2008 and .NET Framework 3.5」というのをインストールするのがよいと思います。

まずは、makecertでpvkとcerファイルを作ります。makecertのコマンドオプションは、msdnのドキュメント等にありますので、詳細はそちらを読んでください。おそらく、最小限で必要なものは、以下のようになると思います。

makecert -r -pe -n "CN=MyCompany" -sv MyCA.pvk MyCA.cer

次に作成したpvkとcerファイルからpfxファイルを作ります。これはpvk2pfxというコマンドを使います。これもWindows SDKに含まれています。このコマンドを使ってClickOnceの証明書として取り込むpfxファイルを作るときには、1つ注意点があります。それは、-poオプションを使って、出力用のパスワードを設定しておくことです。pvk2pfxコマンドの実行例は以下の通り。

pvk2pfx -pvk MyCA.pvk -spc MyCA.cer -pfx MyCA.pfx -po "パスワード"

このコマンドを実行したときにパスワードの入力を求められますが、それはmakecertで作成したときのパスワードです。-poで設定したパスワードは、VC#などでファイルを取り込むときに求められるパスワードとなります。

取り込みが成功すれば、プロパティ表示の中にmakecert作成時に設定したオプションの内容が表示されます。

2010年3月10日水曜日

[.Net ミニメモ] DataGridViewにBindingSourceをバインドしたときに表示がおかしいとき

DataGridViewにBindingSourceをバインドして、同期して表示や編集をしているときに、列やセルの選択の表示がおかしくなっている場合があります。
そのときには、バインドしているBindingSourceを操作しているスレッドがメインスレッドになっているか確認してみてください。通常のUI部品への操作と違って、例外が発生せずにそのまま走りますが、動きが変になるようです。

具体的には、InvokeRequiredでメインスレッドでないかを確認して、必要であればInvoke()を使ってメインのスレッドで動くようにしてください。ありがちなC#のコードは以下ようなものです。

private void exampleHandler(object sender, EventArg e)
{
    if (InvokeRequired) {
        object[] args = { sender, e };
        Invoke(new EventHandler(exampleHandler), args);
        return;
    }
    // 以下に処理したいコードを書く
}

2010年2月16日火曜日

[ミニメモ] PHP5とMySQLのインストール

小ネタですが、忘れないようにメモしておきます。

WindowsでPHPでMySQLiとかのモジュールを利用するとき、MySQLにインストールされているライブラリを参照します。 そのため、MySQLがインストールされているフォルダにPathを通しておかないと、Apache起動時に失敗します。

具体的には、システムのPATH環境変数にMySQLのbinへのパスを追加して、Windowsを再起動します。

2009年8月17日月曜日

[.NET] Windowsフォームでのダイアログの作成と終了処理について

Windowsフォームでダイアログを作成するには、普通にFormを作成してFormBorderStyleやMaximizeBoxやMinimizeBoxのプロパティを変更して、ダイアログの概観を持つフォームを作成します。またモーダルダイアログとして表示するには、ShowDialog();を呼び出します。

OKやキャンセルなどのいわゆるダイアログボタンは、普通にボタンを配置してDialogResultプロパティを設定し、それに合うような文字列をTextプロパティに設定します。DialogResultが設定されたボタンを押すと自動的にFormを閉じようとするので、Clickイベントハンドラでは、Close()を呼び出す記述してはいけません。

DialogResultが設定されたボタンとウィンドウクローズボタン(右上の×ボタン)を含めて、これらのボタンを押されたときに終了させないようにするには、FormClosingイベントハンドラを登録して、FormClosingEventArgsのCloseにfalseを設定します。この処理は、ShowDialog();で表示するFormに限らず、一般的なFormについても同様です。

プログラムからShowDialog();で表示したFormを閉じるには、Visibleプロパティをfalseにします。そうすると、ダイアログが非表示になり、ShowDialog();が復帰します。このときの戻り値は、FormのDialogResultプロパティに設定されているものになります。そのため、Visibleを設定するときにDialogResultを設定しておいたほうがよいでしょう。

2009年7月9日木曜日

[VC++] ミニメモ

自分で作ったnamespaceやclassの「名前空間が見つかりません」のエラーが出たときは、一番最後のエラーメッセージを見よ。

どこかでnamespaceを閉じ忘れているかも…。

2009年7月6日月曜日

[VC++2008] VC++ 2008 Expressでアセンブリに署名をする

VC++ 2008 Expressでアセンブリを遅延署名をしてから、署名する方法です。署名をするためのキーについては、ドキュメントの「公開キーと秘密キーのキー ペアを作成する 」や「厳密名ツール (Sn.exe) 」についてを参照してください。

遅延署名をする

assemblyInfo.cppに遅延署名をするアトリビュートを追加します。ここでは、「AssemblyKeyFileAttribute」の指定はしません。

[assembly:AssemblyDelaySignAttribute(true)];

リンカオプションでパブリックキーを設定する

要するに「/KEYFILE:」のオプションを設定するのですが、プロジェクトのプロパティで「リンカ」の中の「詳細」の一覧の中に「キーファイル」という項目があるので、そこに公開キーファイルを指定します。ファイルのありかによっては、マクロを使ってパスを設定する必要があるかもしれません。

ちゃんと署名をする

遅延署名のままだとデバッグすらできません。デバッグなら一時的に署名のチェックを外すこともできますが、リンクが終わった後に署名します。
カスタムビルドステップで、コマンド(sn.exe)を使って署名をします。基本的には以下のようにコマンドビルドステップのコマンドラインを設定すればよいでしょう。

"$(WindowsSdkDir)bin\sn" -R "$(TargetPath)" <キーペアファイル>

なお、キーペアファイルの指定でフルパスになるようでしたら、二重引用符「"」でくくる必要があります。

2009年6月28日日曜日

[VC++2008] VC++ 2008 ExpressではApplicationSettingsは使えないらしい

VC++ 2008 ExpressnのIDEのフォームデザイナを使ってフォームを作成するとき、コンポーネントのプロパティにあるデータというカテゴリに「(ApplicationSettings)」という項目がある。ApplicationSettingsの詳細についてはドキュメントに譲るが、これを使うとフォームの位置などの情報を簡単に保存して、起動したときに前回の状態と同じように表示させることができる仕組みである。

まず、おもむろにこの「(ApplicationSettings)」にある「(Property Binding)」という項目をクリックすると、設定画面が表示できるボタンが現れる。このボタンを押すと、アプリケーション設定というダイアログが表示されるのだが、この中にはバインド可能なプロパティの一覧が表示されている。
そのプロパティのコンボボックスをドロップダウンさせると、アプリケーション設定を定義することができる。ここで「新規を」クリックするとアプリケーション設定が作成できるのだが、項目を埋めて「OK」ボタンを押すと「設定ファイルがプロジェクトに見つかりません。設定ファイルを追加して、再試行してください。」と表示されて追加することが出来ない。

ここで見つからないといわれているファイルは、「app.settings」である。このファイルもxmlで記述されていて、マイクロソフトでスキーマが定義されている。このファイルの空の状態は、おそらく以下の通り。

<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="" GeneratedClassName="app">
</SettingsFile>

このapp.settingsファイルをプロジェクトに追加すれば、(Property Binding)にアプリケーション設定を定義することができる。また、このapp.settingsファイルを開くとアプリケーション設定用のビューが開き、そこで項目の追加や変更もできる。

ただし、この状態でビルドをすると「stdafx.h」に「app.h」がありませんというコンパイルエラーが発生する。どうやら、app.settingsから値をやりとりするためのクラスをapp.hに定義してあるものとしてビルドしているように思える。
実際にはアプリケーション設定を定義したフォームのコードをみると「::app::Default」という参照をしている。結局のところ、このクラスを定義しないとアプリケーション設定は利用できないということになる。

ここからは、試してないのでなんともいえないが、このapp.hを手で作成してみようとするのであれば、ドキュメントにある「アプリケーション設定アーキテクチャ 」というページをヒントにすれば、うまくいくと思われる。
おそらく、appというクラスを「ApplicationSettingsBase」から派生させて、その中には先ほどのアプリケーション設定のビューで定義した名前と型をプロパティを作成する。スコープがユーザーなら「UserScopedSetting()」アトリビュートで、アプリケーションなら「ApplicationScopedSetting()」アトリビュートとなる。デフォルト値は、「DefaultSettingValue()」のアトリビュートで設定すればよいだろう。
プロパティのget()とset()は、ApplicationSettingsBaseのインデクサの参照、代入となる。

あとは、フォームを閉じるときにファイルに保存する(ApplicationSettingsBase::Save()を呼ぶ)ようにすれば、状態が保存されるはず。
app.hの作成のところは、試してないので動作保障はいたしません。もし、試してうまくいったという人がいらしたら、お知らせしていただければうれしいですw。

2009年4月2日木曜日

【Windows】Eclipse3.4.2(Ganymede)+PDTでZendDebuggerを使う

WindowsでApache2.0でPHP5.2が動作して、Eclipse GanymedeにPDTが組み込まれているという環境で、ZendDebbuggerを組み込む方法についてです。Webサーバーとデバッグするマシンは同じである必要はありません。

ApacheにZendDebuggerを組み込む

ZendDebuggerの入手
http://downloads.zend.com/pdt/server-debugger/にいく。ZendDebugger-5.2.xx-cygwin_nt-i386.zipをダウンロードする(基本的には最新版でよいはず)。
インストール
Zipを展開すると?_?_x_compのようなフォルダがあるが、最初の2つの数がPHPのバージョンを意味しているので5_2_x_compか5_2_x_nts_compのいずれかのフォルダにあるDLLをどこかにコピーする(展開されたフォルダーごとどこかにおいてもよい)。
5_2_x_compと5_2_x_nts_compの違いは、スレッドセーフか非スレッドセーフであり、どちらを組み込むかは、PHPのビルドによって異なる。phpinfo();で出力された中に「Thread Safety」という項目が「enable」で「Debug Build」が「no」であれば、スレッドセーフであり、いずれかがそうでなければ非スレッドセーフということになる。PHPをバイナリでインストールした場合には、スレッドセーフになっているはず。
php.iniの設定
以下の設定をphp.iniに追加する。
zend_extension_ts=<コピーしたDLLのパス>ZendDebugger.dll
zend_debugger.allow_hosts=<アクセスを許可するIPアドレス>
zend_debugger.expose_remotely=always
もし非スレッドセーフなDLLを組み込むときは、「zend_extension_ts」は「zend_extension」となる。
IPアドレスの設定は、複数ある場合カンマ「,」で区切る。
dummy.phpのコピー
展開されたフォルダにあるdummy.phpをApacheのドキュメントルートにコピーする。このファイルは、PDTのデバッグの設定で「TestDebugger」で接続テストをするときにアクセスする。本質的には必須ではない。

設定完了後にApacheを開始または再起動し、エラーログをチェックする。

Eclipseの設定

Webサーバーの設定
Eclipseの設定ダイアログ(Preferencesで表示される)の「PHP」の「PHP Servers」の項目を表示して、デバッグのターゲットとなるWebサーバーを指定する。
または、「PHP」か「PHP Debug」のパースペクティブを表示にあるデバッグのダイアログ(Debug Configurationで表示される)を表示して、「PHP Server」をいう項目を変更または追加する(デフォルトはlocalhostになっている)。
接続の確認
上記のデバッグのダイアログで「Test Debugger」というボタンを押すと接続確認ができる。ただし、dummy.phpをドキュメントルートにコピーしておかないとテストはできない。
デバッグファイルの設定
デバッグのダイアログで、対象となるファイルを指定する。ここでは、ローカルなファイルを指定するので、普通はプロジェクトのフォルダからの相対パスとなる。
ドキュメントルートからのパスの関係がプロジェクトのパスと一致していれば、URLの設定は「Auto Generate」でデバッグできることになる。すなわちこれは、Apacheのドキュメントルート上にPHPのプロジェクトを作成したという状態を意味している。
デバッグの確認
あとは、デバッグのダイアログで「Debug」ボタン(Runの設定の場合「Run」ボタン)を押すとApache上で実行される。
このとき、表示をEclipse上ではなく別のブラウザ上に表示することができる。設定するには、PHPのパースペクティブで、「Window」のメニューで「Web Browser」で変更するか、設定ダイアログの「General」の「Web Browser」で変更することができる。

2008年12月16日火曜日

[VC++ 2008 Express] Formのnamespaceを変更するとランタイムエラーが出る

VC++ 2008 Express Editionでフォームアプリケーションを作成すると、formのソースコードにnamespaceが定義される。このnamespaceはプロジェクト名になっているのであるが、変更したくなることもあるだろう。

namespaceを変更してもビルドも成功するのであるが、実行時に「指定されたカルチャまたはニュートラル カルチャに対して適切なリソースが見つかりませんでした。…」のようなエラーが発生する。
このエラーの発生しているのは、formのなかでリソースを読み取るところ、実際のコードでは「System::ComponentModel::ComponentResourceManager^ resources = …」となっている場所になる。

なぜエラーになるかというと、ComponentResourceManagerを作成するところで、typeidをベースに読み取るのであるが、typeidはnamespaceに依存する。すなわち、<namespace>.<クラス名>となっている。
しかしながら、コンパイルされたリソース(マネージリソース)は、ソースコードで変更されたnamespaceが反映されないため、ランタイム時にリソースを読み込むことができずエラーとなるという仕組みなようだ。

そこで、マネージリソースのコンパイル設定を変えればうまくいくはずで、プロジェクトのプロパティでは、リソースファイル名の設定が「$(IntDir)\$(RootNamespace).$(InputName).resources」となっている。
すなわち、「$(RootNamespace)」の部分が変更したnamespaceになっていればよいということになる。

ひとつの解決方法としては、RootNamespaceの値の参照をやめて、変更したnamespaceに変えてしまうというのがある。要するに「$(RootNamespace)」を書き換えるということである。

どうせなら、RootNamespaceの値を変更したほうがよさそうだと思うのだが、IDE上で変更できそうな画面が見つからない。仕方がないので、プロジェクトの定義ファイル(.vcproj)をテキストエディタで変更する。変更箇所は簡単に見つかるはずで、RootNamespace="…"となっているところを書き換える。

といっても、ある意味無理やり変更しているので、IDE上でなんだかの不都合が生じる可能性もあるので、あくまでも自己責任の範囲でやってみてください。

2008年11月28日金曜日

[VC++ 2008 Express] FormのresXにリソースを追加しても消されてしまう

VC++のフォームアプリケーションで、フォームとは無関係なリソース(例えばイメージ)をそのフォームのresXに追加することはできるのですが、何かの拍子に消えてしまいます(おそらく、フォームデザイナがヘッダーファイルを生成ときのタイミングだと思います)。

VC++のフォームプロジェクトにあるフォームは、ソリューションエクスプローラー内においては、ソースコードは「<フォーム名>.h」でその下の階層に「<フォーム名>.resX」という表示になっています。このresXを開くとリソースの一覧が表示されて、編集可能となっています。そこで、イメージなどのリソースを追加できるのですが、何故か消えてしまうのです。

そこで、フォームとは別なところにリソースを定義して、プログラムで読み取ろうということですが、少なくとも「rcファイル」はmanagedなプロジェクトでは意味がありません。なぜ、Formアプリケーションのプロジェクトに存在しているかはわかりませんのは、おそらくアプリケーションのアイコンの設定するためなのだが、.NETのドキュメントを読むとrcファイルのリソースは利用できませんと書いてあります。

managedなモジュール(Assemblyといったほうが適切か…)にこの手のリソースを組み込む方法はいくつか存在するようですが、新たにresXファイルを追加してそこにリソースを定義するという方法をやってみました。

空のresXファイルを作る

プロジェクトに組み込むresXファイルを用意します。resXといっても所詮XMLファイルなので、ベタな方法として「メモ帳」で作ることにします。ただし、中身がresXの定義に則してないとIDE上で編集できないですし、ビルド時にエラーになるので、最低限の定義だけを書いておきます。

必要な部分だけ、プロジェクト内にあるresXからコピペするのですが、まず、プロジェクトにある(エラーになっていない)resXファイルをXMLエディタで開きます(コンテキストメニューから「ファイルを開くアプリケーションの選択」というのを選ぶ)。そのresXファイルから、最初の「<?xml version="1.0" encoding="utf-8"?>」から、「</resheader>」の部分まで(たぶん、「<resheader name="writer">」の対になっているのが最後のなので、そこまでをコピー、ペーストします。そのあとの行に「</root>」でrootを閉じればOKです。
ファイルは、拡張子をresXにしてUTF-8で保存します。拡張子よりも前の部分(いわゆるbasename)はプログラムで読み込むときに指定するので、わかり易いものにしておいたほうがよいでしょう。

プロジェクトに追加する

作った空のresXファイルをプロジェクトのコンテキストメニューで「追加→既存の項目」で追加すると、ソリューションエクスプローラー上でプロジェクトの下の「リソースファイル」というツリーの中に現れるはずです。そうしたら、そのresXファイルを開くと、resX用のリソース定義用のビュー(マネージリソースエディタ)が表示されます。
そこで、好きなリソースを追加してください。

リソースの読み込み

ManagedなC++のコードの中でリソースを読み込む方法ですが、まずResourceManagerでresXに定義されたリソースを読み込みます。resXファイル名のbasenameを「リソース名」ということにすると(「hoge.resX」なら「hoge」)。

//アセンブリを取得する
System::Reflection::Assembly^ assembly = System::Reflection::Assembly::GetExecutingAssembly();
//リソースを読み込む
System::Resources::ResourceManager^ resources = 
 gcnew System::Resources::ResourceManager("<アセンブリ名>.<リソース名>", assembly);

ここでの「アセンブリ名」といっているのは(この言い方が正しいかは不明)、普通はプロジェクト名になっているのでプロジェクト名にします。

あとは、ResourceManagerなどのドキュメント、サンプルを見ると使い方がわかりますが、例として、イメージを読み込む場合は以下のような感じになります。

//リソースからイメージを読み込む
System::Drawing::Bitmap^ image = (System::Drawing::Bitmap^)(resources->GetObject(L"<リソースオブジェクト名>"));

ここでの「リソースオブジェクト名」は、resXの定義で命名したものを指定します。マネージリソースエディタでイメージファイルを読み込むとファイル名のbasenameになりますが、エディタ上で変更できます。

この場合、リソースファイルを手動で追加しましたが、いわゆる国際化(カルチャー毎の設定)も対応しているはずなので、ファイル名にカルチャー名を付加したresXファイルを用意しておけば、ResourceManagerで適切に処理されるはずです。

2008年11月26日水曜日

[.NET]バインドされたDataGridViewに追加した項目を選択状態にする

先ほどの記事でDataGridViewとBindingSourceおよびDataTableの関係について書いたが、それを調べるための目的は、DataGridViewにバインドされたDataTableにプログラムで行を追加したときにその項目を選択させるためだったので、その方法を書いてみることにする。

実際にこのような動作をするプログラムを書かないと、状況がつかめないかもしれないが、DataGridViewは、バインドされたDataTableに新しく行を追加しても、選択位置は一番最初のままである。そのため、新しく追加された項目は、スクロールアウトされていると表示されない。しかも、DataGridViewは選択されている項目を表示させようとするため、さらに行が追加されると、選択されている行がスクロールアウトされていても選択行を表示させるためスクロールする。

すなわち、新しい行を表示させるためにはDataGridViewの選択行を変更しなければならない。ここで前提としては、DataGridViewの選択モードは、FullRowSelectになっているものとする。CellSelectでもうまくいくと思われるが試してはいない。

以下のコードはC++で書くことにするが、VC++2008 Express EditionでDataTableやDataSetをデータデザイナを使って定義する方法は、こちらの記事を参照されたい。

まず、データデザイナで定義をするとヘッダファイルにDataSetのサブクラスが作られて、その中にDataTableのサブクラスを定義するようなコードを生成するが、ここでは、"MyDataSet"や"MyDataTable"のように"My"+クラス名というものを定義したとする。

あと、DataTableの定義の前提として、主キーでAutoIncrementされる列(Column)を定義し、カラム名は"ID"とする。あと、定義されたDataSet(すなわち"MyDataSet")は、Formのメンバー変数として、「MyDataSet^ myDataSet;」と定義され、コンストラクタのどこかで、「myDataSet = gcnew MyDataSet();」のようにインスタンスを生成しており、また、フォームデザイナでは、BindingSourceを"myBindingSource"と定義したとする。

とりあえず、コードは以下の通り

//新しい行を生成する
MyDataSet::MyDataTableRow^ newRow = myDataSet->MyDataTable->NewMyDataTableRow();

//ここでnewRowのメンバー(カラム)にデータを代入する
//newRow-><カラム名> = …;
//カラム"ID"は追加時に設定されるので何もしなくてよい

//行をDataTableに追加する
myDataSet->MyDataTable->Rows->Add(newRow);
myDataSet->MyDataTable->AcceptChanges();

//追加した行のBindingSourceの位置を取得する
int position = myBindingSource->Find(L"ID", newRow->ID);

//BindingSourceの現在の位置を変更する
myBindingSource->Position = position;

ここでの最大のポイントは、BindingSourceの現在の位置はDataGridViewの選択行に一致するのだが、項目がソートされているとDataTableの行(Row)のインデックスとは一致しない。そのため、新しく追加した項目がBindingSourceでどの位置にあるかを調べてから設定するということである。

あと、このコードでの"newRow"はAdd();で"MyDataTable"に追加すると、"ID"にAutoIncrementされた値が代入される。そのため、この値を使って"myBindingSource"のFind();を呼び出せば、位置がわかるということになる。