.netframeworkの最近のブログ記事

ちょっとはまったので書いておく。

あるサイトにHttpWebRequestでデータをPOSTしようとした。
C#の内部文字コードはUTF-8 送り先はEUC-JP

なので
1.送りたい文字列をEUC-JPに変換
2.1で変換したものをURLエンコード
でいけると思った。

具体的に書くとこんな感じ

//まず文字コードを変換
string data = "日本語の文字列";
byte[] bytes = Encoding.UTF8.GetBytes(data);
string data_euc = Encoding.GetEncoding("EUC-JP").GetString(bytes);
//URLエンコードする
string data_euc_urlencode = HttpUtility.UrlEncode(data_euc));

が 文字化けする

うーんといろいろググった結果 HttpUtility.UrlEncodeの第2引数に文字コードの指定があることが発覚

んじゃあと上のソースの最終行を変更して見るも変わらず。。。
//URLエンコードする
string data_euc_urlencode = HttpUtility.UrlEncode(data_euc,Encoding.GetEncoding("EUC-JP")));


ってかURLEncodeって確か正規表現で1行で書けるような一律置換だから文字コード関係ないはずじゃ・・・
ということで 試しに文字コード変換なしでUrlEncodeの際に文字コードをしてみた。
具体的にはこんな感じ

string data = "日本語の文字列";
//URLエンコードする
string data_euc_urlencode = HttpUtility.UrlEncode(data,Encoding.GetEncoding("EUC-JP")));


めでたく無事解決しました♪
URLエンコードに文字コード変換機能がついてたわけですね。

perlとかじゃありえない ありがた迷惑機能 親切機能だ(笑)

