goo blog サービス終了のお知らせ 

半角チルダ

ExcelVBA、その他。
覚え書きや、補足資料などのスクラップブック。
end-u(1037781)

■xl2007:ModifyAppliesToRangeメソッド

2010-05-31 22:00:00 | 雑記
EXCELVBAであるセルに設定されている条件式書式を取得したいと - 教えて!goo

あっさりと一発で解決しましたが、実はこれ手強かったンです。

Option Explicit

Sub Macro1()
  With Sheets.Add
    'A1:C1範囲に条件1を設定する
    With .Range("A1:C1").FormatConditions
      .Delete
      With .Add(Type:=xlCellValue, Operator:=xlEqual, Formula1:="1")
        .Interior.ColorIndex = 35
      End With
    End With
    'B1:C1範囲に条件2を設定する
    With .Range("B1:C1").FormatConditions
      With .Add(Type:=xlCellValue, Operator:=xlEqual, Formula1:="2")
        .Interior.ColorIndex = 36
      End With
    End With
    'C1に条件3を設定する
    With .Range("C1").FormatConditions
      With .Add(Type:=xlCellValue, Operator:=xlEqual, Formula1:="3")
        .Interior.ColorIndex = 37
      End With
    End With
    .Range("A1:C1").Value = [{1,2,3}]
    Call FormatConditionsTest(.Range("C1"))
  End With
End Sub
'---------------------------------------------------------------------
Sub FormatConditionsTest(ByRef r As Range)
  Dim n As Long
  Dim i As Long

  With r.FormatConditions
    n = .Count
    ReDim ret(1 To n) As String
    For i = 1 To n
      ret(i) = .Item(i).Formula1
    Next
  End With
  MsgBox Join(ret, vbLf)
  Erase ret
End Sub

Macro1 実行後。




現象としては以下。

・Excel2007において、上記コードでC1セルに[条件付き書式]を設定する。
・この時、上図のように[適用先]のセル範囲が、条件ごとに違うと、[Formula1 プロパティ]が正しく取得できない。

2003では正しく取得できます。(そもそも2003には[適用先]という概念がない)

解決策としては、単純に[適用先]を揃える事が考えられます。
2007から[ModifyAppliesToRange メソッド]という[適用先]セル範囲を設定するメソッドも追加されています。
リンクQ&Aで
>どうしても、違う範囲を設定する必要がある場合、
>作業用シートや作業用ブックにコピーして
>ModifyAppliesToRangeメソッドで範囲を揃えてあげると取れます。

などと書いてますが、ここに落とし穴がありましたorz

何も考えず
Sub test1()
  Dim fc As FormatCondition

  ActiveSheet.Copy
  With ActiveSheet '作業用Sheet
    With .Range("C1")
      For Each fc In .FormatConditions
        fc.ModifyAppliesToRange .Item(1)
      Next
      Call FormatConditionsTest(.Item(1))
    End With
  End With
End Sub

...ってやると、作業用シートには変化なく、何と元シートの条件付き書式範囲がModifyされてしまうのです。
これは
Sub test2()
  Dim wb1 As Workbook '元のBook
  Dim wb2 As Workbook '作業用Book
  Dim ws1 As Worksheet '元のSheet
  Dim ws2 As Worksheet '作業用Sheet
  Dim fc As FormatCondition

  Set wb1 = Workbooks("test1.xlsx")
  Set wb2 = Workbooks("test2.xlsx")
  Set ws1 = wb1.Sheets(1)
  ws1.Copy wb2.Sheets(1)
  Set ws2 = wb2.Sheets(1)
  With ws2.Range("C1")
    For Each fc In .FormatConditions
      fc.ModifyAppliesToRange .Item(1)
    Next
    Call FormatConditionsTest(.Item(1))
  End With

  Set ws1 = Nothing
  Set ws2 = Nothing
  Set wb1 = Nothing
  Set wb2 = Nothing
End Sub

...って感じで、くど過ぎるくらいObject指定してもダメでした。
元Bookの"test1.xlsx"の元シートの[適用先]が変更されちゃいます。





DoEventsをLoop回してもダメだったので、リンクQ&AではOnTimeメソッドで一拍おいてます。

