【问题标题】:Convert a string into a stream correctly将字符串正确转换为流
【发布时间】:2019-01-30 09:13:11
【问题描述】:
Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Imports System.IO
Imports System.IO.Compression
Imports System.Text

Namespace WindowScriptingObject
    <Guid("7448E08D-ED0F-4E23-B528-91937BB41756"), _
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
        Public Interface _WindowScriptingObject
        <DispId(1)> Function Decompress(ByVal value as String) As String
    End Interface

    <Guid("B146BF9E-78FC-4DB0-ABFE-9FF026B43E4D"), _
        ClassInterface(ClassInterfaceType.None), _
        ProgId("WindowScriptingObject")> Public Class WindowScriptingObject
    Implements _WindowScriptingObject

    Public WindowScriptingObject()
        Public Function Decompress(ByVal value as string) As String Implements _WindowScriptingObject.Decompress
            Dim x As String
            '     on error resume next
            Dim xstream As New MemoryStream(Encoding.Unicode.GetBytes(value))
            Dim mem2 As New IO.MemoryStream()
            'Dim streamMe As New StreamWriter(mem2,Encoding.UTF8)
            'streamMe.Write(value)
            'StreamMe.Close()
            'mem2.Position=0

            Dim gz As New System.IO.Compression.GZipStream(xstream, IO.Compression.CompressionMode.Decompress)

            Dim sr As New IO.StreamReader(gz)
            x = sr.ReadLine

            sr.Close()
            'End Using

            Decompress = x
        End Function
    End Class
End Namespace

我验证了我发送过来的字符串包含来自我的 VBScript 的正确值。但是,它说标题是坏的。

以上代码需要编译测试

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc.exe" /target:library /out:"%userprofile%\desktop\t.dll" "%userprofile%\desktop\t.txt" /verbose

然后注册

"C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm" /codebase "%userprofile%\desktop\t.dll" /tlb:"%userprofile%\desktop\t.tlb" /v

然后调用

c:\windows\SysWOW64\cscript.exe old.vbs

我放入代码以从文件中读取内容,即使这不是最终目标。当我这样做时,文件解压缩正确。

Dim xstream As New MemoryStream(Encoding.Unicode.GetBytes(value))

这行听到似乎错误地将我的字符串转换为流。

目标是发送压缩字符串并返回未压缩字符串。

上面的代码是用这个代码调用的

Const adTypeBinary = 1
Set wso = CreateObject("WindowScriptingObject")
Dim objStream
Set objStream = CreateObject("ADODB.Stream")
objStream.Type = adTypeBinary
objStream.Open
objStream.LoadFromFile "e:\download\result.gz"
'objStream.Charset = "Windows-1252" 
x = objStream.Read(900)
objStream.Close

For i=1 To Len(x) 
    t = t & Chr(AscW(Mid(x, i, 1)) And 255)
    t = t & Chr((AscW(Mid(x, i, 1)) And 65280)/256)
Next
MsgBox wso.Decompress(t), , "vbs"

我试过了,甚至将字符串转换为 base64 以使其工作。

Dim gzBuffer As Byte() = Convert.FromBase64String(value)
    Using ms As New MemoryStream()
        Dim msgLength As Integer = BitConverter.ToInt32(gzBuffer, 0)
        ms.Write(gzBuffer, 4, gzBuffer.Length - 4)

        Dim buffer As Byte() = New Byte(msgLength - 1) {}
        ms.Position = 0
        Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress)
              zipStream.Read(buffer, 0, buffer.Length)
        End Using
    Decompress=System.Text.Encoding.Unicode.GetString(buffer, 0, buffer.Length)
    End Using

数据未正确转换,因为我在 GZip 标头中仍有幻数不正确。

将base64编码值转储到在线解码器中,我传入的字符串与解码值匹配。

第 2 版 强迫我对它进行base64编码,但它可以工作。 如何消除这种烦恼。

Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Imports System.IO
Imports System.IO.Compression
Imports System.Text