とりあえず無事解決してよかった
現在運用中のWebサービス。
最近バグ報告もあがって来ないからほぼほったらかしで 記憶から消えかけてたんだけど・・・
突然 エラーになる、、、って連絡が(汗

ここ1ヶ月いじってないのになんでやねん!
なんかで間違えて更新しちゃった?

試してみると確かにエラーになる。

むむむ。。。
何かで間違えて更新しちゃった?



WindowsアプリでWEB巡回ツールのような物を作っているのだが
WEBの巡回なので完了までに非常に時間がかかる。

巡回する数にもよるけど1時間以上かかることもざら。
そんなのを同期処理でやってたらたまらないし 途中で中断したいケースもあるのに
同期処理だと中断ボタンを押すこともでいない

なので非同期で呼び出すように実装してみた。
いろいろ悩んだ部分もあるけど おおむねうまく動いている

独自クラスもいくつか作ってるのでこのままコピペしても動かないけれど
イメージとしてはこんな感じ。

ボタンクリックイベント{
//新しいスレッドを作成
threads = new Thread(new ParameterizedThreadStart(startThreadMain));
threads.IsBackground = true;
//スレッドで処理するqueueを引数で渡して起動
threads.Start(thqueues);
}

delegate void CloseDelegate();

private void startThreadMain(Object o) {
//スレッド内での処理をするQueue
ThreadQueue thqueue = (ThreadQueue)o;
try {
//メイン処理を行う
//MainClass.Main内でWebBrowserをNEWしてWEBから情報を取得し
//その結果をFormアプリに結果を返す
DeleMain main = new DeleMain (MainClass.Main);

//
IAsyncResult ret = this.BeginInvoke(main, new object[] { thqueue });
//登録処理が終わるまで待つ
ret.AsyncWaitHandle.WaitOne();
if (ret.IsCompleted) {
object obj = this.EndInvoke(ret);
}
//終了時の処理

//終了メッセージを画面表示
Invoke(new CloseDelegate(finish));

} catch (Exception ex) {
//エラー処理
}
}


ただ自分の実装方法が悪いのかもしれないけど1日中動かしていると2,3回処理が止まってしまうことがある。
CPU使用量もメモリ使用量もたいしたことないのになぜ。。。

目下原因を調査中(笑)
MainClass.Mainの中でもいろいろ複雑なことやってるからそっちが問題の可能性が高いので とりあえずこちら側は公開しておきます(笑)
VisualStudioをSP1にあげたらClickOnceでデスクトップアイコンを配置できるようになるってこともあり SP1にあげてみた。

そしたらローカルでは動くけどClickOnce経由でインストールするとエラーになる。
「保護されているメモリに読み取りまたは書き込み操作を行おうとしました」
デバッグ文を埋め込んで細かくチェックするとSQL Server CompactにSQL文を投げた瞬間にエラーが発生しているらしい。


SP1を当てる前に戻したら動くようになったのでSP1あてたことが原因なのはわかったけど元に戻す、、、ってのが最適な解とも思えないし いろいろ試してみる。
プロジェクトを1から作り直してみたりもうありとあらゆる・・・

そして結局解決したけど原因は VisualStudioをSP1にあげたら同時にSQL Server CompactもSP1にあげないといけなかったみたい。
SP1にあげたうえでそのDLLを旧版と差し替えて無事解決♪



ちなみにローカルではうまく動くから 発行→ログの確認→検証→発行・・・ってサイクルで30回以上やったかな?
リビジョン30ぐらいあがってたのでw
一回の発行に3分ぐらいかかるからホントもうイヤになる。
夜の10時に発生して明け方5時までやっても解決せず3時間ほど寝て起きて作業してやっとこさ14時に解決。

こんなことで解決までに10時間ぐらいかかってもう疲れた。。。
Click Onceで開発を進める中で結構ネックなのがデータファイルの取り扱いについて。

データファイルとしてsdfファイル(SQL Server Compact)を使っているわけですが 何かで開くとクライアントにまで勝手に配布されてしまい 今まで使って蓄積データが全て初期化されてしまう、ってとてもすてきな仕様です。。。

Click Once 便利なんだけど 今一歩かゆいところに手が届いてないですよね。

開かないようにする、、、って運用でカバーをしようかと思ったけど ついうっかりのクダブルクリック一発、、、で全ユーザーの蓄積データが消えてしまう、、、ってのは
恐すぎるので以下のようなアルゴリズムで回避しました。

ご参考までに。

まずデータファイルを2つに分けます。
master.sdf
data.sdf

master.sdfは開発者側で管理するデータが入っているマスタデータのみのデータベースです。
後から追加とか変更も楽々できるので楽ちんです。

そしてdata.sdfはユーザー作成のデータが入るデータベースです。
さらに起動時にmaster.sdfのマスターデータをコピーして上書きするようにしています。
これはデータベースが違うとselect文でINNER JOINとかできなくて不便だから、ということでこうしています。

このようにマスターとデータでデータベースを分けるのが第一段階。
これで マスターデータは開発社側で管理でき ユーザーデータは上書きする心配もあまりない。

ただしついうっかりユーザーデータのファイルを開く(ダブルクリックする)と上書きしてしまうのでもう一工夫が必要です。

それは初回起動時にファイルをリネームしてしまうのです。
たとえば data.sdf → user.sdfとか・・・

厳密には
「data.sdfが存在し、かつuser.sdfが存在しない場合 data.sdfをuser.sdfにリネームする」
です。

これで万が一うっかりファイルを開いてしまってもdata.sdfは上書きされますがuser.sdfは影響ないのでOKです♪

ご参考までに。






引き続きWeb巡回ツールネタ

あるフレームのページを制御しようとしたら「アクセスが拒否されました」とエラーになる。
なんで、、、と思ったら本体とフレームでドメインが違ってた。

簡単にいうとこんな感じ
■アクセス先ページ
http://www.aaaaa.com
■フレームのページ
http://acount.aaaaa.com

ログイン部分のパーツだけフレームになっていて違うサブドメインになっていた。
ド○コムめ。。。

仕方ないので別ドメインのコントロールの仕方、、、をぐぐってみると
WebBrowserにIInternetHostSecurityManager.GetSecurityIdを実装すればいいらしい。
そしてその方法は・・・

かなり小難しいことをしないといけないようで見てて頭が痛くなってきた。
なので1発でやるのはあきらめて以下のような方法で対応

1.フレーム含むページを読み込み フレームのURLを取得する
2.取得したフレームのURLへアクセスする
3.やりたい処理をする


この方法思いついてから5分で実装完了。

こういう風に正面から当たるの難しい場合は迂回路探すのもプログラマーにとって重要な能力ですよね。
迂回路ないときはあきらめて進みますが(笑)

WEB巡回ツールのようなものを作っていて WeBrowserコントロールを使ってWebBrowserDocumentCompleted イベントをトリガーに次の処理、次の処理、、、と進めていくプログラムを作っていたのだけど
同じ処理を流しているのに 想定通りに動く場合とエラーになる場合がある。
3~4回に1回失敗するのでWebBrowserコントロールの問題か?とあきらめてたんだけど
実はフレームのせいだということがひょんなことから判明!?

ページにフレームが含まれる場合 フレームの読み込み発生時にまず1回発生し
全ての読み込みが終わったときにもう一回発生するみたい。

そこで以下のように書いてフレームの場合は処理をしないように対応

WebBrowser1_WebBrowserDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser webb = (WebBrowser)sender;

//フレーム内の場合は両者が一致しないので実行されない。
if (e.Url == webb.Url)
{
//ここに処理を書く
}

VS2008で開発していたら急にフォームデザイナで以下のエラーが出てGUIベースで操作ができなくなる。
ただ操作ができないだけでコンパイルは通るし実行もできる。
なぜに?

Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.DeferredLoadHandler.Microsoft.VisualStudio.TextManager.Interop.IVsTextBufferDataEvents.OnLoadCompleted


いろいろ調べた末 Windowsのインデックスが原因だということが判明
http://geekswithblogs.net/rupreet/archive/2005/12/21/63740.aspx

具体的な対処としては
1.プロジェクトフォルダで右クリックから詳細
2.検索を早くするためにインデックスを~のチェックを外す

なんで解決したかは不明。。。
同じMS製品なんだからちゃんと対応してほしいところです。
作ったソフトをClickOnceで配布しようかと実装してみたのに
ローカルのテストでは動くのにClickOnce経由でインストールするとエラーになる。

なんでやねん!

かなりいろいろ調べた末 SQL Expressのデータファイルが見あたらないのが原因ということが発覚!?
ファイルがコピーされてないか?と思って検索するとちゃんとコピーはされている。
ただ本体と違うパスにコピーされているからなんで???

と調べてみるとClickOnceの機能でデータフォルダというものがあり データベースのファイルなどはこちらに格納されるらしい
そして説明によるとこのフォルダはローカルファイルと同じように扱えるようなことがあったけど実際取れてないので以下のようなソースを書いてみた。


if (ApplicationDeployment.IsNetworkDeployed)
{
dir = ApplicationDeployment.CurrentDeployment.DataDirectory + "\\";
}
else
{
dir = "";
}

PATH + dir + DATABASE_FILENAME;


ClickOnceの場合はフルパスを取得するという風に・・・
なんちゃってVS使いになって2週間、すっかり自分のものにしてせこせこ書いてます(笑)
ただ一点かなりはまったのが文字コードの判別。

PHPとかPerlとか文字コードを自動判別するメソッドが標準や準標準でついているのに
.netframeworkにはついてないしw
Windowsアプリではそんなに文字コード判別することってないのかなぁ?
とも一瞬思いかけたけど ファイルの読み込みとか普通にするはずだし どうにかならないものか?

と思ったらやっぱりありました。

2011年9月

        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30