が、その後検証してみたら、『ActiveSheetをActivateしてあげれば良い』だけなのであった..(ぉぃ
ぃや、SheetでなくてもBookやCellでもいいんですけどね。

Sub test1改()
  Dim fc As FormatCondition

  ActiveSheet.Copy
  With ActiveSheet
    .Activate '■
    With .Range("C1")
      For Each fc In .FormatConditions
        fc.ModifyAppliesToRange .Item(1)
      Next
      Call FormatConditionsTest(.Item(1))
    End With
  End With
End Sub


なんという挙動不審さ。orz
Comments (3)
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

■セルに表示できる最大文字数

2010-04-30 20:00:00 | 雑記
「Excelの限界に挑戦」シリーズ(嘘 :D

2003版『Excel の仕様および制限』
http://office.microsoft.com/ja-jp/excel/HP051992911041.aspx
>セルの内容の長さ (文字列)
>32,767 文字。セルに表示できるのは 1,024 文字まで。数式バーでは 32,767 文字すべての表示が可能。


2007版『Excel の仕様および制限』
http://office.microsoft.com/ja-jp/excel/HP100738491041.aspx
>セルが含むことができる合計文字数
>32,767 文字


..とあります。

『Excel のセルに表示されるのは 1,024 文字のみ』
http://support.microsoft.com/kb/211580/ja

..という記事もあります。

ちょっと整理してみた結論は、
■セル表示形式で[折り返して全体を表示する]にしなければ、シート上に表示されるのは1,024文字まで。
■[折り返して全体を表示する]場合、セル内表示可能文字数は、表示環境と入力内容に依存するので、一概に何文字まで、とは言えない。最大32,767文字の表示が可能。

kb/211580の現象確認手順通りに実行した場合、

Sub test1()
  With Workbooks.Add(xlWBATWorksheet).Sheets(1).Range("A1")
    .Formula = "=REPT(""w"",1024)&""xyz"""
    .WrapText = True
    .Columns.AutoFit
  End With
End Sub

test1実行後。
test1実行後画像

大抵の方の環境では、記事内容どおりではなく、1,025文字以上表示されるのではないでしょうか。
[折り返して全体を表示する]設定では、1,024文字の制限はありません。
この設定を解除すると、シート上には1,024文字までしか表示されません。
右スクロールで確認できます。

Sub test2()
  With Workbooks.Add(xlWBATWorksheet).Sheets(1).Range("A1")
    .Formula = "=REPT(""w"",1023)&""xyz"""
    .Offset(1).Value = .Value
    .Offset(2).Formula = "=RIGHT(A2,3)"
    .Offset(3).Formula = "=LEN(A2)"
  End With
End Sub

test2実行後。
test2実行後画像

セルの列幅行高を拡げて、フォントサイズを小さくし、[折り返して全体を表示する]と、最大32,767文字の表示が可能な事が確認できます。
環境によるかもしれません&全然実用的じゃないですが。

Sub test3()
  With Workbooks.Add(xlWBATWorksheet).Sheets(1).Range("A1")
    '2003は適宜改行が必要(?)
    .Formula = "=" & ThisWorkbook.Name & "!uREPT(""."",32765)&""xyz"""
    '2007はノーマルでOK。
    '.Formula = "=REPT(""."",32765)&""xyz"""
    .Columns.AutoFit
    .WrapText = True
    .RowHeight = 409.5
    .Font.Name = "MS Pゴシック"
    .Font.Size = 7
    .Offset(1).Formula = "=RIGHT(A1,3)"
    .Offset(2).Formula = "=LEN(A1)"
  End With
  ActiveWindow.Zoom = 75
End Sub
'---------------------------------------------------------------------
Public Function uREPT(ByVal arg As String, ByVal cnt As Long)
  Const p As Long = 1148
  Dim ret As String
  Dim i  As Long

  ret = String(cnt, arg)
  For i = p To cnt Step p
    Mid(ret, i) = vbLf
  Next
  uREPT = ret
End Function

test3実行後。
test3実行後画像

ちなみに[縮小して全体を表示する]と...

Sub test4()
  With Workbooks.Add(xlWBATWorksheet).Sheets(1).Range("A1:A2")
    .Item(1).Formula = "=REPT(""."",1024)&""ccc"""
    .Item(2).Formula = "=REPT(""."",1023)&""ccc"""
    .Columns.AutoFit
    .ShrinkToFit = True
  End With
End Sub

test4実行後。
test4実行後画像

1,024文字までですね。

#みぎっ X)
Comment
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

■OFFSETとROW|COLUMN関数を絡めた配列数式

2010-03-06 19:00:00 | 雑記
もう1ヵ月経ったからそろそろ晒していいかな。
これ。

数式中の範囲指定がうまく引き渡させれません - 教えて!goo

1ヵ月以上放置だったけど、今日〆切られたみたい。
多数回答歴ある経験者が必ずしも良質の質問者たり得ないという事ですね。

#...それにしても酷いスレッドだな。
#久し振りに[@呆れ果て]ハンドル使うとこだった。
#まだ書き殴り足りない部分もあるがここは呑みこんでおこう。
#そもそも、私は自分で試そうともしないヒトが大っ嫌いなだけなのである。

中身はちょっとレアっぽいかもしれないのに、スレッドの流れが最低で、紹介するには気がひけてしまいます。
とにかく
・ROW関数やCOLUMN関数の戻り値は、単独の行|列を指定しても配列が返る。
・OFFSET関数の引数にこれらの関数を使って配列数式にすると#VALUE!エラーになるケースがある。
これを勉強できただけでヨシとしておこー。

もっとも、Excel関連掲示板の回答者以外で、複雑な配列数式を駆使して実務に励んでる方が多いとは思えないので、やっぱりマイナーな、あまり為にならないプチ情報である事には変わりないけど。 XD



この記事、最初はこれとセットで『■回答者をオロカモノと呼び質問者をバカと決め付ける』ってお題だったんだけど...まぁ、別にケンカ売る気はないし。あらためて書くまでもなく、割愛しました。それに私グシャでいいですし。:D
Comment
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

■続)ADOのメモリリーク1

2010-02-09 23:00:01 | 雑記
■ADOのメモリリーク の続編。
前回、開いているBookに対してADO、DAO、QueryTable を用いてデータを読み込んだ時、全ての手法でメモリリークが発生する...ような結論になっていました。
今回追加検証をやってみたら、どうも改善されているようです。

結果を先にまとめると。
『Excel2003sp3以降、QueryTableを使った読み込みではメモリリーク問題は解消されているようである』
『OS:WindowsVistaでは、ADO、DAOに関してもメモリリーク問題は解消されているようである』

テストコード内容。

Option Explicit
'Performance monitor functions for Visual Basic from PDH.DLL
Private Declare Function PdhVbOpenQuery _
             Lib "pdh.dll" ( _
               ByRef QueryHandle As Long) As Long
Private Declare Function PdhCloseQuery _
             Lib "pdh.dll" ( _
               ByVal QueryHandle As Long) As Long
Private Declare Function PdhVbAddCounter _
             Lib "pdh.dll" ( _
               ByVal QueryHandle As Long, _
               ByVal CounterPath As String, _
               ByRef CounterHandle As Long) As Long
Private Declare Function PdhRemoveCounter _
             Lib "pdh.dll" ( _
               ByVal CounterHandle As Long) As Long
Private Declare Function PdhCollectQueryData _
             Lib "pdh.dll" ( _
               ByVal QueryHandle As Long) As Long
Private Declare Function PdhVbGetDoubleCounterValue _
             Lib "pdh.dll" ( _
               ByVal CounterHandle As Long, _
               ByRef CounterStatus As Long) As Double
'-------------------------------------------------
Sub test()
  Dim x As String

  x = InputBox("ado or dao or qry")
  If Len(x) = 0 Then Exit Sub
  Call memLeaktest(x)
End Sub
'-------------------------------------------------
Sub memLeaktest(prc As String)
  Const tx As Long = 20  'テスト回数
  Const wkSQL = "SELECT * FROM [data$]"
  Const cPath = "¥Process(Excel)¥Private Bytes"
  Dim ws   As Worksheet
  Dim rng   As Range
  Dim wkBook As String
  Dim hPDHQry As Long  'Handle to performance monitor query
  Dim hPDHCnt As Long  'Handle to performance monitor counter
  Dim cStat  As Long  'Status of counter when checked
  Dim pByts  As Double 'Value of counter when checked
  Dim x    As Long
  Dim i    As Long
  Dim ret(0 To tx + 1)

  With ThisWorkbook
    Set ws = .Sheets("out")
    Set rng = .Sheets("data").Range("A2")
    wkBook = .FullName
  End With
  x = PdhVbOpenQuery(hPDHQry)
  x = PdhVbAddCounter(hPDHQry, cPath, hPDHCnt)
  ret(0) = prc
  x = PdhCollectQueryData(hPDHQry)
  pByts = PdhVbGetDoubleCounterValue(hPDHCnt, cStat)
  If cStat = 0 Then
    ret(1) = CLng(pByts) ¥ 1024
  End If

  For i = 1 To tx
    rng.Value = "'" & i
    '■TESTプロシージャ
    Select Case prc
    Case "ado"
      Call ADOtest(ws, wkBook, wkSQL)
    Case "dao"
      Call DAOtest(ws, wkBook, wkSQL)
    Case "qry"
      Call QRYtest(ws, wkBook, wkSQL)
    End Select
    x = PdhCollectQueryData(hPDHQry)
    pByts = PdhVbGetDoubleCounterValue(hPDHCnt, cStat)
    If cStat = 0 Then
      ret(i + 1) = CLng(pByts) ¥ 1024
    End If
    Debug.Print ws.Cells(1).Value
  Next

  x = PdhRemoveCounter(hPDHCnt)
  x = PdhCloseQuery(hPDHQry)
  ThisWorkbook.Sheets("chk").Range("IV1").End(xlToLeft) _
        .Offset(, 1).Resize(tx + 2).Value _
        = Application.Transpose(ret)

  Set rng = Nothing
  Set ws = Nothing
End Sub

# 2 へ続く
Comment
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

■続)ADOのメモリリーク2

