2013-12-19 5 views
0

Я весь день бил головой об этом, и я просто не могу разобраться в различной (иногда противоречивой) документации по этому вопросу. Чтобы добавить к путанице, в какой-то момент в течение дня это выполняло (вроде) работу, то есть не бросало ошибку нарушения доступа. Но данные на другом конце трубы были бессмысленными, поэтому я подозреваю, что это только «сработало» случайно.Ошибки, вызывающие WriteFile из визуального базового .net 2010 по именованному каналу

У меня есть программа vb.net (с использованием .net 3.0, поэтому нет поддержки System.IO.NamedPipes :(), которая создает именованный канал и ждет другого приложения для подключения и отправки некоторых данных. В качестве 'ack' , Я тогда хочу, чтобы программа vb отправила обратно общую длину полученного сообщения. Я могу создать канал, дождаться соединения и получить сообщение, но он пытается отправить «ack» с помощью WriteFile. (ток) определение я использую для WriteFile основано на соответствующий ReadFile, который, кажется, работает хорошо:

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

Declare Function WriteFile Lib "kernel32" (_ 
    ByVal hFile As Long, _ 
    ByRef lpBuffer As Byte, _ 
    ByVal nNumberOfBytesToWrite As Integer, _ 
    ByRef lpNumberOfBytesWritten As Integer, _ 
    ByVal lpOverlapped As Integer) _ 
    As Integer 

урезанного код (проверка ошибок и отладка печать удалена) выглядят следующим образом:

Dim openMode = PIPE_ACCESS_DUPLEX Or FILE_FLAG_FIRST_PIPE_INSTANCE 
Dim pipeMode = PIPE_WAIT Or PIPE_TYPE_MESSAGE Or PIPE_READMODE_MESSAGE 
Dim res ' Result of dll calls 

pipeN2Q = CreateNamedPipe("\\.\pipe\N2Q", openMode, pipeMode, 10, 1024, 1024, 2000, IntPtr.Zero) 
res = ConnectNamedPipe(pipeN2Q, 0) 

Dim rxCount As Integer = 0 ' To hold the number of bytes received 
Dim txCount As Integer = 0 ' To hold the number of bytes sent 
Dim txReq As Integer = 2 ' To hold the number of bytes we're going to ask to be sent during the 'ack' 

Dim dataIn(256) As Byte 

res = ReadFile(pipeN2Q, dataIn(0), 256, rxCount, Nothing) 

Dim recvBuffer As String = System.Text.Encoding.ASCII.GetString(dataIn, 0, rxCount) 

Dim dataOut(2) As Byte 
dataOut(0) = 42 
dataOut(1) = 43 

res = WriteFile(pipeN2Q, dataOut(0), txReq, txCount, Nothing) 

После того, как код попадает в WriteFile, он бросает AccessViolationException - «Попытка чтения или записи в защищенную память.» Я предполагаю, что это жалуется параметр dataOut, но он не дает никаких дальнейших подробностей. Вещи, которые я пробовал до сих пор включают в себя:

  • Изменение декларации WriteFile так что lpBuffer объявлен: ByVal lpBuffer as IntPtr
  • DATAOUT используя Выделение Marshal.AllocHGlobal(txReq) и инициализируется с помощью Marshal.WriteInt16()
  • Выделяя большой буфер для dataOut (1024 байт) и инициализации их на нули

Чтобы быть ясным, сообщение, полученное из другого приложения, получено отлично и recvBuffer имеет строку точно как отправлено. Я просто не могу убедить WriteFile сотрудничать (за исключением одного, возможно, случайно, и я не смог повторить его). Я надеюсь, что это просто что-то в декларации или инициализации переменных - какие-то подсказки?

+1

Ваши объявления о плющике плохие. Используйте сайт pinvoke.net, чтобы найти хорошие. –

+0

Спасибо - pinvoke.net был одним из сайтов, на которые я ссылался - хотя у него есть 3 разных подписи для WriteFile, а тип возврата для ReadFile показан как SafeFileHandle, тогда как в MSDN он отображается как Boolean. Поэтому я действительно не доверял этому ... Однако версия с StringBuilder как тип буфера передачи, наконец, позволила мне отправлять чистые данные с обеих сторон, поэтому спасибо за указатель. – dsl101

ответ

0

Основываясь на предположении Ганса использовать pinvoke.net, вот комбинация подписей и звонков, которые в конечном итоге сработали для меня - на всякий случай, если кто-то еще сможет ее использовать. Я знаю, что, вероятно, мне следовало бы преобразовать эти первые 3 заявления в тот же формат, что и ReadFile/WriteFile, но он работает на данный момент ...

Declare Function CreateNamedPipe Lib "kernel32" Alias "CreateNamedPipeA" (_ 
    ByVal lpName As String, ByVal dwOpenMode As Integer, _ 
    ByVal dwPipeMode As Integer, ByVal nMaxInstances As Integer, _ 
    ByVal nOutBufferSize As Integer, ByVal nInBufferSize As Integer, _ 
    ByVal nDefaultTimeOut As Integer, ByVal lpSecurityAttributes As IntPtr) _ 
    As SafeFileHandle 

Declare Function ConnectNamedPipe Lib "kernel32" (_ 
    ByVal hNamedPipe As SafeFileHandle, ByVal lpOverlapped As System.Threading.NativeOverlapped) _ 
    As SafeFileHandle 

Declare Function CloseHandle Lib "kernel32" (_ 
    ByVal hFile As SafeFileHandle) _ 
    As Integer 

<DllImport("kernel32.dll", SetlastError:=True)> Friend Shared Function WriteFile(_ 
ByVal File As SafeFileHandle, _ 
ByVal Buffer As System.Text.StringBuilder, _ 
ByVal NumberOfBytesToWrite As Integer, _ 
ByRef NumberOfBytesWritten As Integer, _ 
ByRef Overlapped As System.Threading.NativeOverlapped) As <MarshalAs(UnmanagedType.Bool)> Boolean 
End Function 

<DllImport("kernel32.dll")> Friend Shared Function ReadFile(_ 
ByVal File As SafeFileHandle, _ 
ByVal Buffer As System.Text.StringBuilder, _ 
ByVal NumberOfBytesToRead As Integer, _ 
ByRef NumberOfBytesRead As Integer, _ 
ByRef Overlapped As System.Threading.NativeOverlapped) As <MarshalAs(UnmanagedType.Bool)> Boolean 
End Function 

... 

pipeN2Q = CreateNamedPipe("\\.\pipe\N2Q", openMode, pipeMode, 10, 1024, 1024, 2000, IntPtr.Zero) 
If Not pipeN2Q.IsInvalid Then 
    If Not ConnectNamedPipe(pipeN2Q, Nothing).IsInvalid Then 

     Dim rxCount As Integer = 0 
     Dim txCount As Integer = 0 

     Dim dataIn As New System.Text.StringBuilder 

     res = ReadFile(pipeN2Q, dataIn, 256, rxCount, Nothing) 
     If res <> 0 Then 
      Dim dataOut As New StringBuilder(dataIn.Length.ToString) 

      res = WriteFile(pipeN2Q, dataOut, dataOut.Length, txCount, Nothing) 
     Else 
      Debug.Print("ReadFile ERROR: " & Err.LastDllError) 
     End If 
    Else 
     Debug.Print("ConnectNamedPipe ERROR: " & Err.LastDllError) 
    End If 
    If CloseHandle(pipeN2Q) <> 0 Then 
     Debug.Print("Pipe closed") 
    Else 
     Debug.Print("CloseHandle ERRPR: " & Err.LastDllError) 
    End If 
Else 
    Debug.Print("CreateNamedPipe ERROR: " & Err.LastDllError) 
End If 
+0

эй, ваш ответ здесь может помочь мне с чем-то, над чем я работаю. Но у меня есть вопрос ... когда вы называете 'CreateNamedPipe', что должны быть' OpenMode' и 'pipeMode'? Благодаря! – Rob

+1

Whoa! Пришлось свалить несколько файлов, чтобы найти это! Теперь я перешел к использованию сокетов - гораздо более аккуратное решение, если это возможно для вас.Тем не менее, здесь были заявления, которые я использовал в коде выше, который сделал работу по большей части: Dim OpenMode = PIPE_ACCESS_DUPLEX Или FILE_FLAG_FIRST_PIPE_INSTANCE Dim pipeMode = PIPE_WAIT Или PIPE_TYPE_MESSAGE Или PIPE_READMODE_MESSAGE Вы можете найти все варианты здесь: HTTP : //msdn.microsoft.com/en-gb/library/windows/desktop/aa365150 (v = vs.85) .aspx – dsl101