今更ながら Visual Studio Code でデバッグ環境構築

2年ぐらいかかっていた業務用のウェブシステムのリプレース案件がこの夏ようやく完了。
なんせ仕様調査からシステム構築、デザイン、データベース設計、コーディングをたった一人でやって(web系が分かる人が僕しかいいない環境なので・・・)、前のシステムにあった訳の分からない仕様を撲滅しつつ、妥協できるところは妥協し、譲れないところは譲らず我を通したせいで文句を言われつつ、自分にとってなんとか納得のいくものができたつもり。
幸いなことに、使っているユーザーさんから、前より使いやすくなった、との声をもらえたのでそれだけでもよかったかな、と思います。
あとは営業さんがユーザーさんに簡単な講習会をやって終わり・・・という段階になってやっと強烈なストレスから解放されました。

前置きはともかく・・・

ちょっと時間が空いてきたので、前から興味があった Visual Studio Codeを触ってみよう、と思いいろいろ調べてある程度のデバッグ環境を構築できてきました。そもそもVisual Studio Codeを使ってみようと思ったきっかけは・・・

  • C#やC++で一つのファイルで完結してしまうような小さいコンソールプログラムをたまに書くけど・・・簡単なデバッグ環境が欲しい
  • msbuildとか要らん!そもそも XML は好かん。 nmakeで必要十分だし書くの慣れてる。
  • Visual Studio 2019のIDEを使えば手っ取り早いんだけど・・・おいらのPC環境のせいかどうか分からないが、エディタのフォント表示が異常に汚い(ガタガタ)
  • Visual Studio 2019のIDEを使うほど大したコードを書くわけじゃないし、何より動作が重いし、キーバインドが慣れない
  • 拡張機能でvimのキーバインドがほぼ完璧に再現できるみたい?

早速インストールし、必要そうな拡張機能を入れて使いだしたんだけど、問題が発生。
ターミナル>ビルドタスクの実行を行うと・・・cl.exe、csc.exe、link.exe などのツールへのPATH設定や環境変数などが定義されていないのでエラーに!

普段、c++やc#のコードをビルドするとき、Visual Studio 2019をインストールした時に入る、vcvars64.bat というBATファイルをコンソールウィンドウから実行することで Visual Studio 2019のビルドツールを使えるようにしてたんですが・・・はて・・・ターミナルの設定はどうすればいいのか・・・考えたあげく、下記のような コマンドファイルを作ってこれを setting.json の Terminal.Integrated.Profiles というキーに入れた。

REM ------------------
REM vs2019t.cmd
REM ------------------
@ECHO OFF
CALL "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
%comspec% %*

visual studio 2019のコンソール環境用にterminalのprofileを一つ追加。

下記のように デフォルトのプロファイルとして設定した。

ホントは知らないだけで、もっとスマートな方法があるのかもしれん!
結構ググったんだけど見つけられなかった。。。

で、実際のデバッグ環境としてはかなり良くなった!

ターミナル>既定のビルドタスクの構成からtasks.jsonを作成してビルド方法を書くみたいだけど、一つのファイルをデバッグしたい場合は、cl.exe/csc.exeとかのコマンドを設定して、
複数のファイルがある場合は、nmakeかmsbuildとかにした方が効率がいいかもしれない。
tasks.jsonのテンプレートをいくつか用意しておいた方がいいかもしれん。

vscode/tasks.json テンプレ

.vscode/launch.json テンプレ

とりあえずsubversionで生きていく。

愚痴。

ゴールデンウィークですね。今年も引き籠りの年になりそうです。
来年はどうなるんですかね・・・

まぁ、そんなことはともかく。

ここ半年、gitのトレーニングをしようと、趣味用途のプログラムはほとんどgitを利用して管理してきました。
クラウドサービスでVPSのインスタンスを1個作ってリモートリポジトリとして使って勉強。

結局のところ、僕のような頭の硬いおっさんには、ちょっと肌に合わないな、と思い、結局のところ挫折してしました(-_-;)
根本的に git を理解できなかった。っていうか、gitとかsubversionでブランチを使用した開発の手法をよく理解していないのが根本的な理由かなぁ。

