【VBA】ScriptControlが64bit Officeで使えない時の代替コード

【VBA】ScriptControlが64bit Officeで使えない時の代替コード のアイキャッチ画像 VBA

VBAでURLエンコードをする際、CreateObject("ScriptControl")(JScriptのencodeURIComponentを呼び出す方法)を使っているコードをよく見かけます。32bit版のOfficeではこれで動きますが、64bit版のOfficeではCreateObjectの時点でエラーになり、そもそもオブジェクトが生成できません。

実際に64bit Officeで実行すると、次のエラーになります。

実行時エラー ‘-2147221164 (80040154)’:
クラスが登録されていません。

64bit OfficeでCreateObject("ScriptControl")を実行した際の実行時エラー画面

ScriptControlは32bit専用のActiveXコンポーネントで、64bit版には対応するモジュールが登録されていないためです。バージョンアップなどで32bit Officeから64bit Officeに切り替わったタイミングで、このエラーに突き当たった方も多いのではないでしょうか。

対処法は、ScriptControlのような外部コンポーネントに頼らず、VBAの標準関数だけでURLエンコードを実装することです。

解決コード:AscWベースの純VBA URLエンコーダー

' AscWベースの純VBA UTF-8 URLエンコーダー
Function EncodeURIComponentVBA(ByVal s As String) As String
    Dim i As Long
    Dim code As Long
    Dim ch As String
    Dim result As String
 
    result = ""
    For i = 1 To Len(s)
        ch = Mid(s, i, 1)
        code = AscW(ch)
        If code < 0 Then code = code + 65536
 
        If (code >= 48 And code <= 57) Or _
           (code >= 65 And code <= 90) Or _
           (code >= 97 And code <= 122) Or _
           code = 45 Or code = 46 Or code = 95 Or code = 126 Then
            ' 英数字・ハイフン・ピリオド・アンダースコア・チルダはそのまま
            result = result & ch
        Else
            result = result & EncodeCodePointToUTF8(code)
        End If
    Next i
 
    EncodeURIComponentVBA = result
End Function
 
' 1文字分のUnicodeコードポイントをUTF-8バイト列に変換し、%XX形式にする
Function EncodeCodePointToUTF8(ByVal code As Long) As String
    Dim b1 As Long, b2 As Long, b3 As Long
 
    If code <= &H7F Then
        EncodeCodePointToUTF8 = "%" & PadHex(code)
    ElseIf code <= &H7FF Then
        b1 = &HC0 Or (code \ &H40)
        b2 = &H80 Or (code And &H3F)
        EncodeCodePointToUTF8 = "%" & PadHex(b1) & "%" & PadHex(b2)
    Else
        b1 = &HE0 Or (code \ &H1000)
        b2 = &H80 Or ((code \ &H40) And &H3F)
        b3 = &H80 Or (code And &H3F)
        EncodeCodePointToUTF8 = "%" & PadHex(b1) & "%" & PadHex(b2) & "%" & PadHex(b3)
    End If
End Function
 
Function PadHex(ByVal n As Long) As String
    PadHex = Right("0" & Hex(n), 2)
End Function

解説

AscWは文字1つをUnicodeのコード番号(コードポイント)に変換する関数です。ScriptControlを使わずにUTF-8を組み立てる場合、この関数が起点になります。

注意点は、コード番号が32768(16進で&H8000)以上の文字は、AscWが負の数を返すことです。VBAの内部でこの値を符号付き16bit整数として扱っているためで、絵文字などのサロゲートペアだけでなく、「語」「髙」のような普通の漢字や、「/」のような全角記号も対象になります。実際に試したところ、次のような結果になりました。

文字AscWの戻り値補正後
あ1235412354(補正不要)
語-3005035486
髙-2589539641
/(全角)-24165295

If code < 0 Then code = code + 65536という1行で、この負の値を正しいコード番号に補正できます。この補正がないと、対象の文字だけ変な記号列に化けてしまいます。

AscWベースの自作関数で「語」をURLエンコードした結果を表示するMsgBox

補正後のコード番号をもとに、UTF-8のバイト列を自前で組み立て、%E8%AA%9Eのような%XX形式の文字列にしています。ASCII文字(英数字や一部記号)はエンコードせずそのまま使うのがURLエンコードのルールです。

なお、このコードが対応しているのはコードポイントが0xFFFF以下の文字(日本語の氏名・住所など、通常の業務データで使う範囲)までです。絵文字のようにコードポイントが0x10000以上でサロゲートペアになる文字は、上位・下位2つのコード単位をそれぞれ変換してしまうため、正しいUTF-8にはなりません。業務データのURLエンコードであれば通常は問題になりませんが、念のため制約として書いておきます。

64bit版Officeでは、Declare文にPtrSafeが必要になるなど、古いコードがそのままでは動かない場面があります。Sleep APIの宣言を32bit・64bit両対応にする書き方は、【VBA】ZIP解凍の完了確認|CopyHereの戻りを実測で紹介しています。

URLエンコードではUTF-8のバイト列を扱いますが、Shift_JISでのバイト数を数えたい場面もあります。氏名欄のバイト数チェックは、全角氏名のバイト数チェックはLenBとStrConvで行うで扱っています。

エンコードしたURLをGASなどに送るときの通信オブジェクトの違いは、【VBA】GASへのPOSTが405になる原因と通信オブジェクト比較で実測しています。

まとめ

ScriptControlを使ったURLエンコードは32bit専用で、64bit Officeでは「クラスが登録されていません」というエラーで動きません。AscWと+65536の補正、そして自前のUTF-8バイト組み立てに置き換えれば、外部コンポーネントなしで同じ処理を実現できます。補正が必要なのはサロゲートペアに限らず、コード番号が32768以上の文字全般だという点も覚えておくと役立ちます。64bit Officeへの移行時に同じエラーに当たった方は、試してみてください。

参考:Asc, AscW 関数 | Microsoft Learn

コメント

タイトルとURLをコピーしました