Namespace WindowScriptingObject
    <Guid("7448E08D-ED0F-4E23-B528-91937BB41756"), _
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
        Public Interface _WindowScriptingObject
        <DispId(1)> Function Decompress(ByVal value as String) As String
    End Interface

    <Guid("B146BF9E-78FC-4DB0-ABFE-9FF026B43E4D"), _
        ClassInterface(ClassInterfaceType.None), _
        ProgId("WindowScriptingObject")> Public Class WindowScriptingObject
    Implements _WindowScriptingObject

    Public WindowScriptingObject()
        Public Function Decompress(ByVal value as string) As String Implements _WindowScriptingObject.Decompress
            Dim x As String
            '     on error resume next
    Dim gzBuffer As Byte() = Convert.FromBase64String(value)
    Using ms As New MemoryStream()
        Dim msgLength As Integer = BitConverter.ToInt32(gzBuffer, 0)
        ms.Write(gzBuffer, 0, gzBuffer.Length)

        Dim buffer As Byte() = New Byte(msgLength - 1) {}
        ms.Position = 0
        Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress)
              zipStream.Read(buffer, 0, buffer.Length)
        End Using
    Decompress=System.Text.Encoding.ASCII.GetString(buffer, 0, buffer.Length)
    End Using
'            Dim xstream As New MemoryStream(value.ToArray())
            Dim mem2 As New IO.MemoryStream()
            'Dim streamMe As New StreamWriter(mem2,Encoding.UTF8)
            'streamMe.Write(value)
            'StreamMe.Close()
            'mem2.Position=0

            'Dim gz As New System.IO.Compression.GZipStream(xstream, IO.Compression.CompressionMode.Decompress)

            'Dim sr As New IO.StreamReader(gz)
           ' x = sr.ReadLine

            'sr.Close()
            'End Using

            'Decompress = x
        End Function
    End Class
End Namespace

更新此代码有效,但输出大小为 500K,文本只有 3100 字节。

Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Imports System.IO
Imports System.IO.Compression
Imports System.Text

Namespace WindowScriptingObject
    <Guid("7448E08D-ED0F-4E23-B528-91937BB41756"), _
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
        Public Interface _WindowScriptingObject
        <DispId(1)> Function Decompress(ByVal value as string) As String
    End Interface

    <Guid("B146BF9E-78FC-4DB0-ABFE-9FF026B43E4D"), _
        ClassInterface(ClassInterfaceType.None), _
        ProgId("WindowScriptingObject")> Public Class WindowScriptingObject
    Implements _WindowScriptingObject

    Public WindowScriptingObject()
        Public Function Decompress(ByVal value as string) As String Implements _WindowScriptingObject.Decompress
            '     on error resume next
    Dim gzBuffer() As Byte = System.Text.Encoding.Default.Getbytes(value)

    Using ms As New MemoryStream()
        Dim msgLength As Integer = BitConverter.ToInt32(gzBuffer, 0)
        ms.Write(gzBuffer, 0, gzBuffer.Length)
 msgbox(msgLength)
        Dim buffer As Byte() = New Byte(msgLength - 1) {}
        ms.Position = 0

        Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress)
              zipStream.Read(buffer, 0, buffer.Length)
        End Using
    Decompress=System.Text.Encoding.Default.GetString(buffer, 0, buffer.Length)
    End Using

        End Function
    End Class
End Namespace

由于某种原因,msgLength 的大小为 559,903,解压后的文本大约为 3100 字节。这意味着 BitConverter.toint32 出现故障,因为 gzBuffer 是 865 字节。最终输出大小只有 GZIPStream 函数知道,因为文本被压缩,输入大小与输出大小无关。

其他问题

  1. 可以更有效地编码吗?
  2. 如何防止恶意代码注入?
  3. 将输出限制为正确的大小?
  4. 如果我添加新功能,是否需要更多 Guid?
  5. 如何生成新的 Guid?
  6. 在代码块 #3 中,我将 X 转换为字符串 t 并在不进行转换的情况下传输值。

