VB.Net在32位和64位系统上调用win32 api,在32但不是64

时间:2017-03-07 14:38:38

标签: .net vb.net winapi named-pipes

为了使现有的托管应用程序具有64位的非托管“附属”,我决定将32位非托管VC ++ dll重写为托管VB.Net类库。

应用程序必须能够在2.0以上的任何框架上运行,并且我移植的VC ++代码使用命名管道,仅支持框架3.5。 所以我不得不求助于反射,为与管道相关的所有东西调用windows API函数。

结果在32位模式下完美运行,但在64位中,我遇到了各种麻烦。即使源代码中的变化最小,实际行为也可能发生显着变化,只是交换一些变量声明或将简单的赋值语句移动到代码中的另一个位置可能会导致任何影响

  • “每隔X小时运行一次几乎没有明显的打嗝”,
  • “似乎不时会收到一些不良数据”(实际上,WireShark和运气相当不错,发现在命名管道上收到的13字节数据包的第一个字节作为0x00传递给托管代码,虽然它实际上是在管道数据包中收到0x02,而其余的数据包是完整传递的)

  • “当客户端尝试连接到管道时,很快就会崩溃整个.Net运行时在mscorwks.dll中出现错误0xc0000409”。

如果你问我,症状的快速变化,尤其是后者,指向某个地方,可能是一个指针或句柄,在64位运行时仍然以32位大小存储或传递给Win32 API。位。但我没有成功搞清楚是什么。

最有可能的位置在API声明中。其余的都是托管代码,并使用选项'strict'和'explicit'进行编译。 有人可以看看下面的片段,看看他是否能发现我一直忽视的东西?

操作是异步的。实际的等待是在包含托管和非托管句柄的数组的托管级别上执行的(通过SafeWaitHandle包装),因此可以通过单个WaitHandle.WaitAny(...数组...)处理与非托管内容一起管理的事件。 )打电话。

<DllImport("kernel32", SetLastError:=True)> Private Shared Function CreateNamedPipe(
    lpName As String,
    dwOpenMode As Int32,
    dwPipeMode As Int32,
    nMaxInstances As Int32,
    nOutBufferSize As Int32,
    nInBufferSize As Int32,
    nDefaultTimeOut As Int32,
    lpSecurityAttributes As IntPtr    ' when declared as SECURITY_ATTRIBUTES runtime won't accept passing Nothing, even when marked <[Optional]>
) As Microsoft.Win32.SafeHandles.SafeFileHandle : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function ConnectNamedPipe(
    hNamedPipe As SafeHandle,
    ByRef lpOverlapped As System.Threading.NativeOverlapped
) As Boolean : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function DisconnectNamedPipe(
    ByVal hNamedPipe As SafeHandle
) As Boolean : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function ReadFile(
    <[In]> hFile As SafeHandle,
    <Out> lpBuffer As IntPtr,
    <[In]> nNumberOfBytesToRead As Int32,
    <Out, [Optional]> ByRef lpNumberOfBytesRead As Int32,
    <[In], Out, [Optional]> ByRef lpOverlapped As System.Threading.NativeOverlapped
) As Boolean : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function WriteFile(
    <[In]> hFile As SafeHandle,
    <[In]> lpBuffer As IntPtr,
    <[In]> nNumberOfBytesToWrite As Int32,
    <[Out], [Optional]> ByRef lpNumberOfBytesWritten As Int32,
    <[In], [Out], [Optional]> ByRef lpOverlapped As NativeOverlapped
) As Boolean : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function CloseHandle(hHandle As SafeHandle) As Boolean : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function CloseHandle(hHandle As IntPtr) As Boolean : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function GetOverlappedResult(
    ByVal hFile As SafeHandle,
    ByRef lpOverlapped As System.Threading.NativeOverlapped,
    ByRef lpNumberOfBytesTransferred As Int32,
    ByVal bWait As Boolean
) As Boolean : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function CancelIo(
    <[In]> hFile As SafeHandle
) As Boolean : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function PeekNamedPipe(
    <[In]> hNamedPipe As SafeHandle,
    <Out, [Optional]> ByRef lpBuffer As Byte(),
    <[In]> nBufferSize As Integer,
    <Out, [Optional]> ByRef lpBytesRead As Integer,
    <Out, [Optional]> ByRef lpTotalBytesAvail As Integer,
    <Out, [Optional]> ByRef lpBytesLeftThisMessage As Integer
) As Boolean : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function GetProcessHeap() As IntPtr : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function HeapAlloc(
    <[In]> hHeap As IntPtr,
    <[In]> dwFlags As Int32,
    <[In]> dwBytes As IntPtr
) As IntPtr : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function HeapFree(
    <[In]> hHeap As IntPtr,
    <[In]> dwFlags As Int32,
    <[In]> lpMem As IntPtr
) As Boolean : End Function

<DllImport("kernel32", SetLastError:=True)> Private Shared Function CreateEvent(
    <[In], [Optional]> lpEventAttributes As IntPtr,
    <[In]> bManualReset As Boolean,
    <[In]> bInitialState As Boolean,
    <[In], [Optional]> lpName As String
) As IntPtr : End Function

这就是CreateEvent用于创建句柄托管代码可以等待的方式:

Private Shared Function AllocateEventHandle() As Microsoft.Win32.SafeHandles.SafeWaitHandle
    Return New Microsoft.Win32.SafeHandles.SafeWaitHandle(CreateEvent(Nothing, False, False, Nothing), True)
End Function

在调用非托管代码时使用的缓冲区如下所示:

Private Shared Function AllocateBuffer(nBytes As Integer) As IntPtr
    Return HeapAlloc(GetProcessHeap(), 0, New IntPtr(nBytes))
End Function

1 个答案:

答案 0 :(得分:-1)

我不知道为什么它会在32位和64位之间产生任何影响,但这似乎是困扰我的:

在整个.Net文档中,你读到这个或那个对象类型是线程安全的,只要它是静态的(VB共享) - 这么多,以至于我默默地开始相信它对所有对象类型都是正确的。我尤其期望它对于通常几乎通常用于多线程处理的事物(例如事件句柄)是正确的。

64位代码似乎并非总是如此。

总结一下:我的项目的32位版本总是按原样运行,AnyCpu构建在64位系统上没有。

对于命名管道处理(创建,连接和I / O),我通过API调用分配了win32事件句柄和缓冲区。事件句柄包装在SafeWaitHandles中以供托管使用,缓冲区只是由管理端的IntPtr指向。

因为这些句柄和缓冲区在应用程序启动时只创建了一次,并且在执行期间从不需要释放或替换,所以我尽可能地从应用程序的主启动线程中分配它们。 它们稍后将由另一个线程使用,可以在整个过程的生命周期内多次启动和停止。

我现在所做的更多是通过实验而不是出于智慧,将这些句柄和缓冲区的创建移动到将使用它们的线程中(并在线程出口处释放它们)。

问题就像它从未存在过一样,32和64位运行现在都非常稳定。