2022年5月
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 31        
無料ブログはココログ

« 2008年4月 | トップページ | 2008年6月 »

2008年5月30日 (金)

XP SP3 で WSH が 5.7 になりました。

とは言え、何がどう変わったかのか分かりませんね。障害もそのままだし。:-(

2008年5月29日 (木)

IE7 で Shell と IE が分離されました。(その4)

Shell(Explorer) の場合には、
  Set ie=CreateObject("InternetExplorer.Application")

  Set ie=GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}")
に変えます。

この new:{clsid} モニカは、Windows 2000 や Windows XP などで使えるようです。

他の対処法としては、WSF にして、

<object id="ie" classid="clsid:C08AFD90-F2A1-11D1-8455-00A0C91F3880" />

とするか、レジストリに、

[HKEY_CLASSES_ROOT\Explorer.Application]

[HKEY_CLASSES_ROOT\Explorer.Application\CLSID]
@="{C08AFD90-F2A1-11D1-8455-00A0C91F3880}"

のような ProgID をでっち上げて、

Set ie=CreateObject("Explorer.Application")

で使います。

2008年5月28日 (水)

IE7 で Shell と IE が分離されました。(その3)

そのせいか、IE7 で、「お気に入り」や「リンク」からの起動が制限されました。

When you try to open a shortcut in Internet Explorer 7, the target executable file cannot be loaded

IE6 までは、「お気に入り」や「リンク」に、スクリプトや HTA を入れて、IE から起動できましたが、IE7では、ダウンロードのダイアログが出て、更にセキュリティの警告が出て、と大変なことになりました。

で、その対処法ですが、これも難しそうです。
1番目のダウンロードのダイアログは、どこかの設定を変えればよいのかも知れませんが、それには、きっと、セキュリティ上の問題があるのでしょう。
2番目のセキュリティの警告のほうは、署名を付けて、その署名を信頼してやればよいのでしょうが、それも面倒です。

ツールバーの「リンク」の場合は、右クリック、「プログラムから開く(H)」で(大概は、先頭の)アプリを起動します。
「リンク」の場合は、制限されるのは、「開く」だけなので、同じ内容の関連付けを別のキーで作れば、右クリックで起動できます。

エクスプローラバーの「お気に入り」の場合は、打つ手なし。
もし、「お気に入り」の「フォルダショートカット」を「リンク」の中に作れば、ツールバーの「リンク」の延長で「お気に入り」が使えます。
ただし、再帰の無限ループなど、「フォルダショートカット」は、トラブルの元だったような。。。
printキーを潰して、印刷の代わりに起動する手もありますが。。。

なので、フォーカスが移ってよいなら、タスクバーから起動するようにするか、どうしてもフォーカスを変えたくなければ、コンテキストメニュー拡張にするしかないでしょう。

2008年5月27日 (火)

IE7 で Shell と IE が分離されました。(その2)

