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
Обновление этого кода работает, за исключением того, что размер вывода составляет 500 КБ, а текст - всего 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, поскольку текст сжимается, а размер ввода не имеет отношения к размеру вывода.
Другой вопрос (ы)
Размер вывода, похоже, основан на неверной информации.
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, это кажется ужасной идеей. Если вывод каждый раз превышает 559 КБ, это похоже на переполнение буфера, которое только и ждет, чтобы произойти.
Я думаю, что это решает ужасную проблему размера буфера.
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
@jmcilhinney Возможно, вы правы, но как мне изменить свой код, чтобы получить работающее решение? Источник представляет собой сжатый gzip (двоичный файл), а вывод - текст.
Вы проводите некоторое исследование кодирования текста и убедитесь, что вы используете совместимое кодирование на каждом конце. .NET стандартно предлагает несколько кодировок через класс System.Text.Encoding. Вы знаете, что они все на самом деле означают? Если у вас возникли проблемы с кодировкой, вы должны выяснить это. Вы точно знаете, какой кодировке соответствует опубликованный вами код VBS? Опять же, вы должны узнать.
@jmcilhinney Источник является прямым двоичным кодом, а не UTF. Поэтому Encoding.Unicode.GetBytes(value) кажется неподходящим для работы, но что мне использовать вместо него. Я знаю, что такое UTF (боль в...), который занимает 2 или более байтов для представления одного символа, поэтому вы можете иметь каждый символ на любом языке. Я потратил как минимум 2 дня, чтобы добраться до этого места. Я пробовал (погуглил) десятки способов передать мою двоичную строку в поток, и они терпят неудачу.
@cybernard Что такое «прямой двоичный код», как уже сказал jmcilhinney, кодировки должны совпадать.
Просто чтобы внести ясность: насколько я понимаю, вам нужно обрабатывать данные gzip из vbscript. Поскольку vbscript никогда не получал встроенной поддержки gzip и теперь умирает, поэтому никогда не будет, вы надеетесь написать метод .Net, который вы можете вызывать из vbscript, который может распаковывать данные gzip для вас. Таким образом, любое решение, позволяющее распаковывать данные gzip, подойдет, верно?
@JoelCoehoorn в основном СинийМонахMN имеет правильную идею. Но да, это правильная мысль. Он должен получить строку, а не файл, и вернуть строку. За исключением части «надеюсь», потому что процесс завершен на 90%, и у меня есть рабочий код прототипа. Я потрачу большую часть завтрашнего дня, работая над этим.





Прошло слишком много времени с тех пор, как я писал vbscript, поэтому я больше не знаю достаточно, чтобы исправлять ошибки. Однако я могу указать на некоторые серьезные недостатки в части vbscript этого кода.
Он начинается с чтения до 900 байт из файла .gz, независимо от фактической длины файла. Все, что длиннее 900 байт, не будет прочитано.
Он выполняет это чтение в двоичном режиме. Двоичный режим игнорирует любой набор символов или информацию о кодировке и просто считывает необработанные байты, что подходит для файла .gz. Однако следующее, что происходит с этими данными, — это использование функции Len(), предназначенной для струны, а не для двоичных данных; Len() — это нет, соответствующая функция здесь. Кроме того, данные затем используются в цикле For через функцию Mid(). Mid() также имеет обозначение только для строк, а вариант x — не строка. Строковые объекты vbscript — это больше, чем просто необработанные символы; они включают в себя метаданные для таких вещей, как кодирование, длина и символьные буферы, и эти строковые функции полагаются на правильность построения объектов со всеми метаданными.
Там ни за что этот vbscript выдает правильные результаты. Пока это не решено, нет смысла даже смотреть на код vb.net. Опять же, я слишком далеко зашел, чтобы предложить реальное решение, но я рекомендую попытаться передать на сторону .Net неизмененный массив байтов, а не строку.
Мне удалось заставить ваш код работать, изменив функцию и интерфейс 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 в два набора круглых скобок, но я думаю, что это как-то связано с принудительной передачей параметра по значению, а не по ссылке, и помогает ему преобразовать в массив байтов. Я получал ошибку, прежде чем добавил дополнительную пару скобок.
Редактировать: Чтобы ответить на некоторые другие ваши вопросы:
Хорошо, это выглядит довольно хорошо. Завтра сделаю тест. Однако как я могу добавить обработку ошибок? Скажем, кто-то передает строку 00, теперь gzip выдает ошибку, которую нужно обработать и передать обратно. Кроме того, что произойдет, если x = objStream.Read(342737) будет сокращено до Read(1000), поскольку файл больше, это вызовет ошибку, которую мне нужно будет обработать.
Да, ваш код vb.net проверен и исправен. Я не знаю почему, но без ((x)) мне отказывают в доступе. Я пробовал все комбинации byval, byRef, value, value(), byte и byte(), которые я мог найти, но они недоступны. Однако это избавляет меня от необходимости инвертировать мой буфер вручную, поэтому я сохраняю его.
@cybernard 342737 был просто для тестирования. Вы должны использовать adReadAll для чтения всего, что находится в потоке, вместо фиксированной длины. Вы можете обрабатывать ошибки в VBScript с помощью On Error Resume Next и в VB.NET с помощью try и catch.
Нет такой вещи, как преобразование
StringвStream. Вы создаетеStream, конвертируетеStringв массивByte, а затем записываете массивByteвStream. При переходе от массиваStringк массивуByteнеобходимо указать кодировку. Если вы передаете полученные двоичные данные и хотите впоследствии преобразовать их обратно в текст, вам необходимо использовать одну и ту же кодировку в обоих направлениях. Скорее всего, проблема в том, что вы не такой.