2017年11月12日日曜日

SafeArray でユーザー定義型を使いたい

SafeArray の要素をVBAのユーザー定義型にするにはどうしたらいいんだろうか
https://msdn.microsoft.com/ja-jp/library/windows/desktop/ms221482(v=vs.85).aspx

SAFEARRAY 構造体の fFeatures を FADF_RECORD にすればいいのか?

An array that contains records. When set, there will be a pointer to the IRecordInfo interface at negative offset 4 in the array descriptor.

とあるから IRecordInfo をセットするんだな、ユーザー定義型の情報なんだろう
でもこれどうやって調達すればいいんだろう
自前で作るとかはやりたくない

SafeArrayGetRecordInfo() でとれるかなと思ったんだけど失敗する
 dim arr()
 redim arr(1) as ユーザー定義型
の SafeArray 構造体がどうなってるか確認したら、arr の fFeatures には FADF_RECORD が格納されていない

ユーザー定義型を示すGUIDから取得できたりもするっぽい?
でもGUIDの取得方法わからない

んーーー
ユーザー定義型の配列を variant に突っ込もうとしたときに
「パブリックモジュールで定義されたなんとかかんとか」
みたいなエラーメッセージが表示されたりするのと関係があるんだろうか
そういうの以外は実はCOM的な意味でのユーザー定義型になっていなくて、
要素長だけかんりしてるとか
でもVBEでインテリセンスみたいなメンバ表示が出るあたり、
IRecordInfo とか持ってそうな感じするんだけどなぁ

2017年11月11日土曜日

AddRef() で落ちる 続き


variant 型に IEnumVariant をセットしようとしたときにエクセルが落ちることがある件の続き。

いろいろ試してみると、set variant = a.NewEnum() の時 queryInterface() でGUID B196B283-BAB4-101A-B69C-00AA00341D07 を要求しくることに気づいた。
これは IprovideClassInfo インターフェースというものらしく、唯一?のメソッド GetClassInfo() から ItypeInfo インターフェースを得るためのもの(っぽい)。
これをちゃんと実装するとたぶん落ちなくなるんだと思うが、かなり大変そうなのでやめとく。代わりに E_NOINTERFACE を返すようにした。
とりあえず「型が一致しません」エラーが出て、エクセルが落ちることはなくなった。

2017年10月31日火曜日

AddRef() が落ちる

制作中の VBAEnumration の話
For Each で列挙できるようにするため、EnumVariat をVBAから CoTaskMemAlloc() でメモリ確保して作っています。
そこに標準モジュール関数を登録した vtable をセットして仮想関数持ちのオブジェクトを再現しているんですが、なんか AddRef() で時々落ちてしまいます。エクセルが。

dim a as IEnumVariant
set a = q.NewEnum

だと落ちないっぽいのに、

dim a as variant
set a = q.NewEnum

だと、set a = したところで addref() に飛んで、そこを抜けると落ちる。気がする。

原因がわかんないなぁー

こういう悩み中みたいな話を GitHub のどこかにのせていってもいいんだろうか
Readme とか Issue?は違うのかな目的が

2017年10月25日水曜日

VBAでLINQみたいなことができるクラス群

意味ないものですが作成中

https://github.com/abarabone/vbaEnumeration

GitHub にアップしてるんだけど Git も GitHub もよくわかりませぬ…
とりあえず一通りのオペレータができるまでは黙々とやっていきます

実用性はないでしょうけど

2017年7月5日水曜日

クソVBAをやってる話

VBAでラムダのようなものを使いたくて試行錯誤中

まずは引数として渡せる関数の再現
試行錯誤の履歴

・Application.Run()  … 名前文字列で指定する 重め
                参照引数が使えない
                Application ってどうなの

・CallByName()    … 名前文字列で指定する 重め
                参照引数は使える
                オブジェクトのメソッドしか呼べない
                
・イベント        … 汎用的に呼べるイベントを作ってやることで再現 軽め!
                戻り値はない エラーを呼び出し元に Raise できない
                withEvents のメンバにセットした値を外部に持ち出せば、
                 対応したイベントを呼び出すことができる

