愚者の経験

「また今度」はほとんどこない

月別アーカイブ: 4月 2012

メッセージボックスのボタンが違う

大したことじゃありませんが…Windows7のAccess2010にて。
同じMsgBox関数でも呼び出し元のテーマ?の影響を受けるようです。

 

 

 

 

見ての通りボタンが違います!フォームから呼ぶとボタンに丸みがありますが、
イミディエイトウィンドウから呼ぶとボタンだけクラシックスタイルのような外見になり、
マクロの「メッセージボックス」にいたってはXPのMsgBox関数と同じような外見になります。
全然違います。(ところでマクロで「はい」「いいえ」ボタンってどうやって出すんですか?)

さらに厄介なことに、マクロの「プロシージャの実行」でPublic Functionを呼んで
その中でMsgBox関数を呼んだ時はイミディエイトウィンドウのメッセージボックスになります。orz

同じメッセージボックスを表示したいときは…無理っぽいです。
というかそもそも3種類ある意味ないような(笑)

広告

accdeをランタイムで起動するとエラー

accdeをランタイムで起動するとVBAが動かないです。配布どうしろと…以下再現手順
1.Access2010でaccdeを作成する
2.Access2010のランタイム(SPパッチなし)をインストールした環境で実行する
3.「データベースに含まれているvbaプロジェクトを読み取れないため、データベースを開くことができません」のエラーが発生
おまけ.マクロからPublic Fanctionを呼べない。「指定した式に<>が見つけることができない関数名が含まれています。」が発生

解決策(というか動いた状況)
・accdeにしない。accdbのまま使う(ソースを隠せないため配布には向かない…)
・アップデートしてないAccess2010(Offiece)を使ってaccdeを作成する。
・Access2010ランタイムにSP1パッチを当てて起動する。(ユーザー側で一手間必要)

Access2010RuntimeSP1パッチ(32bit)http://www.microsoft.com/downloads/ja-jp/details.aspx?FamilyID=1c8a450f-0083-4598-94bd-87be8dab716f

せめてパッチ適応済みのランタイム出して欲しいです。

ランタイムでaccdeが動作しない
Accessバージョン

ランタイムでaccdeが正常動作する
Accessバージョン
(CDのインストール直後のAccess)

ほか試したこと
・Access製品版をSP1にアップデートしてaccdeを作成する→×
・XP、Window7それぞれで作成(Access2010のバージョンは上の画像の方)→×

少なくとも去年にデモソフトを作った時には動いていたのに…とっても厳しい状況ですね。

追記:
上記の検証ではAccess2010のランタイムを「XP Mode」上にインストールして発生したものです。
もしかするとWindows7にAccess2010のランタイムを入れた場合動くかもしれません。

Access 2007 「引数が無効です。」

参考URL1:http://answers.microsoft.com/ja-jp/office/forum/office_2007-access/access2007office2007/33f90e65-6d96-4740-b609-de3cac09e229

参考URL2:http://support.microsoft.com/kb/2480088/ja

今まで正常に動いていたのに突然これが出始めたら疑ってみましょう。
Access2007以前のプログラムをAccess2007のSP3で動かすと起きるそうです。
(もともとAccess2010で出ていた模様)

厄介なことに「最適化」で直りません。更にエクスポート&インポートでもウチでは無理でした…
テーブル定義からの作り直しになります。参考URL2では一度XMLにエクスポートしてインポートしています。

直接的な原因としてはAccess2007SP3以前でテーブルのフィールドの削除と追加を繰り返していると起こるそうです。

ウチでは追加クエリの実行時に出現しました。他にもテーブルの「デザインビュー」で開くと起きたり、
レコードセットを最初に開く時に起こったり様々です。

正規表現を使ってHTML整形

正規表現とは「複数の文字列を一つの文字列形式で表現する」ことです。
形式に合致しているかどうか調べることを「パターンマッチ」と言ったりしています。

