ほっとひといき給湯室 |
|
投稿日時: 11/10/21 07:55:51
投稿者: kumatti
|
---|---|
※ PCに関する事なら、何でもどうぞ。
|
|
投稿日時: 11/10/21 07:56:36
投稿者: kumatti
|
---|---|
yayadonさん、回答ありがとうございます。
CreateObject ("JScript") その先をって話です。 # タッチの差でした。 |
|
投稿日時: 11/10/21 08:35:10
投稿者: kumatti
|
---|---|
前スレの
Dim std_excepinfo As stdole.EXCEPINFO ↑構造体ですけど。 それで、何でもなかったようです。 よく分かりませんけども、モジュールのエクスポート、インポートで 解決しました。 (*.tlbを何度も作りなおしたからかな) # IActiveScriptSiteの実装を勉強ってトコです。 |
|
投稿日時: 11/10/21 11:30:46
投稿者: Abyss
|
---|---|
>それで、何でもなかったようです。
|
|
投稿日時: 11/10/21 12:43:48
投稿者: 月
|
---|---|
yayadon さんの引用:' 同一セルに対して,別の参照経由で設定&取得 rng(0).Value = i lngValue = rng(1).Value rng(0).ID = i lngValue = rng(1).ID とすると同時参照数が影響しませんでした。 メンバによっても違うようです。 それにしても、よく考えられたコードだなと思いました。 yayadon さんの引用: Private Sub Test34() 試行の跡も見て取れますしね。 |
|
投稿日時: 11/10/22 00:40:19
投稿者: yayadon
|
---|---|
月 さんの引用:yayadon さんの引用:' 同一セルに対して,別の参照経由で設定&取得 rng(0).Value = i lngValue = rng(1).Value 時間がとれないのと,面倒だったので,他のプロパティは調査しなかったのですが, どのプロパティも影響してしまうと, 他の原因で遅くなっている可能性も捨てきれなくなるので, 参照数が増えても影響しないプロパティがあることを確認できてよかったです。 影響しないプロパティの値は, ・Rangeオブジェクトのインスタンス内でキャッシュしていない値 もしくは ・キャッシュで持ってはいるが,同期していない/同期する必要のない値 のどちらかである可能性が高いと言えるんじゃないかと思います。 同期している(別参照経由でも値が反映される)かどうかも含めて, 一つずつ確認してみると面白いかもしれません。 ----- いろいろと検証したのですが, 同時に参照するRangeオブジェクトの参照を,わざわざ別のオブジェクト参照にしていますが, すべて ActiveCell にしても同じ結果になります。 ですが,念のため, 「すべて同じセルを参照しているから,負荷がかかっているんじゃないの?」 や,逆に 「すべて同じセルを参照しているから,負荷がかからないんじゃないの?」 という仮定を排除するために,別にしたコードをアップしてみました。 あとは,調査するプロパティと受け取る変数の型をどれにするか迷いましたが, Rangeオブジェクトの扱いのノウハウに詳しくないので, すべてのパターンで調査するという王道(笑)にでたのですが, ひとつ示すとしたらということで, (自分の知識内で)一番負荷がニュートラルそうなものを選びました。 COM仕様での値の受け渡しですが, 受け取りは, 変数のアドレス値を渡す ---(A) ことで受け取り可能になり, 値の受け渡しは, そのまま値を渡す 場合と, 変数のアドレス値を渡して,受け渡された側が逆参照して値を得る ---(B) やり方の場合があります。 なので, (A) のときと (B) のとき, 引数の型とデータ値の型が違う場合,引数の型にあわせるために一時変数が作成されて, 繰り返しが多いと,一時変数の影響によるものの可能性も排除できなくなります。 文字列が絡んでくるとさらにややこしくなります。 なので, ひとつのパターンで調査しても, 他のパターンでも同じ傾向が表れないと, ある程度正しいということが言えないので, いろいろ調査する必要が出てくるわけです。 ----- Excelエキスパートの人は, Rangeオブジェクトは,基本中の基本なので, ちゃんと調査しておくと,いいことがあるかもしれません。 |
|
投稿日時: 11/10/22 01:46:13
投稿者: yayadon
|
---|---|
[補足]
yayadon さんの引用: プロパティや関数の引数の型が,データ値の型と異なる場合, 一時変数が作成されて,その変数のアドレス値が渡されます。 これは,純粋な関数呼び出しのときだけです。 VBA の関数の中には,実際は, 予約名(reserved name) が,実際のエンティティ(関数やもろもろ)として振る舞うものがあります。 Len や LenB や CLng や CInt等 です。それらは,実際には純粋な関数呼び出しではないこともあって, 一時変数は作成されません。 一方, VBA.Len や VBA.CLng などは,純粋な関数呼び出しになります。 例えば, VBA.Len では,Long型の変数を渡しても, VBA.Len に実際に渡す引数が Variant型 の一時変数になってしまうため, 内部処理形式の長整数値を文字列に型強制(coercion)してからの評価 になったりするので, 渡すデータ値の型によっては動作が異なったり, VARIANT型は,オートメーション仕様にそった型を扱うものだったりする関係で, 構造体は,エラーになったりします。 他の関数も含めて, 引数が Variant型 の参照渡し (C++的には VARIANT* )になっているものは, データ値の 型 が Variant型 でない場合, 上で見たように,Variant型の一時変数が作成されるので注意が必要です。 (COMオブジェクトのメソッドやプロパティの引数ではなくても, 例えば,MsgBox など,C++的には VARIANT* なものはたくさんあります) 数値の場合,Variant型での演算が遅いので,途中までは数値型がよいのですが, String型の場合,つまり,BSTR の場合, 参照カウント方式を採用していない関係で常に深いコピーになる仕様になっているため, 一時変数への最後の深いコピーで String型 での演算による貯金をまず使い切ってしまうので, 注意が必要です。 引数が Variant型 の参照渡し (C++的に VARIANT* のところ)が最終的に絡んでくる場合は, Variant型 になるところで自然にVariant型に移行させるか, ほとんど演算をしないのならば,最初から Variant型 で用意するのが, 一時変数への最後の深いコピーを避けることができて無難な気がします。 |
|
投稿日時: 11/10/22 02:08:45
投稿者: yayadon
|
---|---|
[補足の補足]
yayadon さんの引用: この理由を Variant型 のサイズが Long型 や Integer型 に比べて大きいから と説明している時を見かける時がありますが,Variant型のサイズが問題なのではなく, Variant型どうしの演算が特別なために遅くなることによって遅くなっています。 数値の場合,変わり具合に対して, 文字列の演算が,Variant型 と String型 とでは,大して変わらないのは, String型であっても,BSTR文字列の演算がもともと特殊なためです。 いずれにしても,引数にVariant型が絡んでくるとややこしくなるということです。 |
|
投稿日時: 11/10/22 05:36:15
投稿者: yayadon
|
---|---|
Letステートメントについて書き直してみました。
Application.ActiveCell = 0 のような場合の単純データ値への評価を考えてみます。 ActiveCell は,Range型 の a class type になります。(汎用Object型では無いということ) 固有クラス型(ヘルプでは 固有オブジェクト型 と表現)を単純データ値へ評価するには, まず,デフォルトのプロパティを評価しようとします。 Rangeクラスのデフォルトのプロパティについて考えてみます。 ここでは,OLE/COM Object Viewer というもので, タイプ ライブラリ上での Rangeクラスの dispinterface の定義を見てみます。 デフォルトのプロパティやデフォルトのメソッドですが, ディスパッチID (DISPID) の値が 0 のものを デフォルトのプロパティと決めています。 DISPID というのは,メソッドを識別する値です。 メソッドの識別の方法は, DISPID によるものと, インターフェース内での先頭からの相対位置によるものがあります。 が,ここでは,デフォルトのプロパティを知りたいので, DISPID が 0 のものを探すため dispinterface の定義を見たということです。 id という属性は,メンバーID と呼ばれている属性で, 要するに DISPID のことです。 探してみると,_Default という名前の [id(00000000), propget, helpcontext(0x00010000)] VARIANT _Default( [in, optional] VARIANT RowIndex, [in, optional] VARIANT ColumnIndex); と [id(00000000), propput, helpcontext(0x00010000)] void _Default( [in, optional] VARIANT RowIndex, [in, optional] VARIANT ColumnIndex, [in] VARIANT rhs); のふたつの定義があることがわかります。 propget 属性が付いている方が,Getプロパティ(取得プロパティ)で, propput 属性が付いている方が,Letプロパティ(設定プロパティ)です。 コードを再び見てみます。 Application.ActiveCell = 0 の Application.ActiveCell は, Letステートメント [Let] l-expression = expression の左側に来ています。 なので,評価の結果は, 値が代入できる左辺式(l-expression)である必要があります。 上のふたつの内, 設定できるプロパティは,Letプロパティの方になります。 (実際は,Getプロパティでも参照が返ってくれば,さらに評価を進めることによって 左辺値になる可能性があるのですが,ここでは,Letプロパティが 見つかったので, そこには立ち入らないことにします) ということで, アクセス可能な デフォルトの Letプロパティ が存在しています。 下に抜き出しました。 [id(00000000), propput, helpcontext(0x00010000)] void _Default( [in, optional] VARIANT RowIndex, [in, optional] VARIANT ColumnIndex, [in] VARIANT rhs); デフォルトの Letプロパティ を引数無しで呼んで, 右側の整数値 0 を設定できる単純データ値なのか?を確認します。 RowIndex と ColumnIndex は, optional の属性が設定されています。 この意味は, 「引数を省略しましたよ」ということを示す VARIANT型 の値を渡す決まり になっています。 具体的には, 内部処理形式が VT_ERROR 型 で, 値が DISP_E_PARAMNOTFOUND (0x80020004L) のVARIANT型のデータ値 を渡すことで,「引数を省略しましたよ」ということを示めすことになっています。 親切なことに,VBA では,背後で代わりにやってくれます。 なので,VBAユーザーは,省略するだけでよいことになっています。 ということで, RowIndex と ColumnIndex は,省略可能です。 残った rhs は,optional属性が付いていないので,省略可能ではないのですが, これは,右側の整数値 0 を渡す引数です。 これは,省略してしまっては意味がないので別扱いです。 rhs は,Variant型 なので,いろいろな型を扱えるのですが, 設定する側なので, 0 を入れれば,内部処理形式 Integer型 の Variant型になるため Variant型を単純データ値として扱えます。 ということで, Application.ActiveCell の単純データ値への評価が成功しました。 一方, 右側の 0 は,最初から単純データ値なので, 単純データ値への評価の中身は,型 の決定だけになります。 Integer型 と評価されます。 代入される側の型が Long型 や Double型 などの場合, Integer型 からそれらの型へ暗黙的な型の変換(Let-coercion)が起こります。 今回は,代入される側の型は,Variant型なので, Integer型の単純データ値の場合は,そのまま値のコピーを代入するだけです。 (内部処理形式の型を示す値も設定しないといけないので, Variant型に対する値の設定の仕方がありますが,VBAが背後でやってくれます) ----- 今回は,右側が 0 のような単純データ値でしたが, そうでない場合,例えば,右側も Application.ActiveCell = Application.ActiveCell のように固有クラス型だった場合は, 右側の Application.ActiveCell も単純データ値への評価をします。 今度は, デフォルトのプロパティのところは, [id(00000000), propget, helpcontext(0x00010000)] VARIANT _Default( [in, optional] VARIANT RowIndex, [in, optional] VARIANT ColumnIndex); こちら側になります。 RowIndex と ColumnIndex は,optional属性が付いていて省略可能です。 省略して呼び出すと,VARIANT型のデータ値が返って来ます。 ということで,下記の Letステートメント Application.ActiveCell = Application.ActiveCell は,結局, VARIANT型引数rhs = VARIANT型のデータ値 のようになります。 注意: デフォルトのプロパティがそもそも無いもの や 評価に失敗する場合, 例えば, Application.ActiveWorkbook = Application.ActiveWorkbook などは,Letステートメントはエラーになります。 今まで,説明しませんでしたが, 評価には,静的な評価と実行時の評価があります。 VBA では,実際の型が,実行時までわからないことがあります。 VARIANT型もそのひとつで, 静的な評価では,VARIANT型の値は,とりあえず 単純データ値 として扱います。 上の右辺値の VARIANT型のデータ値 は, 実行時にならないと,内部的に,単純データ値 かどうか?がわからないということです。 で,実行時に, 該当のセルに,数値の 0 が入っていたとすると, VARIANT型のデータ値に入っているデータ値は, Variant型の内部処理形式の型は,Double型になっているハズで, Double型は,単純データ値なので, 右辺の Application.ActiveCell の単純データ値への評価は成功します。 そして代入という形になります。 |
|
投稿日時: 11/10/22 08:30:49
投稿者: kumatti
|
---|---|
# Abyssさんの意図としては、個人のブログでないのだから、一般的な認識・常識に基づいて
関数またはインターフェイスが予約されているか、または Visual Basic でサポートされていないオートメーション タイプが関数で使用されています。 ところが、定義自体にそれとおぼしき箇所はない。 しばらく後に、モジュールのエクスポート・インポートで解決をみたということです。 (Excel側でマクロ無効で保存でも、同じ効果かもしれませんけど) |
|
投稿日時: 11/10/22 09:55:48
投稿者: ろひ
|
---|---|
前回スレの大分前の発言で恐縮なのですが、気になったので。
kumatti さんの引用: 当該の方面に詳しくないので、表現が適切じゃないかもしれませんが… Windows8(Metro)上においては、VBAからの.NET FlameworkのAPI利用が安定しそうだ …という解釈であってますか? 更には、既存の環境(WindowsやOffice)においても、Windows8(Metro)対応の.NET Flameworkが導入された環境であれば、安定しそうだ(=相性の改善が見込める可能性がある)、という様になりうるのでしょうか? 既存の環境においても、(例えば、新しいWindows Runtimeへ勝手に(ユーザーが意識なしに)更新される機会があるなどして、)VBAからの.NET Flameworkが盛んになりえるなら嬉しいのですが、そこまでの可能性もありえそうでしょうか? |
|
投稿日時: 11/10/23 02:39:08
投稿者: yayadon
|
---|---|
[訂正]
引用:↓ propget 属性が付いている方が,Property Get (取得プロパティ)で, propput 属性が付いている方が,Property Let (設定プロパティ)です。 引用:↓ 上のふたつの内, 設定できるプロパティは,Property Let の方になります。 もしくは, 上のふたつの内, 設定できるプロパティは,設定プロパティ の方になります。 (← ちょっと変ですが) ※ 他の箇所の訂正も同様 |
|
投稿日時: 11/10/23 04:47:36
投稿者: yayadon
|
---|---|
ろひ さんの引用:kumatti さんの引用: kumattiさんでなくて,たぶん,私ですね。 引用: の意味は, VBAは,.NET Framework (のクラス群)にはうまく対応できませんでしたが, 今度の Windows Runtime のクラス群には相性よく対応できるでしょう。 です。 ろひ さんの引用: .NET Framework のクラス群は, C# や 現世代のVB 言語向けに対してのみのクラス群でした。 Windows 8 以降では, 各言語に対して,もっとニュートラルなクラス群を提供して行こう というのが,あたらしいAPI設計のベースにあります。 具体的には, ニュートラルなクラス群で提供する機能と重複する .NET Framework の機能は, Metro向けの .NET Framework の中から削除される ニュートラルなクラス群で提供する機能は, 言語の種類によらず,どの言語からでも直に利用できる というような形になります。(あくまでも現地点での予定です) すでに,Windows 7 において, Windows API (Win32 API) の抽象化が行われ始めていて, 本当に基本的な機能を提供するものとその上位の機能を提供するものとを整理し始めています。 (機能のレベルが違うものが相互依存しないように整理し始めているということです) 最終的には, その基本的な機能を提供するAPIだけを使ったニュートラルなクラス群 を提供するのが目的でしょう。 ろひ さんの引用: 上で見たように, 今後提供するクラス群は,C++ からでも C# からでも,どちらからでも, それらクラス群が, それら言語にとってネイティブな形で利用できるようになるということです。 (VBA はC++側です) 非常にセンシティブな話題なので,まともにとらないで欲しいのですが, .NET無しでの高次元でのプログラミング という世界に移行し始めたということです。 今後提供するクラス群は,機能が被るものは, Metro用の .NET Framework から省かれます。 C++ は, 新しいクラス群が提供する機能は,それらクラスのものを利用し, 新しいクラス群が提供しない機能は,Windows API (Win32 API) を利用する。 C# は, 新しいクラス群が提供する機能は,それらクラスのものを利用し, 新しいクラス群が提供しない機能は,.NET Framework のクラスが提供する機能を利用する。 それでもやれないことは,Windows API (Win32 API) を利用する。 ろひ さんの引用: 上で見たように,Windows 自体がネイティブでクラス群を提供するようになります。 各言語は,参照設定するだけで,それらクラス群を使えるようになります。 VBA が,参照設定すれば,ADO(のクラス群)等を使えるようになるのと同じ感じです。 但し,現在,VBA が参照設定できるのは,タイプ ライブラリー と呼ばれているものだけです。 ご存知だと思いますが, タイプ ライブラリー とは,提供するクラス群や定数等が説明されているもので, COMクラスを説明するのに使われてきました。 で,今回は, 提供するクラス群や定数等を説明するのに, その タイプ ライブラリー は使わずに, メタファイル と呼ばれているものを使います。 メタファイルは,.NET系のクラス群を説明するために使われてきたものです。 残念なことに,現在のVBAでは,メタファイル内のクラス群を参照設定できません。 どうなるのか?は,次期Officeの発表までは,なんとも言えません。 参照設定ができるようになれば,C++ や C# 等と,少なくとも,同じ土俵には上がれます。 参照設定できるようになっても,Metro UI は使えません。 |
|
投稿日時: 11/10/23 10:43:08
投稿者: ろひ
|
---|---|
yayadonさん、丁寧な解説ありがとうございます。
yayadon さんの引用: まず、Windowsの背景・動向がこうだったのですね。MacOSが9からXに以降した後の黎明期(9のAPI群であるCarbonが抽象化されてCocoa環境に統合)を思い出しました。Windowsについても今後の整理・統合による発展を期待してしまいます。 yayadon さんの引用: yayadon さんの引用: こちらも前記のながれにおいて次期Officeに期待ですが、VBAがより様々で高度なクラス群を便利に活用できるようになりうるかの焦点は「メタファイル内のクラス群を参照設定できるか否か」がポイントだと理解しました。 yayadon さんの引用: C++とC#の存在理由はなんとなく理解していましたが、互いを見比べたとき、「どこへ向かおうとしているのか」が外からはわかりませんでしたので、謳う側の思想としては「.NET無しでの高次元でのプログラミング という世界に移行し始めた」なのだと理解しました。 ◇[HOWTO] Microsoft Office で Visual Basic for Applications から Visual Basic .NET クラス ライブラリを呼び出す方法 - Microsoftサポート http://msdn.microsoft.com/ja-jp/library/dd313957.aspx 上記のようなドキュメントもあるので、いったん期待するものの… ◇Microsoft Office と .NET の相互運用性 - MSDN http://msdn.microsoft.com/ja-jp/library/dd313957.aspx 「Office VBA から .NET Framework クラス ライブラリを呼び出す」の項で、「このアプローチにはいくつかの重要な制限事項があります。 それは、COM (Office VBA が基づいている) が .NET Framework クラス ライブラリの以下のものを認識しないためです。」(以下、その具体的事由が記述) …とされていることからも、Windows Runtimeが提供する(であろう)共通クラス(とWindows API(Win32 API))の動向や関係に着目していきたいと思います。 全体が俯瞰しやすくなる解説をいただき、ありがとうございました。 ≪補足≫ ◇VBAで.NET FrameworkのArrayListクラスとRandomクラスを使う - トキドキドキンドットコム http://tokidokidokin.com/2011/03/vba%E3%81%A7-net-framework%E3%81%AEarraylist%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%A8random%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%92%E4%BD%BF%E3%81%86/ (※VBA(VBS)からの.NET Frameworkクラスの利用がわかりやすくまとまっているので掲示。) |
|
投稿日時: 11/10/23 20:37:33
投稿者: yayadon
|
---|---|
そういえば,タイプライブラリが用意されていましたね。
|
|
投稿日時: 11/10/24 00:58:24
投稿者: yayadon
|
---|---|
# 知っている方は,スルーの方向で。
|
|
投稿日時: 11/10/24 02:53:22
投稿者: yayadon
|
---|---|
話は戻りますが,マコさんからの疑問が入っているので。
ふるふる さんの引用:varptr(Application.ActiveCell),varptr(Application.ActiveCell) 発言者の意図はわからないので,置いておいて, 自分が意図したのは,この場合,比べる値は,VarPtr でなく ObjPtr です。 ObjPtr() は,オブジェクト参照のデータ値を返す関数です。 VBA のオリジナル実装(MSの実装)では,仮想メモリのアドレス値を使っています。 ということで, ActiveCell と ActiveWorkbook の ObjPtr関数の値を比べてみます。 ActiveCell は,Rangeインターフェースのオブジェクト参照で, ActiveWorkbook は,Workbookインターフェースのオブジェクト参照です。 Rangeインターフェースのオブジェクト参照のデータ値と Workbookインターフェースのオブジェクト参照のデータ値を比べることになります。 MsgBox ObjPtr(Application.ActiveCell) = ObjPtr(Application.ActiveCell) MsgBox ObjPtr(Application.ActiveWorkbook) = ObjPtr(Application.ActiveWorkbook) 上は,False になって,下は True になります。 ですが,自分が意図したのは,そうことではなくて, 下が True になっているのは,たまたまです。 たまたまではなく,確実に断定するには, COM仕様的には,以下のコードのように IUnknownインターフェースのオブジェクト参照のデータ値を比べて, それらが同じ値かどうか?を確認することで,断定できます。 というのは, IUnknownインターフェースのオブジェクト参照のデータ値が同じ値の時に, 同じインスタンスだと保障されるように, オブジェクトの実装者は,実装する約束になっているからです。 また, COMオブジェクトは,IUnknownインターフェースを実装しないといけないことになっています。 なので, Rangeオブジェクトは,Rangeインターフェースの他に, IUnknownインターフェースも実装しています。 Workbookオブジェクトは,Workbookインターフェースの他に, IUnknownインターフェースも実装しています。 ObjPtr() を使って,インスタンスが同じか?を調べるには, 以下のように,一度,stdole.IUnknown へキャストというか, 実際は,Rangeオブジェクトに対して, IUnknownインターフェースのオブジェクト参照を渡してくださいと, QueryInterface します。 それらは,VBA が背後でやってくれます。 VBA の仕様的には,Set-型強制 (Set-Coercion) といいます。 '' IUnknownインターフェース用のオブジェクト変数 Dim unk1 As stdole.IUnknown Dim unk2 As stdole.IUnknown '' ActiveCellの場合 Dim rng1 As Range Dim rng2 As Range Set rng1 = Application.ActiveCell Set unk1 = rng1 ' Set-型強制 (Set-coercion) つまり QueryInterface Set rng2 = Application.ActiveCell Set unk2 = rng2 ' Set-型強制 (Set-coercion) つまり QueryInterface MsgBox ObjPtr(unk1) = ObjPtr(unk2) ' (A) '' ActiveWorkbookの場合 Dim wb1 As Workbook Dim wb2 As Workbook Set wb1 = Application.ActiveWorkbook Set unk1 = wb1 ' Set-型強制 (Set-coercion) つまり QueryInterface Set wb2 = Application.ActiveWorkbook Set unk2 = wb2 ' Set-型強制 (Set-coercion) つまり QueryInterface MsgBox ObjPtr(unk1) = ObjPtr(unk2) ' (B) (A) は,False になり,(B) は True になります。 先ほどと結果は同じですが,こちらが確実ということです。 注: 一度,Range型 や Workbook型 のオブジェクト変数に入れていますが, 直接 Set unk1 = Application.ActiveCell のようにしても同じです。 上のようなコードは,面倒なのと効率が良くない筈なので, Is演算子を使って, (A) と (B) は, MsgBox Application.ActiveCell Is Application.ActiveCell ' (a) MsgBox Application.ActiveWorkbook Is Application.ActiveWorkbook ' (b) のように一発で済むという話です。 該当レスのすぐ下の Abyssさんのレスで, QueryInterface の話が出てきているのは,そういう意味です。 繰り返しになりますが, Nothing は,オブジェクト参照用のリテラル (VBAのオリジナル実装(MSの実装)では,アドレス値 0 を使用して, オブジェクト参照のデータ値が何も指していないことを表すもの) なのと, Is演算子は,オブジェクト参照を扱う演算子です。 なので,Nothing は, MsgBox Nothing Is Application.ActiveCell MsgBox Nothing Is Nothing のようにも,使えます。 2つ目は意味はないですが,エラーにならず,しかも,True になります。 |
|
投稿日時: 11/10/25 12:58:34
投稿者: ふるふる
|
---|---|
>発言者の意図はわからないので,置いておいて,
|
|
投稿日時: 11/10/25 14:05:02
投稿者: 月
|
---|---|
今までの話の流れと無関係です。すみません。
|
|
投稿日時: 11/10/25 15:20:23
投稿者: yayadon
|
---|---|
ふるふる さんの引用: IsEqual という名前になっているのは,そっち系でしょうね。 |
|
投稿日時: 11/10/27 16:38:27
投稿者: kumatti
|
---|---|
・Excel 2010 x64でのメソッドのフック例
--------------------------- Microsoft Excel --------------------------- エラー 438 オブジェクトは、このプロパティまたはメソッドをサポートしていません。 --------------------------- OK --------------------------- 次のMsgBox --------------------------- Microsoft Excel --------------------------- エラー 438 オブジェクトは、このプロパティまたはメソッドをサポートしていません。 --------------------------- OK --------------------------- 実行結果 test.xlsm のIDispatch::Invoke (DispId=&HAF) が呼ばれました test.xlsm のIDispatch::Invoke (DispId=&HAF) が呼ばれました test.xlsm のIDispatch::Invoke (DispId=&HAF) が呼ばれました ↑実行時バインディング時はInvokeが2度呼ばれので。 コードの中身がよく分からない方は、実行されないでください。 ※ 過去ログからshiraさんのコードをx64用に変更。 Option Explicit Private Declare PtrSafe Function VirtualProtect Lib "kernel32" (ByVal lpAddress As Any, _ ByVal dwSize As LongLong, _ ByVal flNewProtect As Long, _ lpflOldProtect As Long) As Long Const PAGE_EXECUTE_READWRITE = &H40 Private Declare PtrSafe Sub CopyLongPtr Lib "kernel32" Alias "RtlMoveMemory" _ (Destination As Any, Source As Any, _ Optional ByVal Length As LongLong = 8) Private Declare PtrSafe Function DispCallFunc Lib "oleaut32" _ (ByVal pvInstance As LongPtr _ , ByVal oVft As Long, _ ByVal cc As Long, ByVal vtReturn As Integer, _ ByVal cActuals As Long, prgvt As Integer, _ prgpvarg As LongPtr, _ pvargResult As Variant) As Long Const CC_STDCALL = 4 Const DISP_E_MEMBERNOTFOUND = &H80020003 Const S_OK = &H0& Const DISPID_SAVECOPYAS = &HAF& Private m_pProc As LongPtr ' フックプロシージャのアドレス Private m_PrevProc As LongPtr ' 以前の関数ポインタ Private m_pVtbl As LongPtr ' 関数ポインタをセットしたアドレス ' フックプロシージャ(DISPID or 実行時バインド用) Private Function InvokeProc( _ ByVal This As Object, _ ByVal dispIdMember As Long, _ ByVal riid As Long, ByVal lcid As Long, _ ByVal wFlags As Integer, ByVal pDispParams As LongPtr, _ ByVal pVarResult As LongPtr, _ ByVal pExcepInfo As LongPtr, _ ByVal puArgErr As LongPtr) As Long On Error Resume Next Const PARAMCOUNT = 8 Dim Args() As Variant Dim pArgs(0 To PARAMCOUNT - 1) As LongPtr Dim vt(0 To PARAMCOUNT - 1) As Integer Dim vntResult As Variant Dim pObj As LongPtr Dim i As Long Dim hr As Long Dim wb As Workbook If dispIdMember = DISPID_SAVECOPYAS Then Set wb = This Debug.Print wb.Name; " の"; Set wb = Nothing Debug.Print "IDispatch::Invoke (DispId=&H" & _ Hex$(dispIdMember) & ") が呼ばれました" ' 戻り値で実行時エラーを発生させる ' (エラーにしたくなければ S_OK に変更) InvokeProc = DISP_E_MEMBERNOTFOUND Exit Function End If ' フック対象以外なら引き続き 本来のInvokeメソッドを呼び出す Args = VBA.Array(dispIdMember, riid, lcid, wFlags, pDispParams, _ pVarResult, pExcepInfo, puArgErr) For i = 0 To PARAMCOUNT - 1 vt(i) = vbLongLong pArgs(i) = VarPtr(Args(i)) Next 'vt(3) = vbInteger pObj = ObjPtr(This) ' 再帰呼び出しの発生に注意 EndInvokeHook hr = DispCallFunc(pObj, 8 * 6, CC_STDCALL, vbLongLong, _ PARAMCOUNT, vt(0), pArgs(0), vntResult) StartInvokeHook If hr < 0 Then InvokeProc = hr Else InvokeProc = vntResult End If End Function ' メモリ上の指定されたアドレスに64ビット値を書き込み Private Function ForceCopyLongPtr(ByVal Address As LongPtr, _ ByVal Value As LongPtr) As Boolean Dim lngOld As Long If VirtualProtect(Address, 8, _ PAGE_EXECUTE_READWRITE, lngOld) = 0 Then Exit Function End If CopyLongPtr ByVal Address, Value VirtualProtect Address, 8, lngOld, lngOld ForceCopyLongPtr = True End Function ' フック開始 (DISPID or 実行時バインド用) Sub StartInvokeHook() Dim unk As stdole.IUnknown Dim wb As Workbook Dim obj As Object Dim pObj As LongPtr Dim pVtbl As LongPtr If m_pVtbl Then Exit Sub ' すでに開始している ' 内側にあるWorkbookオブジェクトの参照を取得 Set unk = ThisWorkbook Set wb = unk Set obj = wb ' 念のため CopyLongPtr pObj, obj 'pObj=ObjPtr(obj) 'If pObj = 0 Then Exit Sub CopyLongPtr pVtbl, ByVal pObj pVtbl = pVtbl + 8 * 6 ' Invokeメソッドのオフセット CopyLongPtr m_PrevProc, ByVal pVtbl ' 以前のポインタを退避 m_pProc = VBA.CLngPtr(AddressOf InvokeProc) If Not ForceCopyLongPtr(pVtbl, m_pProc) Then Exit Sub m_pVtbl = pVtbl End Sub ' フック終了 Sub EndInvokeHook() Dim p As LongPtr If m_pVtbl = 0 Then Exit Sub CopyLongPtr p, ByVal m_pVtbl If p = m_pProc Then ' 念のためのチェック ForceCopyLongPtr m_pVtbl, m_PrevProc End If m_pVtbl = 0 End Sub Sub test() Dim wb As Workbook Dim obj As Object Set obj = ThisWorkbook Set wb = obj StartInvokeHook On Error GoTo Err_Handler obj.SaveCopyAs "C:\テスト02.xls" ' 実行時バインド wb.SaveCopyAs "C:\テスト01.xls" ' 事前バインド EndInvokeHook 'obj.SaveCopyAs "C:\これは保存される.xls" Exit Sub Err_Handler: MsgBox "エラー " & Err.Number & vbLf & Err.Description, vbCritical Resume Next End Sub |
|
投稿日時: 11/10/28 06:21:16
投稿者: yayadon
|
---|---|
DispCallFunc の宣言のところの ByVal oVft As Long
|
|
投稿日時: 11/10/28 08:19:57
投稿者: kumatti
|
---|---|
> ↑実行時バインディング時はInvokeが2度呼ばれので。
HRESULT DispCallFunc( void *pvInstance, ULONG_PTR oVft, CALLCONV cc, VARTYPE vtReturn, UINT cActuals, VARTYPE *prgvt, VARIANTARG **prgpvarg, VARIANT *pvargResult );> ByVal oVft As Long も LongPtr なのでは? yayadonさん、ご指摘ありがとうございます。 確かにそうですね。 ・権限昇格のコード CreateObject("Shell.Application").ShellExecute "excel.exe", """" & ThisWorkbook.FullName & """", vbNullString, "runas", 1 ThisWorkbook.Saved = True Application.Quit |
|
投稿日時: 11/10/31 08:12:24
投稿者: kumatti
|
---|---|
・LongLong型だと無理なお話
--------------------------- Microsoft Visual Basic for Applications --------------------------- コンパイル エラー: 型が一致しません。 --------------------------- OK ヘルプ --------------------------- Dim tmp1^(0 To 5103^) 'NG Dim tmp2^(0 To 5103@) 'OK Dim tmp3^(0 To 5103#) 'OK 配列の要素数の限界はいくつ? http://hpcgi1.nifty.com/MADIA/VBBBS/wwwlng.cgi?print+200306/03060015.txt # 添字の型はLong型だと今更知る。 |
|
投稿日時: 11/10/31 22:20:50
投稿者: simple
|
---|---|
割込み、失礼します。
|
|
投稿日時: 11/11/01 12:42:16
投稿者: 月
|
---|---|
simple さんの引用: 求めてみましたw自信はありませんw 他の人のことも考えて、答えはあえて載せません。 M(200)でもM(10000)でも一瞬で求まります( ̄ω ̄;) |
|
投稿日時: 11/11/01 12:57:02
投稿者: 月
|
---|---|
私からもクイズ。
|
|
投稿日時: 11/11/01 14:28:52
投稿者: ふるふる
|
---|---|
>このとき、M(10000)を求めてください。
Option Explicit Function Female(n) If n = 0 Then Female = 1 Else Female = n - Male(Female(n - 1)) End If End Function Function Male(n) If n = 0 Then Male = 0 Else Male = n - Female(Male(n - 1)) End If End Function Sub Calc() Dim num As Integer num = InputBox("Input M(Integer)?") MsgBox Male(num) End Sub ソースコードはきれいなんですけどね。 >「プログラマーはハロウィンとクリスマスを区別できない。なぜだろう」 「エキスパートCプログラミング」で読んだネタだったかな。 |
|
投稿日時: 11/11/01 14:36:13
投稿者: 月
|
---|---|
ふるふる さんの引用: なるほど〜。再帰はそうやって書くんですね。 私は再帰で書いてません。 ふるふる さんの引用: 本当ですね。 エキスパートCプログラミング http://www.amazon.co.jp/gp/product/toc/4756116396 |
|
投稿日時: 11/11/01 20:47:40
投稿者: simple
|
---|---|
月さん、ふるふるさん、コメントありがとうございました。
|
|
投稿日時: 11/11/01 21:31:20
投稿者: 月
|
---|---|
simple さんの引用: わーい、当たった。 https://gist.github.com/c38be47a990a9653e214 月 さんの引用: 答え 「OCT 31 = DEC 25だからだ」 https://twitter.com/_k18/status/130667610206515201 解説 OCT 31は8進数の31と読めます。 私たちが普段使っているのは10進数で、9の次は10です。つまり9の次に桁が上がります。 8進数では7の次が10です。つまり7の次に桁が上がります。 8進数の31を10進数に変換すると25になります。 DEC 25は10進数の25と読めます。 よって「OCT 31 = DEC 25」 となります。 |
|
投稿日時: 11/11/02 07:30:54
投稿者: simple
|
---|---|
どちらでもいいことに属しますが、前の発言で、
|
|
投稿日時: 11/11/08 08:43:34
投稿者: kumatti
|
---|---|
質問でなくて報告なので、こちらで。
cmp edx, 20Ah ;WM_MOUSEWHEEL jnz label xor rax, rax ret label: mov r10, 1111111111111111h ;DefSubclassProc sub rsp, 32 call r10 add rsp, 40 ret B ↓ リボン等のスクロールは可、右端の_、□、☓ボタンが効かない。 sub rsp, 56 cmp edx, 20Ah ;WM_MOUSEWHEEL jnz label xor rax, rax add rsp, 56 ret label: mov r10, 1111111111111111h ;DefSubclassProc call r10 add rsp, 56 ret VBA Option Explicit Private Declare PtrSafe Function SetWindowSubclass Lib "ComCtl32" (ByVal hWnd As LongPtr, ByVal pfnSubclass As LongPtr, ByVal uIdSubclass As LongPtr, ByVal dwRefData As LongPtr) As Long Private Declare PtrSafe Function RemoveWindowSubclass Lib "ComCtl32" (ByVal hWnd As LongPtr, ByVal pfnSubclass As LongPtr, ByVal uIdSubclass As LongPtr) As Long Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal length As LongPtr) Private Declare PtrSafe Function VirtualAlloc Lib "kernel32" (ByVal lpAddress As Any, ByVal dwSize As LongPtr, ByVal flAllocationType As Long, ByVal flProtect As Long) As LongPtr Private Declare PtrSafe Function VirtualFree Lib "kernel32" (ByVal lpAddress As Any, ByVal dwSize As LongPtr, ByVal dwFreeType As Long) As Long Private Declare PtrSafe Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr Private Declare PtrSafe Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleW" (ByVal lpModuleName As LongPtr) As LongPtr Const MEM_TOP_DOWN = &H100000 Const MEM_RELEASE = &H8000& Const PAGE_EXECUTE_READWRITE = &H40& Const MEM_RESERVE = &H2000& Const MEM_COMMIT = &H1000& Private vp As LongPtr '実行可能コードへのポインタ Const n1 = 22& 'DefSubclassProc Sub starteHook() Const code$ = "020AFA8138EC834848C03148087500001111BA49C338C483FF411111111111119090C338C48348D2" Dim hWnd As LongPtr, WndPtr As LongPtr, funcPtr As LongPtr Dim i&, j&, UB&, length&, slen& Dim lnglngCode^() vp = 0^ '初期化 hWnd = Application.hWnd slen = Len(code) UB = slen \ 16 length = slen + 1 ReDim lnglngCode(0 To UB - 1) For i = 1 To slen Step 16 lnglngCode(j) = "&H" & Mid$(code, i, 16) j = j + 1 Next '実行可能属性を持った領域を確保。 vp = VirtualAlloc(0&, length, MEM_RESERVE Or MEM_COMMIT, PAGE_EXECUTE_READWRITE) CopyMemory ByVal vp, lnglngCode(0), length funcPtr = GetProcAddress(GetModuleHandle(StrPtr("comctl32")), "DefSubclassProc") CopyMemory ByVal vp + n1, funcPtr, 8 'サブクラス化開始 SetWindowSubclass hWnd, vp, hWnd, 0 End Sub Sub endHook() Dim hWnd As LongPtr hWnd = Application.hWnd 'サブクラス化終了 RemoveWindowSubclass hWnd, vp, hWnd VirtualFree vp, 0, MEM_RELEASE End Sub |
|
投稿日時: 11/11/08 15:30:34
投稿者: Abyss
|
---|---|
SetWindowSubclassとDefSubclassProc関数は最初の4パラメーターが
cmp dx, 20Ah ;WM_MOUSEWHEEL jnz label xor rax, rax ret label: mov r10, qword 0h ;DefSubclassProc jmp r10 const code$ = "480275020afa8166000000ba49c3c031e2ff410000000000" const n1 = 13^ |
|
投稿日時: 11/11/09 07:40:25
投稿者: simple
|
---|---|
こんにちは。
|
|
投稿日時: 11/11/09 08:07:40
投稿者: kumatti
|
---|---|
Abyssさん、クロスアセンブルまでして頂きまして、
sub rsp, 56 cmp dword edx, 20Ah ;WM_MOUSEWHEEL jnz label xor rax, rax add rsp, 56 ret label: mov qword r10, qword 0 ;DefSubclassProc call qword r10 add rsp, 56 ret 元々、天才プログラマーの方のコードの模倣(解析)から 始まったので、凡人(私の事です)には 越えられない壁なのかなと既にあきらめています。 |
|
投稿日時: 11/11/09 12:02:06
投稿者: Abyss
|
---|---|
ダメでしたかぁ。
|
|
投稿日時: 11/11/11 06:21:16
投稿者: simple
|
---|---|
>お邪魔しました。
class Array def mixed_sort sort_by{|s| s.scan(/(\d+)|([^\d]+)/).map{|a| a[1] || a[0].to_i } } end end ary = ["X10Y1","X10Y10","X10Y2","X1Y1","X1Y10","X1Y2","X2Y1","X2Y10","X2Y2"] p ary.mixed_sort # => ["X1Y1", "X1Y2", "X1Y10", "X2Y1", "X2Y2", "X2Y10", "X10Y1", "X10Y2", "X10Y10"] が出力されます。 --------------------------- 配列を扱うクラスは、標準的、基本的なもので、 もちろん言語仕様として提供されているのですが、 Rubyでは、ユーザーが自由にそれにメソッドを追加できます。 (上ではmixed_sortというメソッドを Arrayクラス に追加しています。) この仕組みをオープンクラスと呼んでいます。 mixed_sortというメソッドの中身ですが、 sort_by{|s| s.scan(/(\d+)|([^\d]+)/).map{|a| a[1] || a[0].to_i } }の sort_by{|s| ・・・・ }の{| | ・・・ } 部分はブロックと呼ばれます。 ごくおおざっぱに言うと、sort_byというメソッドに 無名関数のようなものを渡している、と考えて良いと思います。 「配列の各要素 s に対して、ブロックの中で計算した結果」を基準にして、 ソートが行われます。 例えば、配列の一要素 "X10Y1"がブロック引数 s にセットされたあと、 s.scan(/(\d+)|([^\d]+)/).map{|a| a[1] || a[0].to_i }は何をしているかというと、 まず、s.scan(/(\d+)|([^\d]+)/) は、 /(\d+)|([^\d]+)/という正規表現を使って、 数字の連と、文字列を それぞれグループ として記憶し、 それを配列にしたものを返します。 "X10Y1"の場合、 [ [nil ,"X"] , [10, nil] ,[nil "Y"], [1,nil] ] という配列を返します。 さらに、それにmapというブロック付きメソッドを作用させると、 各要素にブロックを適用した結果を配列として返します。 a[1] || a[0].to_i は、 ・a[1]つまり文字列があれば、それ以下は計算せずにその文字列 a[1]を返し、 ・文字列がなければ、a[0]つまり数字を数値(integer)に変換したものを返します。 従って、[ "X", 10 , "Y", 1 ] が返ります。 配列の要素 ソートの判定に使われる配列 ---------- -------------------------- "X10Y1" [ "X", 10 , "Y", 1 ] "X10Y10" [ "X", 10 , "Y", 10 ] ・・・・・・・ "X2Y2" [ "X", 2 , "Y", 2 ]となりますから、 sort_byはこの配列を使ったソートを実行します。 Rubyは結局、よく使われる機能が言語仕様としてうまく備わっていますから、 その点がすっきり書ける原動力だと思います。 ちなみに、sort_byというメソッドは version 1.8 で追加された機能で、 それ以前は、sortしかなく、 C言語の qsort に比較関数のポインタを渡すのと同じ感じで、 大小関係を返すブロックをsortに渡すことが行われていました。 (場合によっては、速度向上のため、schwartz 変換を施すことが perlと同様行われていました。今はsort_byのお陰で、 その必要はありませんが。) ブロックという仕組みは、 関数1つを採る高階関数を実現する仕組みと言えます。 それが結構使い易い形で実現されていて、このブロックが Rubyの大きな利点、特徴かと思います。 関数型言語の一部機能をうまく取り入れていると思います。 (Rubyの開発者によるブロックについての解説が下記にあります。 http://itpro.nikkeibp.co.jp/article/COLUMN/20050930/221971/?ST=oss http://itpro.nikkeibp.co.jp/article/COLUMN/20050930/221978/?ST=oss http://itpro.nikkeibp.co.jp/article/COLUMN/20050930/221979/?ST=oss ) ---------------------------- それで、VBAではどう書くのでしょうか。 VBAで正規表現を書くのはRubyに比べると正直言って面倒です。 しかも、sortに相当するものがありませんから、大変です。 # 簡単かと思って発言してしまったのですが、 # 手間どりました。発言をいったん取り消そうかと思ったほどです。 もう少し時間を置こうかと思います。 # 一部にトンデモナイ間違いがありましたので修正しました。 # 時間感覚が麻痺していたみたいです。 |
|
投稿日時: 11/11/11 22:47:36
投稿者: simple
|
---|---|
コメントがないので、私案をあげてみます。
Sub test() Dim ary Dim re As Object Dim matches, m Dim s Dim myDigit As String, myStr As String Dim k As Long Dim p As Long Dim mat() As String Dim sc As Object Dim myList, temp Dim x ary = Array("X10Y1", "X10Y10", "X10Y2", "X1Y1", _ "X1Y10", "X1Y2", "X2Y1", "X2Y10", "X2Y2") Set re = CreateObject("VBScript.RegExp") re.Pattern = "(\d+)|(\D+)" re.Global = True Set sc = CreateObject("ScriptControl") sc.Language = "JScript" ' myList ; ary の各要素について、 ' 数字と文字に分離したものからなる配列 ' (ただし、最後に要素そのものを追加) ' を要素とする配列 Set myList = sc.eval("new Array()") For Each s In ary Set matches = re.Execute(s) p = 1 Set temp = sc.eval("new Array()") For Each m In matches myDigit = m.submatches(0) myStr = m.submatches(1) p = p + 1 If Len(myDigit) = 0 Then temp.push myStr Else temp.push Right(String(10, "0") & myDigit, 10) End If Next temp.push s myList.push temp Next '配列をソート CallByName myList, "sort", VbMethod '結果を配列にセット ReDim mat(UBound(ary)) p = 0 For Each x In myList mat(p) = CallByName(x, "pop", VbMethod) '最後の項目を取得 p = p + 1 Next Stop End Sub 自前でSort関数を書いてもいいのかもしれませんが。 |
|
投稿日時: 11/11/11 23:04:22
投稿者: Abyss
|
---|---|
> x64のMASMでも同様なのを確認しました。
|
|
投稿日時: 11/11/11 23:19:22
投稿者: n
|
---|---|
こんにちは。
|
|
投稿日時: 11/11/12 10:53:36
投稿者: simple
|
---|---|
nさん ありがとうございました。
|
|
投稿日時: 11/11/12 11:15:16
投稿者: kumatti
|
---|---|
simpleさんらしくないですね。
int StrCmpLogicalW( __in PCWSTR psz1, __in PCWSTR psz2 ); http://msdn.microsoft.com/en-us/library/windows/desktop/bb759947%28v=vs.85%29.aspx これは、Unicode版のみのAPIですよ。 StrPtrを使わないと。 |
|
投稿日時: 11/11/12 16:43:41
投稿者: n
|
---|---|
すみません、単純にSort時の比較の話でした。
Option Explicit Private Declare Function StrCmpLogicalW Lib "SHLWAPI.DLL" ( _ ByVal lpStr1 As String, _ ByVal lpStr2 As String) As Long Sub test() Dim i As Long Dim j As Long Dim mx As Long Dim tmp As String Dim ary ary = VBA.Array("X10Y1", "X10Y10", "X10Y2", "X1Y1", _ "X1Y10", "X1Y2", "X2Y1", "X2Y10", "X2Y2") mx = UBound(ary) For i = 0 To mx - 1 For j = i + 1 To mx If StrCmpLogicalW(StrConv(ary(i), vbUnicode), _ StrConv(ary(j), vbUnicode)) > 0 Then tmp = ary(i) ary(i) = ary(j) ary(j) = tmp End If Next Next Debug.Print Join(ary, vbLf) End Sub #Sort前後でまとめてStrConvしたほうが良いのかもしれませんが、とりあえず。 |
|
投稿日時: 11/11/12 19:15:50
投稿者: simple
|
---|---|
# ただ今、帰宅しました。
|
|
投稿日時: 11/11/12 21:42:00
投稿者: simple
|
---|---|
kumattiさんからご指摘いただいた方法を
|
|
投稿日時: 11/11/13 08:15:41
投稿者: kumatti
|
---|---|
PCWSTR型を別の書き方をすると、
const WCHAR*型です。
Private Declare Function StrCmpLogicalW Lib "SHLWAPI.DLL" ( _ ByVal lpStr1 As Long, _ ByVal lpStr2 As Longg) As Long です。 半角英数字しか処理しない前提なら、文字化けは起こらないのかもしれません。 (推奨される使い方ではないでしょうけど) |
|
投稿日時: 11/11/13 09:24:55
投稿者: simple
|
---|---|
ありがとうございました。動作確認できました。 |
|
投稿日時: 11/11/14 08:39:35
投稿者: kumatti
|
---|---|
失礼。Longgになっていました。
|
|
投稿日時: 11/11/15 14:11:23
投稿者: 月
|
---|---|
StrCmpLogicalW関数の代わりとなる関数を書いてみました。
|
|
投稿日時: 11/11/15 23:59:07
投稿者: simple
|
---|---|
kumatti さん、丁寧な応答、ありがとうございました。
files = Dir["*"] sorted = files.sort{|a,b| File.new(a).mtime <=> File.new(b).mtime}というのが一番素直は方法でした。 しかし、これだと比較するたびにFileオブジェクトが作られるので非効率。 そこで、 更新時刻とファイル名を連結した配列を要素に持つ一時配列を作っておいて、 それでソートを掛けて、そのあとで、2番目の要素(ファイル名)を取り出す、 files = Dir["*"].map{ |f| [test(?M,f),f] }.sort.map{|f| f[1]}などいうことをしていました。 これが発案者の名前に因む、シュワルツ変換です。(Dave Thomas氏の著作から引用) 今回の例も、 Set m(0) = Reg.Execute(a) を何度も繰り返すので、節約しようなどという気にもなるのですが、 多数のソートならいざ知らず、 簡潔なコードにしておいて、全体のわかりやすさを優先するほうが良いのだと思います。 それに速度優先なら、バブルソートではなく、別のソートのほうが効率がいいのでしょう。 今回のテーマとは別ですから、余計な話かもしれません。 |
|
投稿日時: 11/11/16 15:48:59
投稿者: 月
|
---|---|
simple さんの引用: やってみようと思いコードを書き始めたのですがうまくいきません。 Sub test2() Dim ary Dim Reg As RegExp Dim m As MatchCollection ary = VBA.Array("X10Y1", "X10Y10", "X10Y2", "X1Y1", _ "X1Y10", "X1Y2", "X2Y1", "X2Y10", "X2Y2") Set Reg = New RegExp Reg.Global = True Reg.MultiLine = True Reg.pattern = "^(\d+|[^\n\d]+)+$" Set m = Reg.Execute(Join(ary, vbLf)) Stop End Sub ここでmのSubMatchesに期待したものが入らないのです。 "X10Y1"で言えば、SubMatchesに["X", "10", "Y", "1"]の4つが入ることを期待したのですが、最後の"1"しか入っていませんでした。以上、進捗報告でした。 ちなみに、mixedCompare関数を少し修正しました。 変更後のコードのURLは変わりませんが、変更前のコードのURLはこちらです。 https://gist.github.com/1366050/82fdfe7663f6e2cdb1c72766e0e02e9cddb99415 |
|
投稿日時: 11/11/17 15:41:04
投稿者: 月
|
---|---|
月 さんの引用: どこを変更したの?と思いますよね。 変更した箇所を表示するWebサービスを作りました。 diff with url http://honda0510-y4q6xj6z.dotcloud.com/?old=https://raw.github.com/gist/1366050/34ef55a79dd93b419fcca40df8a517937976d998/Module1.bas&new=https://raw.github.com/gist/1366050/411ada541319ae18b8cb98898153453adbec3463/Module1.bas diff with url | 月ノート http://blog.honda0510.dotcloud.com/?p=661 |
|
投稿日時: 11/11/26 08:45:11
投稿者: kumatti
|
---|---|
・JScriptのtypeofの例
var o = new ActiveXObject("Excel.Application"); o.visible = true; o.Workbooks.Open("任意のパス");//\→\\ WScript.Echo(typeof(o.Application.ThisWorkbook) + '\t' + typeof(o.Application.Workbooks(1))); WScript.Quit(); ※ 内側のオブジェクトが評価されるケース。 |
|
投稿日時: 11/12/03 06:29:16
投稿者: yayadon
|
---|---|
# 古い話で恐縮なんですが...
引用: という表現ですが, この背後にある,正確な意味を解ってる人や, 他の言語も知っていて,この手の一般的な仕組みをわかっている人には, こういう表現でもいいんですが, VBA で初めてこの手のことを習う人に対して 仕様書的に,正確に表現するのならば, 任意の整数型 / 任意の整数型 -> Double / Double -> Double となります。 各演算子ごとに, 左右のオペランドの型に対応してそれぞれに決めてある有効な値型(effective value type)に オペランドの型が,Let-型変換(Let-coercion)されてから,演算が行われます。 他も同様で, Integer * Long -> Long や Long * Integer -> Long という感じではなく,仕様書的には, Integer * Long -> Long * Long -> Long や Long * Integer -> Long * Long -> Long という形になります。 あと,物理的な話も出てきていますが, Integer がオーバーフローすること と 物理的なメモリの扱い方からくる制限 のような,物理的に説明するような下位の概念は,言語仕様的には無関係です。 VBA は,高水準言語/高級言語 なので, 言語の記述自体は,抽象度が高いところで行われるので, 記述の仕方から起こる現象を物理的な話で説明するのは,多くの場合,ふさわしくないです。 それは, Dim obj As Object 略 Set obj = Nothing の Set obj = Nothing 説明の適否が × メモリを解放します。<--言語仕様の説明として,物理寄り過ぎる (しかも本当にメモリを解放するのかはオブジェクト依存) ○ オブジェクトを解放します。 となるのと同様です。 演算子の話の戻ります。 Dim a As Integer の a の型は,Integer なんですが, 変数の型のことを,仕様書では,宣言型(declared type) と呼んでいます。 中に入っている値そのものの型のことは,値型(value type)と呼んでいます。 (私が思うには,型の自動拡張があるVariant型が存在する関係で) 型の概念を,側 と 中身 の2つの型で区別しています。 VBA のヘルプでは,宣言型と値型の区別をしないで,型 と単に呼び, Variant型の値型(中身の型のこと)のことを,内部処理形式 として区別しています。 型(type) の意味が,値が取る得る範囲を制限するものという概念なのは同じです。 変数だけでなく, 関数(Function)にも宣言型があって,戻り値の型が宣言型になります。 で,同様に, 演算子にも宣言型があり,演算結果の型が宣言型になります。 一つの演算子には,扱うオペランドの型の違いによって,複数の宣言型があります。 変数の型(宣言型)から値がハミ出すとオーバーフロー エラーを報告するのと同様に, 演算子の宣言型から値がハミ出すとオーバーフロー エラーを報告する仕様になっています。 |
|
投稿日時: 11/12/08 15:42:55
投稿者: yayadon
|
---|---|
# 基本に興味のある方向け
Private Sub TestByRefLong(ByRef lngValue As Long) lngValue = 100 '何が渡ってきても,100 にする。 End Sub これを利用するコードも書いてみます。 Private Sub Test1() Dim lngValue As Long lngValue = 1 TestByRefLong lngValue MsgBox lngValue '(3) End Sub (3) の MsgBox は,100 と表示します。 いわゆる参照渡しのため,値が変更されています。 変数のアドレス値を返す VarPtr で変数のアドレス値を探ってみると, Private Sub TestByRefLong(ByRef lngValue As Long) MsgBox VarPtr(lngValue) '(2) 仮引数 (formal parameter) のアドレス値 lngValue = 100 '何が渡ってきても,100 にする。 End Sub Private Sub Test1() Dim lngValue As Long lngValue = 1 MsgBox VarPtr(lngValue) '(1) 実引数 (argument) のアドレス値 TestByRefLong lngValue MsgBox lngValue End Sub (1) の実引数(argument)側と (2) の仮引数(formal parameter)側は, 同じ値を示すハズです。 同じ変数なので,値が変更されるのも当然と言えます。 ただし,以下のように,lngValue + 1 したものを渡すと Private Sub TestByRefLong(ByRef lngValue As Long) MsgBox VarPtr(lngValue) '(2) lngValue = 100 ' 何が渡ってきても,100 にする。 End Sub Private Sub Test2() Dim lngValue As Long lngValue = 1 MsgBox VarPtr(lngValue) '(1) TestByRefLong lngValue + 1 '<--- 演算結果を渡す MsgBox lngValue '(3) <--- 当然 100 には,ならない。 End Sub (1) の実引数(argument)側と (2) の仮引数(formal parameter)側は, 異なる値を示すハズです。 (3) の MsgBox は,100 にはならず,1 にままになります。 (1) と (2) が,異なっているため, 操作している変数が別物のため,値が変わらない ということになります。 どういうことかというと, lngValue + 1 の結果を入れるための一時的な変数が作成されていて, (2) の仮引数は,その一時的な変数への参照になっているわけです。 今回は,演算の結果の値でしたが, メソッドやプロパティや関数の戻り値でも同様です。 素直に,参照渡しになるのは,変数を直接渡した時だけです。 ここまでは,経験則でわかることです。 ----- 次は,渡す変数の型を Long 型ではなく, Integer 型にして渡してみます。 Private Sub TestByRefLong(ByRef lngValue As Long) lngValue = 100 '何が渡ってきても,100 にする。 End Sub Private Sub Test3() Dim intValue As Integer 'この型だけ Integer に変更 intValue = 1 TestByRefLong intValue MsgBox intValue End Sub コンパイル エラーになります。 ByRef 引数の型が一致しません。 のエラー報告からわかるように,ByRef の時は, Variant型,Object型,固有クラス型 以外の型の場合は, 実引数の型と一致しないといけないことになっています。 仮引数 と 実引数 が実質同じ変数でなければいけないということは, 型も同じでないと困るということでしょう。 これは,仕様書で決まってなくても,なんとなくわかります。 今度は,本題の Variant 型 にしてみます。 Private Sub TestByRefVariant(ByRef vntValue As Variant) MsgBox VarPtr(vntValue) '(2) vntValue = 100 '何が渡ってきても,100 にする。 End Sub Private Sub Test4() Dim vntValue As Variant vntValue = 1 MsgBox VarPtr(vntValue) '(1) TestByRefVariant vntValue MsgBox vntValue '(3) 100 になる。 End Sub (1) の実引数(argument)側と (2) の仮引数(formal parameter)側は, 同じ値を示すハズです。 (3) の値も変更されて,100 になっているハズです。 変数を,直接,ByRef 引数 に渡していますし, 実際に,VarPtr の値も同じなので,納得できると思います。 変数を直接ではなく,演算結果や戻り値を渡した場合は, 先ほど見て,考えてみたように, 一時変数ができて,その参照になるために, 値は,変更されずに 1 のままになります。 このあたりは,Variant 型でも同じです。 ----- ここまでが,前提です。 ここで,実引数の変数の型だけを Long 型にしてみます。 つまり, Long型の変数を ByRef Variant型 引数に直接渡すと,どうなるか? ということを見てみます。 まず,前提として, ByRef 引数の 型 が,Variant 型の場合は, 引数の型が,実引数と仮引数で異なっていても, コンパイル エラーにならずに実行できるという仕様になっています。 Private Sub TestByRefVariant(ByRef vntValue As Variant) MsgBox VarPtr(vntValue) '(2) vntValue = 100 '何が渡ってきても,100 にする。 End Sub Private Sub Test5() Dim lngValue As Long lngValue = 1 MsgBox VarPtr(lngValue) '(1) TestByRefVariant lngValue MsgBox lngValue '(3) 100 になる。 End Sub (1) の実引数(argument)側と (2) の仮引数(formal parameter)側は, 異なる値を示すハズです。 素直に変数を渡しているのに,別物になっています。 そもそも,Variant/Object/固有クラス型以外の時は, 型が異なると,ByRef 渡しは成立せずエラーになっていたのは, 同じ変数を指さないと行けないからです。 ということは,異なる値を示したということは, 仮引数が参照しているものは,一時変数で,かつ, 型が同じもの,つまり,Variant型の一時変数 である可能性が高いことになります。 調べ方は省略しますが,実際に調べてみると, 16バイトのVariant型だということがわかります。 要するに, lngValue と vntValue が指すVariant型の一時変数 は,別物 なわけです。 で,別物であるにもかかわらず, (3) の値は変更されていて,100 になっているハズです。 それは,なぜだろうか?と考えると, 普段は意識しない Variant 型の側面(ある仕組み)が見えてくると思います。 |
|
投稿日時: 11/12/09 12:03:54
投稿者: yayadon
|
---|---|
# 時間がとれたので
------------------------------- Dim lngValue As Long 実引数 ↑参照 ByRef refValue As Long 仮引数 ------------------------------- Dim vntValue As Variant 実引数 ↑参照 ByRef refValue As Variant 仮引数 -------------------------------refValue は,変数を指すポインターであるが, refValue がVBA言語内で扱われる時は,直接渡された変数と実質同じものとなる。 ※ オブジェクト参照だけでなく,これも 参照(reference) と呼ばれていますが, 多くの場合,「ByRef 引数」のように表現される時が多いようです。 ◆ 型が同じ演算結果や戻り値が渡された場合 -------------------------------------- Dim lngValue As Long 実引数 ↓代入 Dim tmpValue As Long 一時変数 ↑参照 ByRef refValue As Long 仮引数 -------------------------------------- Dim vntValue As Variant 実引数 ↓代入 Dim tmpValue As Variant 一時変数 ↑参照 ByRef refValue As Variant 仮引数 --------------------------------------refValue は,一時変数(tmpValue)を指すポインターであるが, refValue がVBA言語内で扱われる時は,一時変数(tmpValue)と実質同じものとなる。 ◆ 型が異なる変数が直接渡された場合 ByRef 引数 の型が Variant/Object/固有クラス以外の時 コンパイル エラー になる。(ByRef 引数の型が一致しません) -------------------------------------- Dim intValue As Integer ← 例えば,Integer の時 ByRef refValue As Long ← 例えば,Long の時 -------------------------------------- ByRef 引数 の型が Variant の時 -------------------------------------- Dim lngValue As Long ← 例えば,Long の時 ↑参照 Dim tmpValue As Variant 一時変数 ↑参照 ByRef refValue As Variant --------------------------------------refValue は,一時変数(tmpValue)を指すポインターであるが, refValue がVBA言語内で扱われる時は,一時変数(tmpValue)と実質同じものとなる。 tmpValue の値は,変数 lngValue を指すポインター値。VBA的には参照。 そこで, Dim lngValue As Long ↑参照 Dim tmpValue As Variant の部分が, Dim lngValue As Long ↑参照 ByRef refValue As Long と実質同じになっていれば,良さそうです。 まず,次のコードを見てください。 Dim lngValue As Long Dim vntValue As Variant lngValue = 1 vntValue = lngValue の vntValue は,内部処理形式 が Long の Variant型になっています。 Variant 型の値は,Long型 の値 1 です。 Variant型の 内部処理形式 が ByRef refValue As Long のように なれば,ポインター として,参照 として振る舞えるようになれます。 で, 実際に Variant型 には,内部処理形式 の値として, 参照フラグ(VT_BYREF / &H4000 / 0x4000 ) というものがあります。 VBA にも, Variant型の 内部処理形式 の値を返す機能として, VarType というものがあります。 しかし,Long 型 等の値は返しますが, この参照フラグの部分の値は,返してくれません。 ByRef 引数 の取り扱い方からわかるように, VBA では,参照 と 実際の変数 の取り扱い方を区別しません。 そのため,参照フラグ が立っていても, 参照フラグ が立っていないものとして処理してくれます。 そのため,参照フラグ は,必要ないものとして, VarType では,&H4000 の部分の値は返さないことにしたのでしょう。 どういうことかというと, Variant型の 内部処理形式 が,参照フラグ + Long型 だった時は, あたかも Long型 だけのように振る舞ってくれるようになっています。 もう一度,以下を見てみます。 -------------------------------------- Dim lngValue As Long 実引数 例えば,Long の時 ↑ Dim tmpValue As Variant 一時変数は,参照フラグ が立っている。 ↑ ByRef refValue As Variant 仮引数 --------------------------------------仮引数 refValue は,あたかも 一時変数 tmpValue のように振る舞います。 そして,一時変数 tmpValue は, 参照フラグ が立っているために,あたかも 実引数 lngValue のように振る舞います。 その結果, 仮引数 refValue は,あたかも 実引数 lngValue のように振る舞うようになります。 つまり,参照 という仕組みにおいて, 参照の参照 は,参照 と同じように振る舞います。 もちろん, そのためにVBA側で,カラクリが仕込んであります。 IDE上で,表示される値を見ているとき,気が付かないですが, ByRef 引数 は,参照先変数 と実質同じものとして扱えていて, 参照先変数の値を見ています。 かつ,ByRef 引数 が Variant型 の場合, Variant型 の参照先変数で 参照フラグ が立っている時は, 表示されている値は,参照の参照の値,つまり,渡された変数の値 になっています。 Variant型 の参照先変数で 参照フラグ が立っていない時は, 表示されている値は,参照の値,つまり,一時的な変数の値 になっています。 Private Sub TestByRefVariant(ByRef vntValue As Variant) MsgBox VarPtr(vntValue) '(2) vntValue = 100 '何が渡ってきても,100 にする。 End Sub Private Sub Test5() Dim lngValue As Long lngValue = 1 MsgBox VarPtr(lngValue) '(1) TestByRefVariant lngValue + 1 '演算結果を渡す MsgBox lngValue '(3) 100 になる。 End Sub -------------------------------------------- Dim lngValue As Long ← 例えば,Long の時 lngValue + 1 ↓ ← 演算結果の代入(つまり,値のコピー) Dim tmpValue As Variant 一時変数は,参照フラグ が立っていない。 ↑ ByRef refValue As Variant -------------------------------------- 演算結果や戻り値は,参照フラグが立たず,値のコピーになります。 ----- ◆ ByRef 引数 ----- プロパティの場合 実は,パラメータに変数を直接渡しても,参照フラグが立たない場合があります。 Letステートメントの左辺式(l-expression) にパラメータがある場合です。 それは,どんな場合かというと,例えば, プロパティに値をセットするような場合で,かつ, そのプロパティの型が,ByRef 付き もしくは 何も無し※1 で宣言されている場合です。 ※1 参考書では,必ず習うので,いままで,説明しませんでしたが, ByRef も ByVal も付いていない時は,ByRef がついているものとみなされます。 ByRef String型のプロパティ タイプライブラリの定義だと,BSTR* つまり ポインター になっているものの場合や, ByRef Variant型 のプロパティ, タイプライブラリの定義だと,VARIANT* つまり ポインター になっているものの場合 など,ポインタ渡しでも, オブジェクト参照.Value = lngValue のようなプロパティへの代入時は,一時変数への値のコピーになります。 (1) Long -> ByRef Long ------------------------------------- Dim lngValue As Long ← 例えば,Long の時 オブジェクト参照.Value = lngValue ---------------------------------- lngValue ↓ ← 演算結果の代入(つまり,値のコピー) Dim tmpValue As Long 一時変数 ↑ ByRef refValue As Long ← 例えば,ByRef Long の時 ------------------------------------------------------------------- (2) Variant -> ByRef Variant Dim vntValue As Variant ← 例えば,Variant の時 オブジェクト参照.Value = vntValue ------------------------------------- vntValue ↓ ← 演算結果の代入(つまり,値のコピー) Dim tmpValue As Variant 一時変数は,参照フラグ が立っていない。 ↑ ByRef refValue As Variant ← 例えば,ByRef Variant の時 ------------------------------------------------------------------- (3) Long -> ByRef Variant Dim lngValue As Long ← 例えば,Long の時 オブジェクト参照.Value = vntValue ---------------------------------- lngValue ↓ ← 演算結果の代入(つまり,値のコピー) Dim tmpValue As Variant 一時変数は,参照フラグ が立っていない。 ↑ ByRef refValue As Variant ------------------------------------------------------------------- 最後に, 変数の直接渡しを,プロパティ と メソッド で比べてみます。 '' Class1 クラスモジュール Option Explicit 'プロパティ ByRef Long Public Property Let PropertyLongByRef(ByRef refValue As Long) refValue = 100 '参照先は一時変数なので,値は反映されません。 End Property 'メソッド ByRef Long Public Sub MethodLongByRef(ByRef refValue As Long) refValue = 100 '直接渡された変数ならば,値は反映されます。 End Sub 'プロパティ ByRef Variant Public Property Let PropertyVariantByRef(ByRef refValue As Variant) refValue = 100 '参照先のVariant型一時変数には 参照フラグは立ちません。 'なので,値は反映しません。 End Property 'メソッド ByRef Variant Public Sub MethodVariantByRef(ByRef refValue As Variant) refValue = 100 '参照先のVariant型一時変数には 参照フラグが立ちます。 'なので,値は反映されます。 End Sub Private Sub Test6() Dim myClass As Class1 Dim lngValue As Long Dim vntValue As Long Set myClass = New Class1 lngValue = 1 myClass.PropertyLongByRef = lngValue MsgBox lngValue ' 1 のまま lngValue = 1 myClass.MethodLongByRef lngValue MsgBox lngValue ' 100 になっている vntValue = 1 myClass.PropertyVariantByRef = vntValue MsgBox vntValue ' 1 のまま vntValue = 1 myClass.MethodVariantByRef vntValue MsgBox vntValue ' 100 になっている End Sub 感覚的にも, プロパティの方は,代入(Letステートメント)しているんだから, コピーになるのは,当然のように思えますが, 仕様書を見ると, MS-VBAL さんの引用: may とあるので,ByVal 渡しも含めて, パラメータへの渡し方において,セマンティクスの違いがあっても良い ことには,なっています。 上で,ByRef 引数 が,一時変数への参照 であるように表現していますが, 仕様書上のセマンティクス的には, それら2つをあわせて, プロシージャ内のローカル変数のようなスコープの扱い※2になっています。 どのように実装するかは,決められていなく, その変数/値が有効な範囲(スコープ/エクステント)と, どの型であるか?もしくは,どの型を持っているとして扱うか?のことだけが定義されいます。 注2: VBA 仕様書では,スコープ のことを エクステント(extent) と呼んでいるようです。 注3: 直書きしているので,コードが動かない場合は,適宜変更してください。 注4: Object型 と 固有クラス型 は,時間があれば,あとでまとめます。 |
|
投稿日時: 11/12/09 15:36:04
投稿者: yayadon
|
---|---|
補足
yayadon さんの引用: 例えば, ' ByRef 引数 Private Sub TestByRefLong(ByRef lngValue As Long) lngValue = 100 ' 何が渡ってきても,100 にする。 End Sub Private Sub Test2() Dim lngValue As Long lngValue = 1 TestByRefLong lngValue + 1 '<--- 演算結果(a value)を渡す End Sub のようなコードの場合のように, ByRef 引数 lngValue に, 変数(a variable)ではなく lngValue + 1 の演算結果(a value)を渡すような場合, Private Sub TestByRefLong(ByRef lngValue As Long) lngValue = 100 ' 何が渡ってきても,100 にする。 End Sub が,セマンティクス的には,ByVal 引数 lngValue であるかのように Private Sub TestByRefLong(ByVal lngValue As Long) lngValue = 100 ' 何が渡ってきても,100 にする。 End Sub のようなコードに解釈されて, その ByVal 引数 lngValue に lngValue + 1 の演算結果が Let-代入 される形になります。 このような解釈になるのは,ByVal 引数 も含めて区分けすると ・Optional 引数 に対して,実引数が省略された時 ・ByVal 引数 の時 ・ByRef 引数 かつ 渡される式が,値(演算結果のこと) 関数 プロパティ unboundメンバー に分類される時 になります。 純粋に ByRef 引数 のまま,つまり,参照渡し(referece parameter binding) になるのは, ・ByRef 引数 かつ 渡される式が 変数 に分類される時 になります。 cf. MS-VBAL p.86 ----- 先のレスも含めて,なんのことか,さっぱりわからない場合は, ・関数の ByRef 引数 に,実引数を渡す時,変数 以外の場合は, たとえ同じ型であっても,Let-代入 のためコピーが起きる ・ByRef 引数 の時,変数を直接渡す場合, 実引数の型が ByRef 引数と同じ場合は,参照渡しになる ・Variant 型 の ByRef 引数 の時,変数を直接渡す場合, 実引数が同じ型(Variant)の場合は,参照渡しになるが, それ以外の型の場合は,新規 Variant型変数 に参照が作成される ・プロパティに渡す場合は,変数を直接であっても Let-代入 の範疇に入るためコピーが起きる の4つを覚えておけばOKです。 |
|
投稿日時: 11/12/09 16:48:21
投稿者: yayadon
|
---|---|
# さらに補足
MS-VBAL さんの引用: MS-VBAL さんの引用: MS-VBAL さんの引用: 日本語訳:---------------------------------------------------------------- <予約名> は,あたかも,それがふつうにプログラム内で定義されたエンティティのように 式の中で使われる <予約識別子> である。 <special-form> は,あたかも,それがプログラム内で定義されたプロシージャ名であり,けれども,その引数に関しては,特別な構文規則をもっているかのように,式の中で使われる <予約識別子> である。 ------------------------------------------------------------------------- エンティティが,なんのことかよくわからないかもしれませんが, 関数などのプログラムの構成要素(p.24)の総称のことです。 要するに上記の 予約名 は,プログラムのなんらかの構成要素になっているものとして扱い, special-form は,関数呼び出しだが,引数の扱い方は特別な規則で扱う ということです。 実際には, 先の規則にそって呼び出すと, 関数呼び出しだと都合が悪いことが多々あったり, プロシージャとして呼び出す時, 先の規則のままだと都合が悪いかったりするので, どんなエンティティなのかをぼかしているということでしょう。 |
|
投稿日時: 11/12/09 18:06:25
投稿者: yayadon
|
---|---|
訂正
Private Sub Test6() Dim myClass As Class1 Dim lngValue As Long Set myClass = New Class1 lngValue = 1 myClass.PropertyLongByRef = lngValue MsgBox lngValue ' 1 のまま lngValue = 1 myClass.MethodLongByRef lngValue MsgBox lngValue ' 100 になっている lngValue = 1 myClass.PropertyVariantByRef = lngValue MsgBox lngValue ' 1 のまま lngValue = 1 myClass.MethodVariantByRef lngValue MsgBox lngValue ' 100 になっている End Sub です。 |
|
投稿日時: 11/12/11 23:38:53
投稿者: yayadon
|
---|---|
ByRef Variant のまとめ
Dim lngValue As Long lngValue = 1234567890 MsgBox Len(lngValue) '(1) MsgBox VBA.Len(lngValue) '(2) (1) は,4 になります。4 バイト ということでしょう。 素直に Long 型で扱っています。 (2) について考えてみます。 VBE7.DLL の宣言を見てみると,Len 関数は, VARIANT _stdcall Len([in] VARIANT* Expression); となっています。 VARIANT* は,VBA で表現すると ByRef Expression As Variant になるので, VBA.Len 関数の引数の型は,ByRef Variant になります。 渡された変数 lngValue の型は,Long 型で, VBA.Len 関数の引数の型は Variant 型ということは,上で見てきたことから, lngValue ↑(参照) variant型一時変数 参照フラグ + Long (C++ だと,VT_BYREF | VT_I4) ↑(参照) Expression となります。 その結果,VBA.Len関数 側から見ると, 渡された Long型 の変数ではなく,Variant型 の一時(or ローカル)変数に見えます。 Variant型が渡された場合は,文字列に型強制した時の 文字数 だったので, (2) の MsgBox は,10 と表示される筈です。 ----- 文字列の場合も理屈は同じになります。 例えば,MsgBox 関数は ByRef Varaint 型 をとり 以下のようなコード Dim strValue As String strValue = "ABC" MsgBox strVAlue は, strValue ↑(参照) Variant型一時変数 参照フラグ + String (C++ だと,VT_BYREF | VT_BSTR) ↑(参照) Expression の形になり,参照フラグが立つ一時変数が挟まり, 一方,以下のようなコード Dim vntValue As Variant vntValue = "ABC" MsgBox vntVAlue は,型が,Variant型 どうしで同じなので,直に参照になり vntValue ↑(参照) Expression の形になります。 なので, Dim strValue As String strValue = "ABC" MsgBox strValue と Dim vntValue As Variant vntValue = "ABC" MsgBox vntValue ならば, String型 の方が 参照フラグ + String (VT_BYREF | VT_BSTR) の Variant型変数の作成があるため (参照のため,文字列のコピーは起きませんが) 手数は多くなります。 文字列のコピーが起こるわけではないので,ほとんど変わりませんが, String型変数の方が,上記理屈どおり遅くはなります。 Private Declare Function QueryPerformanceCounter Lib "Kernel32" _ (lpPerformanceCount As Currency) As Long Private Declare Function QueryPerformanceFrequency Lib "Kernel32" _ (lpFrequency As Currency) As Long '' String型変数 => ByRef Variant Private Sub TestString() Dim ctr1 As Currency Dim ctr2 As Currency Dim freq As Currency Dim i As Long Dim m As Long Const IterationCount As Long = 100000 Dim strValue As String strValue = "ABC" Debug.Print "String型変数を直接" For m = 1 To 10 Call QueryPerformanceCounter(ctr1) For i = 1 To IterationCount Call VBA.CStr(strValue) 'String型 変数 直接 Next Call QueryPerformanceCounter(ctr2) Call QueryPerformanceFrequency(freq) Debug.Print Format((ctr2 - ctr1) / freq, "0.00000000E-"" sec.""") Next MsgBox "終了" End Sub '' Variant型変数 => ByRef Variant Private Sub TestVariant() Dim ctr1 As Currency Dim ctr2 As Currency Dim freq As Currency Dim i As Long Dim m As Long Const IterationCount As Long = 100000 Dim vntValue As Variant vntValue = "ABC" Debug.Print "Variant型変数を直接" For m = 1 To 10 Call QueryPerformanceCounter(ctr1) For i = 1 To IterationCount Call VBA.CStr(vntValue) 'Variant型 変数 直接 Next Call QueryPerformanceCounter(ctr2) Call QueryPerformanceFrequency(freq) Debug.Print Format((ctr2 - ctr1) / freq, "0.00000000E-"" sec.""") Next MsgBox "終了" End Sub ----- 以下のように,演算結果 を ByRef Variant に渡すもの Dim strValue As String strValue = "ABC" MsgBox strValue & "DEF" Dim vntValue As Variant vntValue = "ABC" MsgBox vntValue & "DEF" や 関数の戻り値 を ByRef Variant に渡すもの Dim lngValue As Long lngValue = 1234567890 MsgBox Hex$(lngValue) Dim lngValue As Long lngValue = 1234567890 MsgBox Hex(lngValue) は, 変数ではなく,値の代入になるので,話が変わってきます。 MsgBox でなく,VBA.CStr 等にすれば計測できますが, それに渡すまでにかかる時間が実際にはわからないので, 時間を計って確認してもあまり意味が無い気もするのでやめておきます。 ----- 文字列の連結の方は, vntValue & "DEF" は,演算時,strValue & "DEF" の演算になり, どちらも,文字列を結合した地点では,値型は String型(BSTR) です。 なので,そこからは,同じになります。 文字列どうしの演算になるとは,どういうことかというと, & 演算子は,オペランド vntValue を単純データ値として評価する必要があるため, 実装は,vntValue = 100 のように Integer が入っていれば, 文字列型の "100" に型強制をする必要があります。 そのため, 先に,内部処理形式(vt)のチェックが必要になるでしょうから, Variant型の方が理屈上は遅くなるハズです。 ----- 関数の戻り値の方は, 理屈で説明できるほど,ちょっとよくわかっていないのでやめておきます。 経験上は,ByRef Variant に対しては, 戻り値が Variant型 の方が速い気がいます。 |
|
投稿日時: 11/12/17 03:27:43
投稿者: yayadon
|
---|---|
自分も Enum について少し調べてみました。
MIDL さんの引用: 日本語訳: enum 型のオブジェクトは int 型であり,そのサイズはシステム依存である。既定では,enum 型のオブジェクトは,ネットワーク上を伝わる際は,unsigned short 型の16ビットオブジェクトとして取り扱われる。範囲 0 - 32,767 以外の値は実行時例外 RPC_X_ENUM_VALUE_OUT_OF_RANGE を引き起こす。 ----- 要点は, 実際のサイズは,システム依存だが, 値は,0 から 32,767 の値 つまり 0 から &H7FFF つまり, どのようなサイズでも, 0ビット目から14ビット目までが 1 になれるという意味のようです。 # The maximum number of identifiers is 65,535. と # 最上位(the most significant bit)の 15ビット目も 1 になれるようにかかれていますが, # リンク先上のツッコミも 15ビット目以上は 0 でないとエラーになるといっています。 上記訳中の ネットワーク上を伝わる際 というのは,各マシン間 だけでなく, 別プロセスとのやり取りも含まれます。 また,同一プロセス内でのやり取りでも, 別アパートメント間とのやり取りもこれに含まれます。 が,アパートメントがどういうものかわからない場合は, 意識する必要はないです。 要するに,ネットワーク上は 0 から 32,767 の値が許されるということです。 VBA の Enum の仕様 VBAL p.23 VBAL さんの引用: 日本語訳: Enum に固有の値型は存在しない。代わりに,Enum のメンバーは Long データ値として表わされる。 ----- VBA の場合,あくまで仕様上の話ですが,ビットでみると Long 値の 3 1 0 □□□□□□□□ □□□□□□□□ □■■■■■■■ ■■■■■■■■ の黒いところだけが,1 になれるようです。 # ネットワーク上は,ビッグエンディアン で流れるので, # 左側(先頭側)を 31ビット目にしてあります。 実例を見てみると... ----- タイプライブラリ ----- MsgBox の場合 (OLE/COM Object Viewer -> ファイル メニュー -> ViewTypeLib... -> %Program Files%\Common Files\microsoft shared\VBA\VBA7\VBE7.DLL) MsgBox は [entry("(null)"), helpcontext(0x000f6552)] VbMsgBoxResult _stdcall MsgBox( [in] VARIANT* Prompt, [in, optional, defaultvalue(0)] VbMsgBoxStyle Buttons, [in, optional] VARIANT* Title, [in, optional] VARIANT* HelpFile, [in, optional] VARIANT* Context); その戻り値 VbMsgBoxResult は, typedef [uuid(ED822012-6D7F-11CF-B949-00AA004455EA), helpcontext(0x0010fdf8)] enum { vbOK = 1, vbCancel = 2, vbAbort = 3, vbRetry = 4, vbIgnore = 5, vbYes = 6, vbNo = 7 } VbMsgBoxResult; typedef は,型 に 別名 を付けるもので, VBA 仕様上では固有の型が無い Enum に VbMsgBoxResult という 型名 を付けています。 Dim returnValue As 型名 とする時,例えば,MsgBox の場合は, 上記で見てきた仕様にそって実装されているハズなので, 王道?なのが VbMsgBoxResult で, 面倒なら Long で。 # kumattiさん と同じ意見だけど,これに限らず, # インテリセンスが効いた方がなんとなく正統派って気がする(笑)。 # でも,自分は Integer って書いてたかも(爆) |
|
投稿日時: 11/12/17 17:39:00
投稿者: kumatti
|
---|---|
・Application.GetOpenFilenameメソッド
[id(0x00000433), helpcontext(0x00020816)] HRESULT GetOpenFilename( [in, optional] VARIANT FileFilter, [in, optional] VARIANT FilterIndex, [in, optional] VARIANT Title, [in, optional] VARIANT ButtonText, [in, optional] VARIANT MultiSelect, [in, lcid] long lcid, [out, retval] VARIANT* RHS); うーん、[out, retval]属性や内側の戻り値(HRESULT型)をどう説明すべきか。 # yayadonさん、どうも。 |
|
投稿日時: 11/12/17 20:53:32
投稿者: yayadon
|
---|---|
訂正:
|
|
投稿日時: 11/12/18 08:52:11
投稿者: kumatti
|
---|---|
最後の引数が[out, retval]属性で定義されてるなら、VBAでは戻り値扱いになります。
yayadon さんの引用: エクスプローラだと環境変数でも開けますけど、OLE/COM Object Viewerでは開けませんでした。 |
|
投稿日時: 11/12/18 11:01:05
投稿者: yayadon
|
---|---|
kumatti さんの引用: こちら Windows 7 32bit だと開けるんですけどね。 64bitだと使えないんですね。 報告ありがとうございます。 |
|
投稿日時: 11/12/18 12:06:48
投稿者: kumatti
|
---|---|
失礼しました。
%ProgramFiles% でいいです。 WOW64での環境変数 http://d.hatena.ne.jp/espresso3389/20080129/1201612560 # 64Bit版だと、 --------------------------- OLE/COM Object Viewer --------------------------- LoadTypeLib( C:\Program Files\Common Files\Microsoft Shared\VBA\VBA7\VBE7.DLL ) failed. <No system message defined> STG_E_FILENOTFOUND ($80030002) --------------------------- OK --------------------------- で読み出せなかったので、32Bit版で試して、 (こちらの環境では) C:\Program Files (x86)\以下に該当のファイルがないので 開けなかっただけでした。 |
|
投稿日時: 11/12/20 19:36:10
投稿者: yayadon
|
---|---|
◆ 参照渡し: メソッドの戻り値([out, retval] 型* 引数)の場合
Dim myFile As Variant 'Variant 型に変更してあります。 myFile = Application.GetOpenFilename("CSVファイル(*.csv),*.csv") は,VBA 側が変数をいくつか用意することで, '引数省略時用 ----- 実は引数は省略できないため Public Const vtMissing As Variant = 初期化(VT_ERROR で値は DISP_E_PARAMNOTFOUND) 'ロケールとソート Public Const LOCALE_USER_DEFAULT As Long = &H400& ---------------------------------------------------------------------------- Dim myFile As Variant Dim myFileFilter As Variant 'or String Dim hr As Long '実際の戻り値 HRESULT を受ける変数 Dim tmpRtn As Variant 'VBA が用意した参照渡し戻り値用変数 myFileFilter = "CSVファイル(*.csv),*.csv" hr = Application.GetOpenFilename( _ myFileFilter, _ vtMissing, _ vtMissing, _ vtMissing, _ vtMissing, _ LOCALE_USER_DEFAULT, _ tmpRtn ) '<-- 戻り値の参照渡しの箇所 If hr がエラーを示すか? Then ' S_OK ( 0& ) と比較しているだけに見えるが... ' もろもろの処理 ' ここでエラーを発生させる等。 End If '成功時 myFile = tmpRtn ' 実際の戻り値用変数に代入(浅いコピー※) のような感じになります。 ※ 浅いコピー(shallow copy)とは, 変数の値が指す文字列や配列の実態のコピーまでは伴わないコピーのこと。 VBA では,通常は, 文字列の実態までコピーする深いコピー(deep copy)になります。 |
|
投稿日時: 11/12/21 10:46:04
投稿者: kumatti
|
---|---|
Microsoft Windows XP との支援技術の互換性をテストする
mt.exe -manifest DeclareDPIAware.manifest -outputresource:AccExplorer32.exe;1 # 我ながらよく捜したなと。 |
|
投稿日時: 11/12/22 01:59:45
投稿者: yayadon
|
---|---|
EXCELのシートをAccessのテーブルの様に扱うには
Dim cn As ADODB.Connection '(0) Set cn = New ADODB.Connection '(1) cn.Provider = "Microsoft.Jet.OLEDB.4.0" '(2) cn.Properties("Data Source").Value = "Excelファイルのパス" '(3) cn.Properties("Extended Properties").Value = "Excel 8.0;HDR=YES;" '(4) cn.Open '(5) cn.Close '(6) Set cn = Nothing '(7) 次の DLL A: ADO (msado15.dll) B: JETエンジン (msjet40.dll) & JET OLEDB (msjetoledb40.dll) & OLE DB Core (oledb32.dll) C: Excel ISAM ドライバ (msexcl40.dll) がロードされるタイミングは,どの行が実行される/された時なのか?まで わかっていたら JETエキスパート でしょう。 一つだけ,毎回アンロードされる DLL もあります。 カラクリ調査の三種の神器? OLE/COM Object Viewer Dependency Walker Process Explorer のうち,Process Explorer で確認できます。 呼び出し順は,仕組み的に見ると ADO → OLE DB Core Service → Jet 4.0 OLEDB Provider → Jet Engine → Excel ISAM Driver となっています。なので,ロードされる順はこの順になります。 # 今回は,実験のために個別にプロパティを設定していますが, # ConnectionString で一度に情報を提供してあげる方が好みです。 # また,変数で一度受けたインターフェースを # 再度 With で受けるのは,不必要な AddRef/Release を増やすだけなので, # 個人的にはお勧めしません。 |
|
投稿日時: 11/12/23 10:15:58
投稿者: kumatti
|
---|---|
simple さんの引用: 魔界の仮面弁士さんがそのコードを載せられていますね。 http://hpcgi1.nifty.com/MADIA/VBBBS/wwwlng.cgi?print+201112/11120007.txt |
|
投稿日時: 11/12/23 15:45:17
投稿者: simple
|
---|---|
ありがとうございます。参考になります。
Sub test() Dim sc As Object Set sc = CreateObject("ScriptControl") '「Set sc = CreateObject("MSScriptControl.ScriptControl")」でも OK 。 sc.Language = "jscript" Dim objArray As Object Set objArray = sc.eval("a=new Array()") sc.ExecuteStatement "function Push(s){if(!isNaN(parseInt(s)))a.push(s)}" sc.Run "Push", "1" sc.Run "Push", "5" sc.Run "Push", "2" sc.Run "Push", "3" sc.ExecuteStatement "a.sort(function(x,y){return x-y})" 'sc.eval "a.sort(function(x,y){return x-y})" Dim GetValues GetValues = CallByName(objArray, "toString", VbMethod) '「GetValues = objArray.toString()」でも OK 。 '「GetValues = objArray.ToString()」だと NG 。 Stop '確かにソートされてます。 End Sub ただ、11/11/11 22:47:36 で挙げた例では、 配列のどの要素が文字列になるか、数値になるかは動的に変化するので、 比較関数を明示的に書くのは難しいですね。 再帰的な比較関数ということになるのでしょうか。 かと言って、 仮に数字をevalで数値に変換して配列に代入して、 sortに任せると、 文字列と数値が混在した配列の場合では、 数値が自動的に文字列に変換されて、辞書式順序が適用されるようでした。 ("10"のほうが"2"より小さい) いずれにしましても、情報提供ありがとうございました。 |
|
投稿日時: 11/12/28 09:01:03
投稿者: kumatti
|
---|---|
クリップボードへのコピーをトリガーにしたい
|
|
投稿日時: 11/12/29 08:40:49
投稿者: kumatti
|
---|---|
http://www.moug.net/faq/viewtopic.php?t=61597
|
|
投稿日時: 11/12/29 08:49:15
投稿者: kumatti
|
---|---|
> Firefoxのmozctl.dll
|