输出大小似乎基于错误信息。

intOutputLength=zipStream.Read(buffer, 0, buffer.Length)
End Using
Decompress=System.Text.Encoding.Default.GetString(buffer, 0, intOutputLength)

至少这减少了返回主程序的数据量。

Dim msgLength As Integer = BitConverter.ToInt32(gzBuffer, 0)

如果我没看错,msgLength 是由输入流的前 4 个字符决定的吗?由于 GZip 标头始终是 1f 8b 08 00 这似乎是一个可怕的想法。如果输出大于 559k,则似乎等待发生的缓冲区溢出。

我认为这解决了可怕的缓冲区大小问题。

Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Imports System.IO
Imports System.IO.Compression
Imports System.Text


Namespace WindowScriptingObject
    <Guid("7448E08D-ED0F-4E23-B528-91937BB41756"), _
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
        Public Interface _WindowScriptingObject
        <DispId(1)> Function Decompress(ByVal value as string) As String
    End Interface


    <Guid("B146BF9E-78FC-4DB0-ABFE-9FF026B43E4D"), _
        ClassInterface(ClassInterfaceType.None), _
        ProgId("WindowScriptingObject")> Public Class WindowScriptingObject
    Implements _WindowScriptingObject


    Public WindowScriptingObject()
        Public Function Decompress(ByVal value as string) As String Implements _WindowScriptingObject.Decompress
            '     on error resume next
    Dim gzBuffer() As Byte = System.Text.Encoding.Default.Getbytes(value)
    dim intOutputLength as integer
    Dim intBlock as integer
    Decompress=""
    Using ms As New MemoryStream()
        Dim msgLength As Integer = 4096
        ms.Write(gzBuffer, 0, gzBuffer.Length)

        Dim buffer As Byte() = New Byte(4096) {}
        ms.Position = 0

        Using zipStream As New System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress)
        intOutputLength=0
        intBlock=4096
        while intBlock=4096
              intBlock=zipStream.Read(buffer, 0, buffer.Length)
            Decompress+=System.Text.Encoding.Default.GetString(buffer, 0, intBlock)
            intOutputLength+=intBlock
        end while 
        End Using

    End Using

        End Function
    End Class
End Namespace

【问题讨论】:

  • 没有将String 转换为Stream 这样的事情。您创建一个Stream,将String 转换为Byte 数组,然后将Byte 数组写入Stream。从StringByte 数组时,必须指定编码。如果您传输生成的二进制数据并希望在之后转换回文本,则需要在两个方向上使用相同的编码。最可能的问题是您不是。
  • @jmcilhinney 您可能是对的,但是如何更改我的代码以获得有效的解决方案?源是 gzip 压缩(二进制),输出是文本。
  • 您对文本编码进行了一些研究,并确保您在每一端都使用了兼容的编码。 .NET 通过System.Text.Encoding 类提供多种编码作为标准。你知道它们的真正含义吗?如果您遇到编码问题,您应该找出答案。您确切知道您发布的 VBS 代码对应的编码是什么吗?再次,您应该找出答案。
  • @jmcilhinney 源代码是直接二进制,而不是 UTF 任何东西。因此,Encoding.Unicode.GetBytes(value) 似乎不适合这项工作,但我用什么来代替它。我知道 UTF 是什么(这很痛苦),它需要 2 个或更多字节来表示一个字符,这样你就可以拥有每种语言的每个字符。我花了至少 2 天时间才走到这一步。我已经尝试(谷歌搜索)几十种方法将我的二进制字符串放入流中,但都失败了。
  • @cybernard 究竟什么是“直接二进制”,因为 jmcilhinney 已经说过编码必须匹配。

标签: vb.net vbscript


【解决方案1】:

通过将 VB.NET 函数和接口更改为如下所示(主要是更改参数类型),我能够让您的代码正常工作:

<Guid("7448E08E-ED0F-4E23-B528-91937BB41756"),
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface _WindowScriptingObject
   <DispId(1)> Function Decompress(ByVal value As Byte()) As String
End Interface

Public Function Decompress(ByVal value As Byte()) As String Implements _WindowScriptingObject.Decompress
   Using xstream As New MemoryStream(value)
      Using gz As New System.IO.Compression.GZipStream(xstream, IO.Compression.CompressionMode.Decompress)
         Using sr As New IO.StreamReader(gz)
            Return sr.ReadLine()
         End Using
      End Using
   End Using
End Function

我的测试 VBS 是这样的

Const adTypeBinary = 1
Dim wso
Set wso = CreateObject("WindowScriptingObject")
Dim objStream, x
Set objStream = CreateObject("ADODB.Stream")
objStream.Type = adTypeBinary
objStream.Open
objStream.LoadFromFile "c:\users\bluem\desktop\Notes.txt.gz"
x = objStream.Read(342737)
objStream.Close
WScript.StdOut.WriteLine wso.Decompress((x))

我不完全确定为什么需要将 x 参数括在两组括号中,但我认为这与强制参数通过值而不是通过引用传递并帮助它转换为字节数组。在添加额外的一对括号之前,我遇到了一个错误。

编辑: 要回答您的其他一些问题:

  • 我认为您不需要为新功能创建新 GUID,只需为新接口或类创建新 GUID。
  • 要创建一个新的 GUID,您只需复制现有的 GUID 并将其中的一部分(0 到 F 之间的数字)更改为唯一的,或者您可以转到 https://www.guidgenerator.com/ 或者您可以选择“创建 GUID” Visual Studio 的工具菜单。
  • 如果您可以根据新代码澄清您的数据长度问题(如果问题仍然存在),我也许可以回答。

【讨论】:

  • 好的,这看起来不错。我明天做测试。但是,如何添加错误处理?假设有人传入了一个 00 的字符串,现在 gzip 会发出一个需要处理并传回的错误。此外,如果 x = objStream.Read(342737) 减少为 Read(1000) 会发生什么,因为文件较大,它会产生一个我需要处理的错误。
  • 是的,您的 vb.net 代码已经过测试并且很好。我不知道为什么,但没有 ((x)) 我会被拒绝访问。我尝试了 byval、byRef、value、value()、byte 和 byte() 的所有组合,我可能无法使用。但是,它使我免于手动反转缓冲区,因此我保留了它。
  • @cybernard 342737 仅用于测试。您应该使用 adReadAll 来读取流中的任何内容,而不是固定长度。您可以使用 On Error Resume Next 处理 VBScript 中的错误,并使用 trycatch 在 VB.NET 中处理错误。
【解决方案2】:

我写vbscript已经太久了,所以我知道的还不够多,无法提供修复。但是,我可以指出这段代码的 vbscript 部分中的一些严重缺陷

它首先从.gz 文件中读取最多 900 个字节,而不管文件的实际长度如何。任何超过 900 字节的内容都不会被读取。

它以二进制模式执行此读取。二进制模式忽略任何字符集或编码信息,只读取原始字节,这适用于.gz 文件。然而,接下来发生的事情是使用Len() 函数,该函数用于字符串,而不是二进制数据; Len() 不是这里的适当功能。此外,接下来通过Mid() 函数在For 循环中使用数据。 Mid() 同样仅用于字符串x 变体不是字符串。 vbscript 字符串对象不仅仅是原始字符;它们包括编码、长度和字符缓冲区等元数据,而这些字符串函数依赖于使用所有元数据正确构造的对象。

不可能这个 vbscript 产生正确的结果。在解决这个问题之前,即使查看 vb.net 代码也没有任何意义。同样,我建议一个真正的解决方案太过分了,但我建议尝试将未更改的字节数组传递给 .Net 端,而不是字符串。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-13
    • 2018-01-03
    • 2014-06-25
    • 1970-01-01
    • 2015-11-18
    • 1970-01-01
    相关资源
    最近更新 更多