今まで (IE6) は、Shell.ApplicationのWindows().Item() で、現在または最後にアクティブな IE が捕捉できましたが、それができなくなりました。:-(

Shell.ApplicationのWindows().Item() で捕捉できるのは、現在または最後にアクティブな Shell だけです。

それを利用していたスクリプト類は、全滅です。:-(

で、その対処法ですが、難しそうですね。駄目っぽい。

「現に」アクティブな IE の場合には、
  Set ie=CreateObject("Shell.Application").Windows().Item()

  Set Shell=CreateObject("Shell.Application")
  For Each ie In Shell.Windows()
    If TypeName(ie.Document)="HTMLDocument" Then If ie.Document.hasFocus() Then Exit For
  Next
に変えます。
ただし、フォーカスを Web ページ部分に置いておく必要があります。
もし、フォーカスが外れてると、見つけ損ないます。

HTA スクリプトは、フォーカスを奪うので、この手は使えません。

「最後にアクティブ」の部分は、打つ手なし、です。

今まで (IE6) は、これを利用して、ブックマークレットっぽいことが出来ましたが、IE7 ではもう無理みたい。
これからは、コンテキストメニュー拡張でやるしかないかなぁ~。

2008年5月26日 (月)

IE7 で Shell と IE が分離されました。

今まで (IE6) は、IE (IExplore.exe) でフォルダを開いたり、Shell (Explorer.exe) で Web ページを開いたり、相互乗り入れが出来ましたが、それが出来なくなりました。:-(

KB928675 Windows シェルから Internet Explorer 7 の分離

それを利用していたスクリプト類は、全滅です。:-(

で、その対処法ですが、Shell(Explorer)の場合には、
  Set ie=CreateObject("InternetExplorer.Application")

  Set ie=GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}")
に変えます。

VBAなどでは、
  C:\WINDOWS\system32\shdocvw.dll
  Microsoft Internet Controls
を参照設定して、
  Dim ie As SHDocVw.ShellBrowserWindow
  Set ie = New SHDocVw.ShellBrowserWindow
を使います。

これは、まだ、対処法があるのでよいけれど。。。

2008年5月25日 (日)

未初期化の配列を空の配列のように扱うUBound()の代替関数

Option Base {0|1}にも対応します。

Function UpperBound(a)
UpperBound = UBound(Array())
On Error Resume Next
UpperBound = UBound(a)
End Function

または、

Function UpperBound(a, Optional n = 1)
UpperBound = UBound(Array())
On Error Resume Next
UpperBound = UBound(a, n)
End Function

2008年5月24日 (土)

Scripting.Dictionary を配列の代わりに使う。(補足)

配列の代用として使う場合は、インデックスの抜けを避けて使います。

もし、抜けがある場合は、追加インデックスに a.Keys()(a.Count-1)+1 を使います。

a.Add a.Keys()(a.Count - 1) + 1, f

または、

a.Item(a.Keys()(a.Count - 1) + 1) = f

ただし、これは、a.Count=0 のときに、エラーになるので、

If a.Count Then k = a.Keys()(a.Count - 1) + 1 Else k = 0

2008年5月23日 (金)

VBA関数を呼び出すだけの構文は?

LBound()は、VBScriptではグローバルオブジェクトのメソッドなので、
  Call LBound(a)
で呼べますが、VBAではVBA関数なので?、Callだとエラーになります。
  r = LBound(a)
なら、よいようですが、ダミー変数が必要です。
  If LBound(a) Then:
これなら、ダミー変数は不要です。VBA/VBScript共通に使える構文です。

2008年5月22日 (木)

VBScriptやVBAの配列が初期化されているか? 配列の次元数は?

VBScriptやVBAでは、以下のようにLBound()のエラーを拾う。のが普通のやり方みたいです。

Function NumberOfDimensions(a)
NumberOfDimensions = -1
On Error Resume Next
Do
  NumberOfDimensions = NumberOfDimensions + 1
  If LBound(a, NumberOfDimensions + 1) Then:
Loop Until Err
End Function

ここで、次元数=0が、「配列が初期化されてない。」の意味です。

ところで、JScriptには、そういうメソッドがあります!えっ?

Function NumberOfDimensions(a)
Dim sc As Object
Set sc = CreateObject("ScriptControl")
sc.Language = "JScript"
sc.AddCode "function NumberOfDimensions(a){return new VBArray(a).dimensions();}"
NumberOfDimensions = sc.CodeObject.NumberOfDimensions(a)
End Function

2008年5月21日 (水)

VBAの配列に不定数の要素を追加するには?(その4)

空の配列も、未初期化の配列も、エラーハンドリングも使わないで、なぜか、逆説的に、デクレメントやEraseを使う方法です。

Dim a() As String
Dim f As String
ReDim a(0)
f = Dir("*")
Do While Len(f)
  a(UBound(a)) = f
  ReDim Preserve a(UBound(a) + 1)
  f = Dir()
Loop
If UBound(a) Then
  ReDim Preserve a(UBound(a) - 1)
Else
  Erase a
End If
MsgBox Join(a, vbLf)

これも、要素がないと、後で使うときにエラーになるので、要素が必ず存在するような場合に使うとよいでしょう。

或いは、配列の最後に空の要素を余分に持つというコンベンションにすれば、デクレメントやEraseは不要です。
For k = 0 To UBound(a) - 1
で回す。とか、
Join(a, vbLf)
で末尾にも改行が付いて、これはこれで便利です。

2008年5月20日 (火)

VBAの配列に不定数の要素を追加するには?(その3)

Variant型の空の配列は、
  Dim a As Variant
  a = Array()
で作れますが、Variant型以外のデータ型の場合は?

String型は、
  Dim a() As String
  a = Split("")
Byte型は、
  Dim a() As Byte
  a = ""
で作れますが、Long型などは、空の配列が作れません。

もし、空の配列の代わりに、未初期化の配列を使うと、UBound(a)がエラーになります。

そこで、空の配列の代わりに、未初期化の配列を使うやり方。

Dim a() As String
Dim n As Long
Dim f As String
f = Dir("*")
Do While Len(f)
  n = -1
  On Error Resume Next
  n = UBound(a)
  On Error GoTo 0
  ReDim Preserve a(n + 1)
  a(UBound(a)) = f
  f = Dir()
Loop
MsgBox Join(a, vbLf)

さすがに手順がちょっと多いので、別にPush関数を作ってやるほうがよいでしょう。

Dim a() As String
Dim f As String
f = Dir("*")
Do While Len(f)
  Push a, f
  f = Dir()
Loop
MsgBox Join(a, vbLf)

Sub Push(Items, Item)
Dim n As Long
n = -1
On Error Resume Next
n = UBound(Items)
On Error GoTo 0
ReDim Preserve Items(n + 1)
Items(UBound(Items)) = Item
End Sub

ただし、未初期化のままだと、後で使うときにエラーになるので、要素が必ず存在するような場合に使うとよいでしょう。

なので、要素がないこともあるときは、空の配列が作れるデータ型なら、それで。
空の配列が作れないデータ型なら、できればVaiant型にしたほうがよいでしょう。

2008年5月19日 (月)

VBScriptやVBAの配列に不定数の要素を追加するには?(その2)

Scripting.Dictionaryを配列の代わりに使います。

Dim a As Object
Dim f As String
Set a = CreateObject("Scripting.Dictionary")
f = Dir("*")
Do While Len(f)
  a.Add a.Count, f
'または、好みで、
'  a.Item(a.Count) = f
  f = Dir()
Loop
MsgBox Join(a.Items(), vbLf)

push()メソッド同様に1行で書けます。

配列の代わりにScripting.Dictionaryを使うときのポイントは、
Dictionaryのキーにインデックス(0~)を使います。
配列でUBound(a)+1と書く代わりにDictionaryでa.Countを使います。
配列でa(k)と書く代わりに、Dictionaryでa.Item(k)と書きます。
Dictionaryがa.Items()で配列になります。
For Each k In aで取り出せるのはキー。a.Item(k)で値。
For Each x In a.Items()で値。

2008年5月18日 (日)

VBScriptやVBAの配列に不定数の要素を追加するには?

例えば、VBAのDir()関数でファイルを列挙して配列に格納するには、

Dim a As Variant
Dim f As String
a = Array()
f = Dir("*")
Do While Len(f)
  ReDim Preserve a(UBound(a) + 1)
  a(UBound(a)) = f
  f = Dir()
Loop
MsgBox Join(a, vbLf)

JScriptならArrayオブジェクトのpush()メソッドで簡単なのですが、VBScriptやVBAの配列では2行になります。

もし、push()メソッド相当のPush関数を別に作ってやれば、1行になります。

Dim a As Variant
Dim f As String
a = Array()
f = Dir("*")
Do While Len(f)
  Push a, f
  f = Dir()
Loop
MsgBox Join(a, vbLf)

Sub Push(Items, Item)
ReDim Preserve Items(UBound(Items) + 1)
Items(UBound(Items)) = Item
End Sub

ここでは、空の配列を作るために、Array()を使用しているので、使えるのは、Variant配列だけです。:-(

2008年5月17日 (土)

VBScriptやVBAからJScriptのArrayオブジェクトのインデックスアクセス(その3)

ラッパ関数を介して、インデックスアクセスするのが、お勧めです。

Set sc=CreateObject("ScriptControl")
sc.Language="JScript"
sc.AddCode "function set(a,k,v){a[k]=v;}function get(a,k){return a[k];}"

参照は、
  x=a[k]
の代わりに、
  x=sc.CodeObject.get(a,k)
または、
  x=sc.Run("get",a,k)

設定は、
  a[k]=x
の代わりに、
  call sc.CodeObject.set(a,k,x)
または、
  call sc.Run("set",a,k,x)

性能は、o(n)なので、n=a.length が大きくても、使えます。

また、Run()よりCodeObjectのほうが速くてよいでしょう。

2008年5月16日 (金)

VBScriptやVBAからJScriptのArrayオブジェクトのインデックスアクセス(その2)

slice()/splice()を使って、見た目、スマートにインデックスアクセスできます。でも、隠れメタボかも?

参照は、
  x=a[k]
の代わりに、
  x=a.slice(k,k+1).pop()

設定は、
  a[k]=x
の代わりに、
  call a.splice(k,1,x)

ただし、性能は、o(n**2)なので、n=a.length が大きいときは使わないほうがよいでしょう。

2008年5月15日 (木)

VBScriptやVBAからJScriptのArrayオブジェクトのインデックスアクセス

VBScriptの配列は、a(k)でインデックスアクセスします。
一方、JScriptのArrayオブジェクトは、JScriptで、a[k]でインデックスアクセスします。
では、JScriptのArrayオブジェクトを、VBScriptやVBAで、どうやってインデックスアクセスするか?

既存のインデックスなら、プロパティとして、a.[0]でアクセスできますが。。。

参照は、
  x=a[k]
の代わりに、
  x=Eval("a.[" & k & "]")
また、VBAなら、
  x = CallByName(a, k, VbGet)

設定は、
  a[k]=x
の代わりに、
  Execute "a.[" & k & "]=x"

しかし、EvalやExecuteは使いにくいですね。新規のインデックスにも使えないし。

2008年5月14日 (水)

JScriptのArrayオブジェクトからの取り出し(pop/shift)の代替

取り出し(pop/shift)は遅いので、For Eachで列挙するのが速くてよいでしょう。

Set sc=CreateObject("ScriptControl")
sc.Language="JScript"
set a=sc.Eval("new Array()")
t1=Timer
For k=1 To 1024*512
  a.push k
Next
MsgBox Timer-t1
'10.35156

t1=Timer
For Each x In a
Next
MsgBox Timer-t1
'0.3046875

もし、インデクスで取り出したければ、For Eachで配列に転写するか、Evalを使うとよいでしょう。

t1=Timer
redim b(a.length-1)
k=-1
For Each x In a
  k=k+1
  b(k)=x
Next
MsgBox Timer-t1
'1.210938

t1=Timer
For k=1 To a.Length
  x=Eval("a.[" & k-1 & "]")
Next
MsgBox Timer-t1
'13.78906

VBAの場合は、CallByName()も使えます。

t1 = Timer
For k = 1 To CallByName(a, "length", VbGet)
  x = CallByName(a, k - 1, VbGet)
Next
Debug.Print Timer - t1
'1.8125

2008年5月13日 (火)

JScriptのArrayオブジェクトからの取り出し(pop/shift)は、ともに遅い。

JScriptのArrayオブジェクトから取り出すとき、pop()も遅いが、shift()はもっと遅い。

Set sc = CreateObject("ScriptControl")
sc.Language = "JScript"
Set a = sc.Eval("new Array()")
t1 = Timer
For k = 1 To 1024 * 16
  a.push k
Next
MsgBox Timer - t1
'0.28125

t1 = Timer
For k = 1 To a.length
  x = a.pop()
Next
MsgBox Timer - t1
'23.76172

t1 = Timer
For k = 1 To a.length
  x = a.shift()
Next
MsgBox Timer - t1
'101.3867

pop()も、shift()も、共にo(n**2)みたい。

より遅いshift()は、使わないほうがよいかも。
もし、必要なら、reverse()とpop()で代替したほうがよいでしょう。

2008年5月12日 (月)

JScriptのArrayオブジェクトへの追加では、unshift()が遅い。

JScriptのArrayオブジェクトに追加するとき、push()はそれほどでもないが、unshift()はとても遅い。

t1 = Timer
Set sc = CreateObject("ScriptControl")
sc.Language = "JScript"
Set a = sc.Eval("new Array()")
For k = 1 To 1024 * 512
  a.push k
Next
MsgBox Timer - t1
'9.523438

push()は、VBScriptやVBAの配列をReDimで漸増するより速いので、その代替に使えます。

t1 = Timer
Set sc = CreateObject("ScriptControl")
sc.Language = "JScript"
Set a = sc.Eval("new Array()")
For k = 1 To 1024 * 8
  a.unshift k
Next
MsgBox Timer - t1
'5.617188

unshift()は、怖ろしく遅いので使うべきでないかも。

push()は、o(n)だけど、unshift()は、o(n**2)みたい。

もし、必要なら、push()とreverse()で代替したほうがよいでしょう。

2008年5月11日 (日)

VBScriptの配列は、漸増が遅い。

VBScriptの配列をReDimで伸縮するとき、漸減はそれほどでもないが、漸増はとても遅い。

t1 = Timer
ReDim a(1024 * 512)
For k = 1024 * 512 To 1 Step -1
  ReDim Preserve a(k - 1)
Next
MsgBox Timer - t1
'1.011719

t1 = Timer
a = Array()
For k = 1 To 1024 * 512
  ReDim Preserve a(k)
Next
MsgBox Timer - t1
'31.84375

束で増やすようにすれば、それなりに速くなります。
もし最大値が予想できるなら、最初にどんと大きく作って、最後に小さく調整するとよいでしょう。

2008年5月 8日 (木)

VBScriptやVBAのSplit()関数の代替にJScriptを使う。

ScriptControl経由でJScriptのStringオブジェクトのsplit()メソッドを使う。

split()メソッドの結果は、JScriptのArrayオブジェクトなので、これをVBScriptの配列に変換します。

a=String(1024*1024,"a")
t1=Timer
Set sc=CreateObject("ScriptControl")
sc.Language="JScript"
sc.AddCode "function split(s,p){return s.split(p);}"
set b=sc.CodeObject.split(a,"a")
Dim c()
ReDim c(b.length-1)
k=0
For Each d In b
  c(k)=d
  k=k+1
Next
MsgBox Timer-t1
'4.148438

For Eachで使う分には、VBScriptの配列にする必要はなく、JScriptのArrayオブジェクトのまま使えば、もっと速い。

a=String(1024*1024,"a")
t1=Timer
Set sc=CreateObject("ScriptControl")
sc.Language="JScript"
sc.AddCode "function split(s,p){return s.split(p);}"
b=sc.CodeObject.split(a,"a")
MsgBox Timer-t1
' 1.609375

JScriptのArrayオブジェクトのまま使うのが、お勧めです。

VBScriptの配列にするなら、代替関数のほうがよいでしょう。

2008年5月 7日 (水)

VBScriptやVBAのSplit()関数の代替関数

代替関数のほうが、速い。とは、情けない。

a = String(1024 * 1024,"a")
t1 = Timer
b = Splitx(a, "a")
MsgBox Timer - t1
' 7.492188

Function Splitx(s, p)
Dim a(), n, b, e, f
ReDim a(Len(s))
b = 1
n = 0
f = InStr(b, s, p)
Do While f
  a(n) = Mid(s, b, f - b)
  n = n + 1
  b = f + Len(p)
  f = InStr(b, s, p)
Loop
a(n) = Mid(s, b)
ReDim Preserve a(n)
Splitx = a
End Function

2008年5月 6日 (火)

VBScriptやVBAのSplit()関数も、チョー遅い!

Split()関数もReplace()関数と同程度に遅いですね。

a = String(1024 * 1024,"a")
t1 = Timer
b = Split(a, "a")
MsgBox Timer - t1
' 32.125

同程度に遅いのでは、Replace()の代替には使えません。

a=String(1024*1024,"a")
t1=Timer
b=Join(Split(a,"a"),"b")
MsgBox Timer-t1
' 33.125

と言うか、こんなに性能が近いのは、Replace()の実装が、実は、Join(Split())だったりして?

それにしても、ひどい実装です。

Replace()関数は、高速なRegExpのReplace()メソッドで代替できるのでよいけれど、Split()関数の代替はどうしましょう?

2008年5月 5日 (月)

VBAのReplace()関数の代替関数

これも同様です。性能は、中くらい!
なので、これより、"VBScript.RegExp"のReplace()メソッドがお勧めです。

a = String(1024& * 1024&, "a")
t1 = Timer
b = Replacex(a, "a", "b")
Debug.Print Timer - t1
' 2.523438

Function Replacex(s, p, r)
Dim a() As String, n As Long, b As Long, e As Long, f As Long
ReDim a(Len(s))
b = 1
n = 0
f = InStr(b, s, p)
Do While f
  a(n) = Mid(s, b, f - b)
  n = n + 1
  b = f + Len(p)
  f = InStr(b, s, p)
Loop
a(n) = Mid(s, b)
ReDim Preserve a(n)
Replacex = Join(a, r)
End Function

2008年5月 4日 (日)

VBAのReplace()関数も、チョー遅い!

VBAのReplace()関数も、VBScriptと同じ実装のようです。というか、VBAが先に遅い実装をした?

a = String(1024& * 1024&, "a")
t1 = Timer
b = Replace(a, "a", "b")
Debug.Print Timer - t1
' 32.875

同様に、"VBScript.RegExp"のReplace()メソッドは、チョー速い!

a = String(1024& * 1024&, "a")
t1 = Timer
Set re = CreateObject("VBScript.RegExp")
re.Global = True
re.Pattern = "a"
b = re.Replace(a, "b")
Debug.Print Timer - t1
' 0.203125

2008年5月 3日 (土)

VBScriptのReplace()関数の代替関数を作る。

代替関数を作ってはみたものの、性能は、中くらい!(:-p)
RegExpのReplace()メソッドが一番ですね。

a = String(1024 * 1024,"a")
t1 = Timer
b = Replacex(a, "a", "b")
MsgBox Timer - t1
' 5.125

Function Replacex(s, p, r)
Dim a(), n, b, e, f
ReDim a(Len(s))
b = 1
n = 0
f = InStr(b, s, p)
Do While f
  a(n) = Mid(s, b, f - b)
  n = n + 1
  b = f + Len(p)
  f = InStr(b, s, p)
Loop
a(n) = Mid(s, b)
ReDim Preserve a(n)
Replacex = Join(a, r)
End Function

2008年5月 2日 (金)

VBScriptのReplace()関数の代替にJScriptを使う。

ScriptControl経由でJScriptのStringオブジェクトのreplace()メソッドを使う。

a=String(1024*1024,"a")
t1=Timer
Set sc=CreateObject("ScriptControl")
sc.Language="JScript"
sc.AddCode "function replace(s,p,r){return s.replace(p,r);}"
b=sc.CodeObject.replace(a,"a","b")
MsgBox Timer-t1
' 0.625

わざわざ使うほどのことはないですが、HTMLやWSFなどならいいかも。

2008年5月 1日 (木)

VBScriptのReplace()関数は、チョー遅い!

VBScriptのReplace()関数が遅い?と思ったことありませんか?
そこで試してみました。
置換回数(n)が多いと、o(n**2)で?、遅くなるようです。

a=String(1024*1024,"a")
t1=Timer
b=Replace(a,"a","b")
MsgBox Timer-t1
' 33.125

一方、RegExpのReplace()メソッドは、チョー速い!

a=String(1024*1024,"a")
t1=Timer
Set re=New RegExp
re.Global=True
re.Pattern="a"
b=re.Replace(a,"b")
MsgBox Timer-t1
' 0.1875

RegExpのReplace()メソッドを使いましょう。

« 2008年4月 | トップページ | 2008年6月 »