【问题标题】:VB.NET 2008 or higher: Memory violation reading STDIN via winapiVB.NET 2008 或更高版本:通过 winapi 读取 STDIN 的内存冲突
【发布时间】:2020-11-13 09:13:36
【问题描述】:

我正在尝试将 VB6 应用程序移植到 VB.NET。 这是一个控制台应用程序,它从 STDIN 读取数据,修改数据并将其写入 STDOUT。

Google 是我的朋友,所以我现在花了几天时间寻找解决方案。 这个搜索为我提供了几个版本的 WinApi ReadFile,我都试过了。 我也尝试了几个版本的.NET。 但是.....在我尝试过的每一种组合中,我都会不断收到内存违规错误。 根据我使用的 ReadFile 变体,错误会略有不同,但都相似。

例如看这个错误的变种:

makeDataFilter < Test.in > Test.Out

Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at makeDataFilter.Filter_IO.ReadFile(Int64 hFile, String& lpBuffer, Int64 nNumberOfBytesToRead, Int64 lpNumberOfBytesRead, String& lpOverlapped)
   at makeDataFilter.Filter_IO.GetData() in C:\Projects\.NET 2019\makeDataFilter\Filter_IO.vb:line 229
   at makeDataFilter.Filter_IO.Main() in C:\Projects\.NET 2019\makeDataFilter\Filter_IO.vb:line 118
   
   

我用这个来获取句柄:

Public Const STD_INPUT_HANDLE = -10&
Public Const STD_OUTPUT_HANDLE = -11&

' Open en handle to StdIn or StdOut
    Private Declare Function GetStdHandle Lib "kernel32.dll" (
        ByVal nStdHandle As Int32
    ) As IntPtr

这是产生上述错误的 ReadFile API 调用的变体:

<DllImport("kernel32")>
Declare Function ReadFile(
    ByVal hFile As Long,
    lpBuffer As String,
    nNumberOfBytesToRead As Long,
    lpNumberOfBytesRead As Long,
    lpOverlapped As String
) As String

以下是我发现并尝试过的所有其他变体。都给出类似的错误:

' Read from Stdin
'Declare Function ReadFile Lib "kernel32.dll" (
'    ByVal hFile As Long,
'    lpBuffer As Any,
'    ByVal nNumberOfBytesToRead As Long,
'    lpNumberOfBytesRead As Long,
'    lpOverlapped As Any
') As Long

'Declare Function ReadFile Lib "kernel32" (
'      ByVal hFile As Long,
'      lpBuffer As String,
'      ByVal nNumberOfBytesToRead As Long,
'      lpNumberOfBytesRead As Long,
'      ByVal lpOverlapped As String
') As Long

'Declare Auto Function ReadFile Lib "Kernel32.dll" (
'    ByVal hndRef As Integer,
'    ByVal lpBuffer As StringBuilder,
'    ByVal numberOfBytesToRead As Integer,
'    ByRef numberOfBytesRead As Integer,
'    ByVal flag As Integer
') As Boolean

'Declare Function ReadFile Lib "kernel32" (
'        ByVal hFile As Integer,
'        ByVal lpBuffer As Integer,
'        ByVal nNumberOfBytesToRead As Integer,
'        lpNumberOfBytesRead As Integer,
'        ByVal lpOverlapped As Integer
') As Integer

我在代码中使用它的方式在这里:

Sub GetData()
    Dim sBuff As String         
    Dim lBytesRead As Long      
    Dim rc As Long              
                                
    lContentLength = 256        
    sBuff = lContentLength      
    Do
        rc = ReadFile(hStdIn, sBuff, lContentLength, lBytesRead, 0&)
        sFilterData &= Left$(sBuff, lBytesRead)
    Loop While rc = 1
End Sub

请有人帮助我找到一种工作方式来读取 STDIN 并写入 VB.NET 中的 STDOUT 吗?

【问题讨论】:

  • System.String 是不可变的,因此它绝不能作为缓冲区参数的类型出现。请改用 StringBuilder。并且一定要避免这段代码中导致AVE的bug,你必须在调用之前设置它的Capacity。
  • StringBuilder....... 请提供代码示例。我是一个偶尔的 .net 程序员 :-)
  • @RensDuijsens 见Default Marshaling for Strings: Fixed Length String Buffers。尽管该示例是为 C# 编写的,但同样的概念也适用于 VB.NET

标签: vb.net winapi stdout stdin kernel32


【解决方案1】:

我在想……

.NET Framework 提供了一个System.Console 类,它包含读取/写入标准输入/输出流的功能。不使用该功能是否有特殊原因?

我猜你的GetData 方法可以改写成这样:

Sub GetData()
    sFilterData = Console.In.ReadToEnd()
End Sub

但是,“动态”处理输入流数据而不是先读取整个输入流并将其存储到字符串中然后开始处理可能更有效(尤其是在内存使用方面)。

【讨论】:

  • 如果您包括使用Console.IsInputRedirectedConsole.IsOutputRedirected 在未重定向的情况下允许程序流控制,您的答案会更好。无论如何都是好答案。
  • 嗯........有时事情比人们期望的要简单。 :-) 就像我提到的,我的代码来自 VB6。我不知道这样做的“新”方法。是的,关闭,因为这要好得多。感谢您的提醒。非常感谢您提供快速而清晰的答案。虽然我想知道我是否尝试查找有关“从 STDIN 读取的 VB.NET”的信息,但没有出现类似的情况。我只看到了 Kernel32 方法的几种变体。比如我用“GetPrivateProfileString”读取ini文件的方式
  • 啊,是的。我懂了。当我个人尝试在 Google 上搜索某些内容时,我也会尝试搜索潜在的文档内容,这些内容通常使用常规的英文术语。我刚刚通过搜索“VB.NET 从标准输入流读取”进行了尝试,第一个结果是对Console.OpenStandardInput 方法的引用。在您的情况下,也许 STDIN 是一个过于技术性或过于具体的搜索标准...... ;)
  • .NET Framework 不直接支持 INI 文件。你现在确实可以使用内核函数 (GetPrivateProfileString)。但是,从长远来看(在迁移您的应用程序之后),我建议您为配置文件切换到另一种文件格式,例如 XML 或 JSON。这不仅会使您的应用程序更便携(必要时),而且还会提供更多的技术配置灵活性。 (您可能需要搜索“.NET app.config”或“.NET Core appsettings.json”以获取更多信息。)
  • 谢谢巴特,两个答案。很有用。自从我的 VB6 天以来,发生了很多变化。 :-D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-31
  • 2015-07-31
相关资源
最近更新 更多