VBAから正規表現を利用するには
「Microsoft VBScript Regular Expressions 5.5」を「参照設定」してRegExpオブジェクトを使うか
実行時バインディングで以下のように記述します。

    Dim Regex As Object
 
    Set Regex = CreateObject(“VBScript.Regexp”)

正規表現の使い方は他のサイトの方に任せるとして、
これを使ってHTMLの整形にチャレンジしてみます。

正規表現で開始タグから終了タグを検索します。

    Regex.Pattern = “(.|\n)*?()”

上記の正規表現では入れ子の場合に正確な開始タグと終了タグを取得できません。

(例)文字列『

あいえお

』の場合

ヒットする結果は『

あい』です。

当然欲しい結果は『』です(内側から処理していかないと正確にインデントできない)。
なので終了タグから開始タグを類推してもう一度正規表現を利用します。

    Temp = HTML
    With Regex
        ‘閉じがあるタグを変換
        .Pattern = “(.|\n)*?()”
        Do
            ‘パターンマッチ文字列の結果1件取得(取得できない場合はタグをすべて変換し終わった)
            Set Match = .Execute(Temp)(0)
         
            ‘終了タグから開始タグの正規表現作成
            nRegex.Pattern = “()”
         
            ‘パターンマッチ文字列の結果取得
            Set nMatches = nRegex.Execute(Match.Value)
         
            ‘最後の結果を取得
            Set nMatch = nMatches(nMatches.Count – 1)
         
            ‘開始タグから終了タグの文字列取得
            Contents = Mid$(Match.Value, nMatch.FirstIndex + 1)
         
            ‘文字列を変換
            Temp = Replace(Temp, Contents, “{” & CStr(idx) & “}”, , 1)
         
            ‘タグ削除
            Contents = Replace(Mid$(Contents, Len(nMatch.SubMatches(0)) + 1), Match.SubMatches(2), “”, , 1)
                     
            replacelist.Add vbCrLf & nMatch.SubMatches(0) & vbCrLf & _
                                vbTab & Contents & vbCrLf & _
                            Match.SubMatches(2) & vbCrLf, CStr(idx)
            idx = idx + 1
        Loop
Complete:
    End With

一気にコードを載せましたが、Match.SubMatches(3)で終了タグが取得できます。
間に同じタグが複数ある場合もあるので最後の結果を取得するために

    Set nMatch = nMatches(nMatches.Count – 1)

このようにしています。

その後完全な開始タグから終了タグまでの文字列を取得し
開始タグ、中の文字列、終了タグに分割します。

    Contents = Mid$(Match.Value, nMatch.FirstIndex + 1)

    Contents = Replace(Mid$(Contents, Len(nMatch.SubMatches(0)) + 1), Match.SubMatches(2), “”, , 1)

開始タグと終了タグを削除するには「PlainText」メソッドが適任かと思ったのですが、
「PlainText」メソッドはScriptタグとStyleタグに対して使うと中の文字列も消して返してきます。
他にもあるかもしれません。

PlainText(“aaa“)→aaa○
PlainText(“aaaaa”)→””×

なのでMid関数とReplace関数を併用して中の文字列を取得しています。
開始タグは

    nMatch.SubMatches(0)

これで取得できます。

このようにして内側からタグを順番に発見し、”{” & 連番 & “}”という形で変換していき、
元の文字列とセットで連番をKeyにしてCollectionに追加していきます。
すべてのタグが変換し終わったら、次のステップに進みます。

次は”{” & 連番 & “}”でCollectionにアクセスし元の文字列を取得して復元していきますが
もちろんただ戻すだけではありません。ここでインデントしながら元に戻していきます。