2010-02-09 23:00:00 | 雑記
# 1 の続き...

Sub ADOtest(ByRef ws As Worksheet, _
      ByVal wkBook As String, _
      ByVal wkSQL As String)
  Dim wkCon As Object 'ADODB.Connection
  Dim wkRst As Object 'ADODB.Recordset

  On Error GoTo conErr
  ws.UsedRange.ClearContents
  Set wkCon = CreateObject("ADODB.Connection")
  With wkCon
    .Provider = "Microsoft.Jet.OLEDB.4.0"
    .Properties("Extended Properties") = "Excel 8.0"
    .Properties("Data Source") = wkBook
    .Open
  End With
  On Error GoTo rsErr
  Set wkRst = CreateObject("ADODB.Recordset")
  wkRst.Open wkSQL, wkCon
  ws.Range("A1").CopyFromRecordset wkRst
  wkRst.Close
rsErr:
  wkCon.Close
conErr:
  Set wkRst = Nothing
  Set wkCon = Nothing
  With Err()
    If .Number <> 0 Then
      Debug.Print .Number, .Description
    End If
  End With
End Sub
'-------------------------------------------------
Sub DAOtest(ByRef ws As Worksheet, _
      ByVal wkBook As String, _
      ByVal wkSQL As String)
  Const dbOpenDynaset As Long = 2
  Dim wkDb As Object 'DAO.Database
  Dim wkRs As Object 'DAO.Recordset

  On Error GoTo conErr
  ws.UsedRange.ClearContents
  Set wkDb = CreateObject("DAO.DBEngine.36") _
        .OpenDatabase(wkBook, False, False, "EXCEL 8.0")
  On Error GoTo rsErr
  Set wkRs = wkDb.OpenRecordset(wkSQL, dbOpenDynaset)
  ws.Range("A1").CopyFromRecordset wkRs
  wkRs.Close