挫折したポイントとしては、ブランチの扱い方が Subversionとあまりに違い過ぎて、付いていけなくなった(理解できなかった)こと。
いや、issue毎、feature毎にブランチを切って、マージして、という、一連の流れは分かるんですよ。
それこそ、チュートリアル的な単純なプロジェクトだと普通に分かるんすよ。ある程度コツがつかめてきて、仕事で使おうとしてたんだけど・・・どういえばいいのかちょっと迷うんだけど・・・ブランチを切る粒度というか、それがイマイチ分からないんですよねぇ。。。

アプリのソースがあって・・・
1. ある不具合があって、masterから修正のためのブランチを切る。
2. そのブランチでバグ潰しているうちに、そのバグに関連して別のバグを発見!😭

この時、
A. masterブランチから別のブランチを切って、後々リベースすればいいのか???
B. そのブランチでついでにその関連したバグを修正すればいいのか???
C. 一旦コミットしたのち、またブランチを切りなおせばいいのか?
subversionだとそんなの構わず一気に修正して、コミットして、ログに修正項目を羅列してたんだけど・・・。要は作業記録して残してるだけ、みたいな。

さらに、客側から、新しい機能のリクエストがあって、それを featureブランチを切って・・・とかやってると、慣れない git のブランチの管理でパニック!

これがチームで開発してて、同じ git使いなら、「ねぇ、今、こっちの修正で時間かかってるんで、別のバグ修正しといてくれない?」って感じで頼めるんだろうけど、おひとり様開発なので、んなのメンドクサイから、ついでに直して一緒にコミットすればいいや、って風になってしまう。
こういうのが重なってくると git の良い所を自ら潰していってるようなもん。履歴なんて滅茶苦茶。

結局、git を仕事で使うのを断念して、Subversionで、バグがあるたびに、issueブランチを作って(コピーして)、修正したらtrunkにマージして・・・という風な感じで進めていった方が生産的じゃないか???と思いつつあります。。。

一人で開発してるのって、制約がないので自由にできるけど、逆に言えば、引き継ぎのハードルが異常に高くなってしまう。
んー、こんな煩わしいことに時間を割きたくないんだけどな・・・

JSONデータをExcelファイルに展開したい、ブラウザで。

2022年3月8日 一部記述削除


2021年7月31日 修正
babelの設定を書き忘れてた!package.jsonにbabelの設定を追加。


これの続編

サーバーで処理させるよりデータだけをWeb APIで引っ張ってきてブラウザ上のJavaScriptで処理しよう、っていうのがここ最近のトレンドだと思います。そのための環境は整ってきました。

ここ最近、業務系のウェブシステムでExcelをあーだこーだしろ、っていうのが急に増えてきてまして・・・たぶん昔動かしていたAccessアプリケーションを単純にWebへ・・・という流れなんでしょうかね?

で・・・Webサーバー(LAMP)でPHP-Excel(PHP-Spreadsheet)使ってエクセルファイルを処理させると滅茶苦茶メモリ食いませんか?
数年前に少し試してみたんですが、通常業務で利用するぐらいの行数(列数)でもメモリ不足でエラーでるわ、処理が遅くて使いもんにならんわ、で使っていくのを速攻で止めました。
(PHPじゃなくてJavaとかだと速いのかもしれんが、Javaは難易度が高すぎるし、そもそもLAMPを主戦場にしている仕事環境なので。)

さて、ちょっと前に Excelファイルを読み書きする SheetJS っていうのをネタに備忘録として書いたけど、SheetJSは Pro版じゃないと(有料)、スタイルなど文字の大きさとかフォントの指定だとかは扱えません。実質、既存のExcelのデータを読むだけになると思います。まぁPro版買えって話なんですけど。

で、良い感じにブラウザでカンタンにきれいなExcel帳票を作れないかなぁ・・・といろいろ githubとか漁ってたら、XLSX-Rendererっていうのを見つけました。簡単に言うと、WORDでいうところの差し込み印刷のように、テンプレートのExcelファイルにデータを差し込んでExcelファイルを作ってくれる。
XLSX-Renderer自体は ExcelJS というnodeモジュールを使用しているみたい。

XLSX-Renderer
https://github.com/Siemienik/XToolset/tree/master/packages/xlsx-renderer

まぁ、いつもの如く中身はよく分かんねーけど一度試してみました。今回もその備忘録です。
【動作デモはこちらから】

開発環境としてnodejsとnpmが使える環境が大前提です。
また、モダンなJavaScriptの知識( Promise,await/async,thenなど)と、必要であれば、BabelJSなどのカンタンな使い方を知っている人向けです。じゃないとワケワカメになると思います。ご了承のほど。

システムにbrowserify,uglify-jsをインストール

>> sudo npm install -g browserify
>> sudo npm install -g uglify-js

説明するまでもないとは思いますが、browserifyはnodejsモジュールをブラウザで利用できるようにワンパッケージにしてくれるコマンド。uglifyjsはminifyするため。詳しくは知らない。手順だけ知っておけばいい。理屈は後回し。フロントエンド周りに詳しい人は webpack とかいろいろ方法はあるとは思いますが、僕はこれしか知らないので💦

まず、下記のようなテンプレートとなるExcelファイルを作ります。(テンプレートExcelファイル

このExcelファイルに、データをぶち込みたいところへ、#から始まる命令コマンドをセルに埋め込んでいく、という感じ。自分でセルを埋め込むメソッドをチマチマコーディングするより圧倒的にラクできる感じ。
#! FOR_EACH 命令を使えば、配列をぶち込める。

要は MVCモデルの View の出力先を エクセルファイルにしている感じでしょうか。同様に、DOCX-Renderer とか PDF-Renderer ってのも作ってほしいよ(笑)

次に本題の処理する部分をコーディングします。

扱いやすいように、僕はグローバル関数を(windowオブジェクトのプロパティとして)定義してコールするようしました。このあたりはどういう処理を行わせるかによって実装方法は変わると思います。とりあえず関数として実装しました。
このままではブラウザでは動かないので、上記のコードをブラウザで利用できるようにするため、この xlsx-template.jsをbabelなどのツールを使ってビルドします。そのため、ディレクトリを一個作って以下のpackage.jsonを置き、

>> tree .
.
├── package.json
└── xlsx-template.js

そのディレクトリで以下を実行

>> npm install
>> npm run bundle

そうすると、bundle.min.jsが生成されるので、これをHTMLファイルのscriptタグからロードします。
HTMLファイルはこんな感じでしょうか。実際にはJSONデータはサーバーから取得することになりますが・・・。データ構造は簡単で、キーと値がそのままテンプレートのExcelファイルのセルに記述した “## キー名” に対応してます。このあたりは XLSX-Renderer のgithubページに記述の仕方が書かれているので参考に。比較的簡単です。

そうすると、下記のようにテンプレートとなるExcelファイルにデータを埋めてくれます。

【動作デモ】

エクセルのレイアウトが変わっても、テンプレートのExcelファイルを変更すればいいだけので、変更も簡単で、実行コードを修正する必要もない。

まだ試していませんが・・・データベースサーバーからウェブサーバー経由で1万件ぐらいのJSONデータを取ってきて、処理させてテストしてみて、処理速度が業務に耐えられるようであれば、本格的に利用していきたい、と思ってます。

・・・しかし・・・みんなExcel好きだよねぇ。。。

ブラウザで画像ファイルのリサイズ処理

2022年12月20日 修正
resize処理を書き換え。
・imgタグを生成するのをやめて、window.createImageBitmap関数に変更。
・OffScreenCanvasが使えるブラウザでは OffScreenCanvasを使用し、使えない場合は cavas要素を生成して使用するように変更。


2021年9月11日 修正
ソース中の冗長的な構文を修正。


2021年9月15日
つづき。Web Worker。


最近のお話。あくまでフィクションです💦 進行中案件の担当さんにある日相談を受ける。


画像をウェブサーバーにアップロードしたいけど、大きいサイズはリサイズしてサーバーに保存したいんだよねー
でも、外注先の人が・・・
『サーバーで画像縮小するんで負荷が大きいし、画像処理にGD使うからPHPのmemory_limitを上げてくれないと・・・』
って言われてるんです!サーバーは共用のレンタルサーバーでmemory_limitなんて変更してくれそうもないし・・・。
『memory_limitを仮に上げてもアクセスが重なるとサーバー落ちてしまうかもしれないけど、責任持てない』
とも言われます!

何か方法ない?


んー、アップロードする前に JavaScript でリサイズすればいいんじゃね?って単純に思ったわけですけど・・・
そんなことも分かんねー外注先使ってんのか・・・っていうのが一つ目。
つーか、重くなるのが分かってんならサーバーで処理させるな! っていうのが2つ目

ってなわけで、サンプルコードを渡して「後は良きに計らえ」コース。
サンプルを作ってやれば、あとはなんとかしてくれるんじゃねーか? つか、なんでこっちがサンプル作んなきゃいけないんだよ!!!
本来外注先のシステム制作会社がやるべき仕事だろーよ!
まぁ、怒ってもしょうがない。そのレベルの外注先しかやってくれるところないんだからしょうがない。
こうやってこのブログのネタにできるんなら、怪我の功名というやつです。(ん?使い方間違ってないか?)

本題です。要は ブラウザで画像をリサイズしてBlobオブジェクトとして取得できれば、あとは FormDataを作ってappend するなり、してやれば万事解決です。

ググればJavaScriptとCanvas APIを使ったコードがゴロゴロ転がってるので参考にしてこちらの用途に合うように手を加えていく。
画像リサイズ処理自体は Canvas API を使えば簡単にできるので、後は File オブジェクトを FileReader で読み込んで、リサイズ後に canvasオブジェクトの toBlobメソッドで Blobオブジェクトを取得してやればいい。

ややこしいのが、それらすべてが非同期で処理しなければならない事。Promiseを使って一連の画像処理をしてやれば、await構文で同期処理のように待つことができる。

再利用できるように モジュールとして書いた。
簡単に説明すると、リサイズが必要な画像のFileオブジェクトと短辺の最大サイズを引数にして、コールするとリサイズされた画像がblobオブジェクトとして resolve される。

当然ブラウザのバージョンに依存してしまうが・・・ゴチャゴチャ言われたら ターゲットのブラウザ用に Babel で変換しちまえばいいし💦

これを下記コードのように input[type=file]のonChangeイベントハンドラでゴニョゴニョしてサーバーにアップロードする。
僕は jQuery が大好きなので jQuery を使う。何度も言うけど、やっぱり document.querySelector(‘p’) とかタイプするより、$(‘p’) の方がラクなんだよね。

当然ながら Internet Explorer は全バージョンエラーになる。
chrome/firefox や safari でもバージョンによっては動かない。。。これじゃダメだ!って言われたら、最終手段 Babel のご登場(⌒∇⌒)

僕は下記のような .babelrc を書いて・・・

 >> browserify ./index.js --transform babelify | uglifyjs -c -m --output ./es5/bundle.min.js 

とかやると、とりあえず IE11でもシンタックスエラーにはならないようになる。だけど、結局 FormData.appendメソッドでエラーになるけどね😫

まぁ、今更なんですが、Canvas APIではリサイズ処理だけじゃなく図形を描画したり、ピクセル単位で演算処理を行うことでいろんなことが可能なので、サーバーサイドで画像処理を行わず、余力のあるクライアント(フロントエンド)で前処理させてから・・・というのがイマドキのやり方なんだろうな、と思います。

Windows Explorerシェルで複数のファイルをプログラムに渡したい

ファイルの差分表示に WinMerge (日本語版) を使っていました。TortoiseSVNにも自動で設定してくれるし、重宝してました。

一方、ubuntuとかCentOSのbashシェル内で作業しているときは、vim のdiffモードを使うことも増えてきまして、どうせならWindowsのデスクトップで作業しているときも gVim でやろうかな、と思い立ち、Windowsのレジストリをいじって関連付けしようとしました。

具体的には↓のように・・・

まぁ、でもこれは失敗します。この方法で2つファイルを選択した状態で、右クリックして opendiff をクリックするとgvimが二つ起動してそれぞれ別のgvimで開いてしまいます。失敗。
※追記 2021年6月24日 official buildのvimだと GvimExt32/GvimExt64 というシェルエクステンションが既にバンドルされてるみたいすね・・・。僕は kaoriya vim しか使ってなかったので気づかなかった💦

で、じゃぁ、%2とか%* ってのをつけ足せばいいんじゃない? って思うのですが、やってみれば分かりますが、失敗します。上記と同じように複数のgVimウィンドウが開きます。
どうやら標準で複数選択したファイルを同一のプログラムのインスタンスに渡すことはできないようです。

じゃあ、どうすればいいのか?というと、Explorerのシェルエクステンション(インプロセス COMサーバー)を書いてレジストリに登録する必要があります。(グーグル調べ)

ってことで、現実的に C++ で実装するのはヒジョーにメンドクサイ。探せばシェルエクステンション用のフレームワークみたいなものがあるとは思うのですが、今はC#をメインに使っているので、できるなら C# でできないか、調べたところ(グーグルで)、SharpShell という、C#のシェルエクステンション用のフレームワークがあるようです。

SharpShell
SharpShell makes it easy to create Windows Shell Extensions using the .NET Framework.

ショートカットメニュー用のシェルエクステンション作成とインストールはチュートリアルがあるので、この通りに進めていけば苦も無く作成できると思います。

チュートリアル(英語)

ただ、このチュートリアルはかなり古く2014年の時のものですが、基本的にはあまり変わっていません。
注意点は、SharpShellを nugetで入れる場合は、2021年2月現在最新のもの(2.7.2)を入れる場合、Server Manager などのツールも最新のものを使う必要があります。nugetで SharpShell Tools を入れると なぜか 2.2.0のものがインストールされてしまうので上記 githubサイトで配布されている 2.7.2 のものを使う方がいいと思います。

基本的には・・・Visual Studio 2019 Community を使用します。

  1. プロジェクトを .NET Framework のライブラリ(DLL)として作成。
  2. 「プロジェクト ⇒ nugetのパッケージ管理」で SharpShell をインストール
  3. チュートリアルで説明されているように、SharpContextMenu を継承したクラスを作成
  4. ショートカットメニューに必要な抽象メソッドを実装する(Visual Studio 2019のコード補完で自動的にスケルトンが出ると思います)
  5. プロジェクト・プロパティ⇒アプリケーション⇒アセンブリ情報 で「アセンブリをCOM参照可能にする」にチェック
  6. プロジェクト・プロパティ⇒署名⇒アセンブリに署名する(チェック)⇒新規作成 適当な名前をつける
  7. ビルド
  8. ServerManagerでビルドしたDLLファイルを読み込んで、Server⇒Install Server(x64) Register Server(x64) を順番に実行
  9. インストール&登録する前にテストしたければ、Test Server In Test Shell をクリックするとテスト用のエクスプローラもどきが出てくるのでそこでテストできるようです。

まぁ、そんなこんなで、やりたいことはできました(^▽^)/

※ただ一点注意しないといけないのは、.NET Frameworkのようなマネージコードをインプロセスサーバーとしてエクスプローラのプロセス内で動作させるのは未だにいいのかどうか分からないって点です。マイクロソフトの中の人も良いと言ってる人もいれば、やらない方がいいっていう人もいるようです。 この辺りは SharpShell のドキュメントでも書かれています。2021年現在、実際のところどうなんなんでしょうかね・・・・?

ただ、ショートカットメニューをクリックしてプロセスをキックするだけならあまり問題ないのかな~と思ってます。

ずっと前にC++で書いたシェルエクステンションのソースコードが残っているのでC++で書こうと思えば書けるんですが・・・やっぱりオッサンはもう頭が硬くなってるので時間がかかるんですよねぇ。

書いたコードはこんだけ。便利なライブラリに多謝・多謝。