イベントに光を見出して、文字列コードをラムダにして動的モジュールを生成していくようにした。
LINQのオペレータ的なものを、Skip,  Take, Select, Where, SelectMany, くらいまで作成した。
しかし for each に対応させられずに、Do Loop で回す感じになっていて、不満があった。

IEnumVariant を V-table ごとメモリ上に構築してしまえばよいことを知る。
APIを使用することをなぜか拒んでいたが、解禁する。つまらないこだわりだったと思う。
VBAのオブジェクトはCOM関係そのものということに気づく。

で、ついでにここでこれに気づいてしまう…

・DispCallFunc()    … AddressOf で関数ポインタを得て、使用できる。
                参照引数も使用できる。型も使用できる。
                が、引数指定のクセが強く、たやすくハングアップする。これは問題。
                なやむ。
                また、オブジェクトのメソッドを V-table のオフセットで指定実行できる。

そこからVBAのクラスは IDispatch  を継承しているということに気づく。
ITypeInfo を作成すればいろいろいじれる。
ただ、本当に DispCallFunc() の引数はセンシティブで、試行錯誤しまくった。情報もないし…。

また、クラスメソッドを呼ぼうとしたときに、第一引数が戻り値になってしまうという謎の現象に遭遇。
回避できない。
同じことで悩んでる外人さんもいた。
どうしたものか

そこで今、このページを発見。
https://msdn.microsoft.com/en-us/library/ms221141(v=vs.85).aspx
うおぅ!!
今までやってた煩雑なことが、APIでできそう???
と興奮すると同時に徒労感が。

いまここ。


2017年6月4日日曜日

async/await について

async/await

await XxxxAsync();
と記述した時、XxxxAsync() が終了するまで制御をOSに返し、
それ以降の実行を待機する仕組み

async 関数を await で実行

void を返す      ... fire & forget
Task<int> を返す   ... 非同期処理完了後に同期して int を返す
Task  を返す     ... 非同期処理完了後に同期して void を返す

fire & forget とは

await XxxxAsync(); と、その下に続くコードが並列実行される
(ノンブロッキング関数的なイメージ)

await

await XxxxAsync(); の下に続くコードは、
XxxxAsync() から戻ってきた後に実行されるコールバックとしてコンパイルされる。
コールバックが実行されるスレッドを決定するのが同期コンテキスト。

同期コンテキスト(synchronization context)

Task オブジェクトがタスク実行後に戻る文脈を保持する。
await 呼び出し側の SynchronizationContext.Current に戻ってくるように設定される。
デフォルトでは task.ConfigureAwait( true ) となっており、同期コンテキストが適用される。

実行環境による同期コンテキストの違い

WPF等GUI  … UIスレッド
ASP.NET    … リクエストとレスポンスが同一スレッド
コンソール   … null(同期コンテキストが存在しない)
※同期コンテキストが存在しない
  = 戻るべきスレッドを規定しない
  = スレッドプールの任意のスレッドに戻る(というか移行)

同期コンテキストを無視したいとき

await XxxAsync().ConfigureAwait( false );
コンソールと同じ動作となる

単に、何かの処理を別スレッドで実行したい場合について

await/async で、せっかくコールバックをインデントのネストから解放できたのに、
任意の処理を別スレッドでさせたいときなんかに Task.Run() で処理を書かざるを得ないのはダサく感じる。
平坦に書きたいです。

同期コンテキストがなければ await Task.Yield() でそれ以降を別スレッドに移せるぽいが、
そうでない場合は await Task.Run( () => {} ).ConfigureAwait( false ); などと書かないとダメかも。
でも無意味なタスクオブジェクトが発生したりしそう。

そもそも、async/await は非同期タスクを待ち受けるための構文であって、
スレッド実行を平坦に記述するという汎用的な目的で使うべきものではない、ということか。
同期させるスレッドを自由に切り替えできればいいのにと思うけど、そういう思想のものじゃない、のか…。

ニューラルネットやってみてる

最近ニューラルネットやってみてる 理屈を学ぼうと思って、まずはオブジェクト的に作ってみてる (ベクトル化とかは後回しで) sigmoid, tanh, ReLU MSE, cross entorpy あたりを小規模にいじってみてます でも今 soft max の逆伝...