rsErr:
  wkDb.Close
conErr:
  Set wkRs = Nothing
  Set wkDb = Nothing
  With Err()
    If .Number <> 0 Then
      Debug.Print .Number, .Description
    End If
  End With
End Sub
'-------------------------------------------------
Sub QRYtest(ByRef ws As Worksheet, _
      ByVal wkBook As String, _
      ByVal wkSQL As String)
  Dim wkQRY As QueryTable

  On Error Resume Next
  ws.UsedRange.ClearContents
  Set wkQRY = ws.QueryTables.Add( _
          Connection:="ODBC;DSN=Excel Files;" & _
                "DBQ=" & wkBook & ";", _
          Destination:=ws.Range("A1"), _
          Sql:=wkSQL)
  With wkQRY
    .FieldNames = False
    .RefreshStyle = xlOverwriteCells
    .AdjustColumnWidth = False
    .Refresh BackgroundQuery:=False
    ws.Names(.Name).Delete
    .Delete
  End With
  Set wkQRY = Nothing
  With Err()
    If .Number <> 0 Then
      Debug.Print .Number, .Description
    End If
  End With
End Sub

メモリ計測は[BUG: Memory leak occurs when you query an open Excel worksheet by using ActiveX Data Objects (ADO)]に載っているPDH.DLLを使用。
新規Book標準モジュールにコードを置いて、名前をつけて保存した後、Private Sub test_PRE()を実行。(テストデータを作成して上書き保存します)

Private Sub test_PRE()
  Const x As Long = 2000 'データ行数
  Const y As Long = 10  'データ列数
  Dim i  As Long
  Dim j  As Long
  Dim v() As String

  ReDim v(1 To x, 1 To y)
  With ThisWorkbook
    With .Sheets.Add
      .Name = "data"
      With .Range("A1").Resize(x, y)
        For i = 1 To x
          For j = 1 To y
            v(i, j) = .Cells(i, j).Address
          Next
        Next
        .Value = v
      End With
    End With
    .Sheets.Add.Name = "out"
    .Sheets.Add.Name = "chk"
    .Save
  End With
End Sub

その後 Sub test() を実行。(InputBoxに文字列を入力する事でテストプロシージャを分岐)

テストは実行するごとにExcelを再起動して実行しました。
以下は結果データ。


【環境1】

[Windows]
XP pro 5.1.2600 SP3

[EXCEL]
2000 9.0.8968 SP3
2003 11.8316.8221 SP3
2007 12.0.6514.5000 SP2





【環境2】

[Windows]
VISTA ultimate 6.0 6022 SP2

[EXCEL]
2000 9.0.8968 SP3
2007 12.0.6514.5000 SP2





Comment
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする