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

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の戻り値 | 補正後 |
|---|---|---|
| あ | 12354 | 12354(補正不要) |
| 語 | -30050 | 35486 |
| 髙 | -25895 | 39641 |
| /(全角) | -241 | 65295 |
If code < 0 Then code = code + 65536という1行で、この負の値を正しいコード番号に補正できます。この補正がないと、対象の文字だけ変な記号列に化けてしまいます。

補正後のコード番号をもとに、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への移行時に同じエラーに当たった方は、試してみてください。


コメント