On Error Resume Next
    ‘文字列を復元しインデントする
    With Regex
        .Global = True
        .IgnoreCase = True
        .MultiLine = True
        .Pattern = “\{(\d*?)\}”
     
        Dim cnt As Long
        Dim idtcnt As Collection
        Set idtcnt = New Collection
     
        For idx = idx – 1 To 0 Step -1
            ‘変換リストから文字列取得
            Contents = replacelist(CStr(idx))
         
            ‘インデント対象の検索
            Set Matches = Regex.Execute(Contents)
         
            ‘インデント数の取得
            cnt = 0
            cnt = idtcnt(CStr(idx))
            For Each Match In Matches
                idtcnt.Add cnt + 1, CStr(Match.SubMatches(0))
            Next
            Temp = Replace(Temp, “{” & CStr(idx) & “}”, Replace(Contents, vbCrLf, vbCrLf & String$(cnt, 9)))
         
            If (cnt > 0) Then
                Temp = Replace(Temp, vbCrLf & String$(cnt, 9) & vbCrLf, vbCrLf)
            End If
        Next
    End With

例えば以下のようなHTML文字列があるとします。

テスト

これはテストです。

以下の順番で”{” & 連番 & “}”に変換されます。
0 テスト

1 {0}
2 これはテストです。
3 {1}{2}

これを逆順で文字列を復元するまえに文字列内にある”{” & 連番 & “}”に対してインデント数を
計算していきます。最初のインデント数は0です。

{3}インデント数=0
{1}{2}→{1}{2}のインデント数=0+1

{2}インデント数=1
これはテストです。

{1}インデント数=1
{0}→{0}のインデント数=1+1

{0}インデント数=2
テスト

復元の際に「取得したインデント数×vbTab」を改行に追加します。

    Temp = Replace(Temp, “{” & CStr(idx) & “}”, Replace(Contents, vbCrLf, vbCrLf & String$(cnt, 9)))

全コードを載せます。上記だけでは閉じがないタグ(imgタグ等)があるといびつになります。
またコメントやaspのスクリプトも有りますのでそれを先に変換する処理を加えています。

Private Function Surgery(HTML As String) As String
    Dim p As Variant
    Dim Temp As String
    Dim idx As Long
 
    Dim Regex As Object
    Dim Matches As Object
    Dim Match As Object
     
    Dim Contents As String
    Dim Letter As String
    Dim nMatches As Object
    Dim nMatch As Object
    Dim LastIndex As Long
    Dim NestContents As Long
 
    Set Regex = CreateObject(“VBScript.Regexp”)
 
    Dim tag0 As Collection
    Set tag0 = New Collection
 
    ‘閉じがないタグ
    With tag0
        .Add “



        .Add “

        .Add “
        .Add “


        .Add “


        .Add “”
        .Add “”
        .Add “”
        .Add “”
        .Add “”
        .Add “”
        .Add “”
        .Add “”
        ‘xml用
        .Add “”
    End With
         
    Dim nRegex As Object
    Set nRegex = CreateObject(“VBScript.Regexp”)
    With nRegex
        .Global = True
        .IgnoreCase = True
        .MultiLine = True
    End With
 
    ‘変換リスト
    Dim replacelist As Collection
    Set replacelist = New Collection
 
    With Regex
        .Global = True
        .IgnoreCase = True
        .MultiLine = True
     
        Temp = HTML
     
        ‘aspのスクリプトとコメントを変換(ユーザーによる改行があるため)
        .Pattern = “”
        Set Matches = .Execute(Temp)
        For Each Match In Matches
            Temp = Replace(Temp, Match.Value, “{” & CStr(idx) & “}”, , 1)
            replacelist.Add Match.Value & vbCrLf, CStr(idx)
            idx = idx + 1
        Next
     
        .Pattern = “”
        Set Matches = .Execute(Temp)
        For Each Match In Matches
            Temp = Replace(Temp, Match.Value, “{” & CStr(idx) & “}”, , 1)
            replacelist.Add Match.Value & vbCrLf, CStr(idx)
            idx = idx + 1
        Next
     
        ‘タグ内の改行を変換
        .Pattern = “”
        nRegex.Pattern = “\s\s+”
        Set Matches = .Execute(Temp)
        For Each Match In Matches
            Contents = nRegex.Replace(Match.Value, ” “)
            Temp = Replace(Temp, Match.Value, Contents, , 1)
        Next
     
        ‘連続した改行、スペースを削除
        .Pattern = “\s\s+”
        Temp = .Replace(Temp, “”)
     
        ‘閉じがないタグを変換
        For Each p In tag0
            .Pattern = p
            nRegex.Pattern = “<"
            Set Matches = .Execute(Temp)
            For Each Match In Matches
             
                ‘パターンマッチ文字列の結果取得
                Set nMatches = nRegex.Execute(Match.Value)
             
                ‘最後の結果を取得
                Set nMatch = nMatches(nMatches.Count – 1)
             
                ‘開始タグから終了タグの文字列取得
                Contents = Mid$(Match.Value, nMatch.FirstIndex + 1)
             
                ‘タグが全く同一の場合一気に変換するとインデントできなくなるので(特にbrタグ)変換は一つのみ
                Temp = Replace(Temp, Contents, “{” & CStr(idx) & “}”, , 1)

                replacelist.Add Contents & vbCrLf, CStr(idx)
                idx = idx + 1
            Next
        Next
     
On Error GoTo Complete
        ‘閉じがあるタグを変換
        .Pattern = “(.|\n)*?()”
        Do
            ‘パターンマッチ文字列の結果1件取得(取得できない場合はタグをすべて変換し終わった)
            Set Match = .Execute(Temp)(0)
         
            ‘終了タグから開始タグの正規表現作成
            nRegex.Pattern = “()”
         
            ‘パターンマッチ文字列の結果取得
            Set nMatches = nRegex.Execute(Match.Value)
         
            ‘最後の結果を取得
            Set nMatch = nMatches(nMatches.Count – 1)
         
            ‘開始タグから終了タグの文字列取得
            Contents = Mid$(Match.Value, nMatch.FirstIndex + 1)
         
            ‘文字列を変換
            Temp = Replace(Temp, Contents, “{” & CStr(idx) & “}”, , 1)
         
            ‘タグ削除
            Contents = Replace(Mid$(Contents, Len(nMatch.SubMatches(0)) + 1), Match.SubMatches(2), “”)
                     
            replacelist.Add vbCrLf & nMatch.SubMatches(0) & vbCrLf & _
                                vbTab & Contents & vbCrLf & _
                            Match.SubMatches(2) & vbCrLf, CStr(idx)
            idx = idx + 1
        Loop
Complete:
    End With
    Resume Restore
Restore:
On Error Resume Next
    ‘文字列を復元しインデントする
    With Regex
        .Global = True
        .IgnoreCase = True
        .MultiLine = True
        .Pattern = “\{(\d*?)\}”
     
        Dim cnt As Long
        Dim idtcnt As Collection
        Set idtcnt = New Collection
     
        For idx = idx – 1 To 0 Step -1
            ‘変換リストから文字列取得
            Contents = replacelist(CStr(idx))
         
            ‘インデント対象の検索
            Set Matches = Regex.Execute(Contents)
         
            ‘インデント数の取得
            cnt = 0
            cnt = idtcnt(CStr(idx))
            For Each Match In Matches
                idtcnt.Add cnt + 1, CStr(Match.SubMatches(0))
            Next

            ‘インデントを追加して文字列復元
            Temp = Replace(Temp, “{” & CStr(idx) & “}”, Replace(Contents, vbCrLf, vbCrLf & String$(cnt, 9)))
         
            If (cnt > 0) Then
                ‘余計な改行削除
                Temp = Replace(Temp, vbCrLf & String$(cnt, 9) & vbCrLf, vbCrLf)
            End If
        Next
    End With
 
     Surgery = Temp
End Function

たまに改行が残ったりしているのはご愛嬌です。またxmlにも一応対応しているつもりです。
閉じがないタグが他にもある場合は手動で追加してください。

またタグ内での改行は消しています。