/// <summary> /// Sends the command to the driver. /// </summary> /// <param name="command">Command to be sent to the driver.</param> /// <param name="responseType">Type of the response. This parameter may be <see langword="null"/>.</param> /// <returns>Response received from the driver, or <see langword="null"/>, if the <paramref name="responseType"/> is <see langword="null"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="command"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentException"><paramref name="responseType"/> is not a structure type.</exception> /// <exception cref="InvalidOperationException"> /// Client is not connected. /// <para>-or-</para> /// Memory for the command or response was not allocated. /// <para>-or-</para> /// Message was not sent to the driver. /// </exception> private object ExecuteCommand(IDriverCommand command, Type responseType) { if (command == null) { throw new ArgumentNullException(nameof(command)); } if (responseType != null && (!responseType.IsValueType || responseType.IsPrimitive)) { throw new ArgumentException("Response type is not a structure type.", nameof(responseType)); } lock (this.syncRoot) { if (this.state != ConnectionState.Connected) { throw new InvalidOperationException("Client is not connected."); } // We want to have two separate buffers, because the driver can update the response buffer // while parsing the command. IntPtr commandBuffer = IntPtr.Zero; IntPtr responseBuffer = IntPtr.Zero; try { // Allocate buffer for the command. // Command header contains 'Type' and 'DataLength' (Int32) values. // See the 'DRIVER_COMMAND' structure in the 'CommunicationData.h' for details. int commandHeaderSize = Marshal.SizeOf(command.Type) + Marshal.SizeOf(typeof(int)); int commandDataSize = command.Data?.Length ?? 0; int commandSize = commandHeaderSize + commandDataSize; try { commandBuffer = Marshal.AllocHGlobal(commandSize); } catch (OutOfMemoryException oom) { string message = "Unable to allocate memory for the command."; DriverClientBase.Logger.Error(message, oom); throw new InvalidOperationException(message, oom); } // Marshal command to the buffer allocated, if the command.Data is NULL, it'll be ignored. MarshalingHelper.MarshalObjectsToPointer(commandBuffer, commandSize, command.Type, commandDataSize, command.Data); // // Allocate the response buffer, if needed. // int responseSize = 0; if (responseType != null) { responseSize = Marshal.SizeOf(responseType); try { responseBuffer = Marshal.AllocHGlobal(responseSize); } catch (OutOfMemoryException oom) { string message = "Unable to allocate memory for the response."; DriverClientBase.Logger.Error(message, oom); throw new InvalidOperationException(message, oom); } } // // Send command to the driver. // uint bytesReceived; uint hr = NativeMethods.FilterSendMessage(this.filterPortHandle, commandBuffer, (uint)commandSize, responseBuffer, (uint)responseSize, out bytesReceived); if (hr != NativeMethods.Ok) { string message = string.Format(CultureInfo.InvariantCulture, "Unable to send message to the driver: 0x{0:X8}", hr); Exception innerException = Marshal.GetExceptionForHR((int)hr); DriverClientBase.Logger.Error(innerException, message); throw new InvalidOperationException(message, innerException); } // Return NULL, if response type is not specified. return(responseType == null ? null : Marshal.PtrToStructure(responseBuffer, responseType)); } catch { this.Disconnect(); this.state = ConnectionState.Faulted; throw; } finally { if (commandBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(commandBuffer); } if (responseBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(responseBuffer); } } } }
private void ProcessNotification(ResizableBuffer resizableBuffer) { IntPtr bufferPointer = resizableBuffer.DangerousGetPointer(); // Marshal the notification received. DriverNotificationHeader header = (DriverNotificationHeader)Marshal.PtrToStructure(bufferPointer, typeof(DriverNotificationHeader)); DriverNotification notification = new DriverNotification { Type = header.Type, DataLength = header.DataLength, Data = bufferPointer + this.notificationHeaderSize }; // Get the reply object from the user-defined handler. object reply = null; int handlerResult = (int)NativeMethods.Ok; try { reply = this.handler(notification); } catch (Exception e) { handlerResult = Marshal.GetHRForException(e); NotificationsMonitor.Logger.Error(e, "Notification handler threw an exception."); } // Driver is not expecting any reply. if (header.ReplyLength == 0) { if (reply != null) { NotificationsMonitor.Logger.Warn(CultureInfo.InvariantCulture, "Driver is not expecting any reply, but reply object is returned from handler: {0}", reply.GetType()); } return; } int replySize = this.replyHeaderSize + MarshalingHelper.GetObjectSize(reply); if (replySize > header.ReplyLength) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Reply ({0} bytes) is bigger than the one expected by the driver ({1} bytes).", replySize, header.ReplyLength)); } DriverReplyHeader replyHeader = new DriverReplyHeader { MessageId = header.MessageId, // Notify driver about the exception thrown, if any. Status = handlerResult }; // Adjust the buffer to fit the reply. resizableBuffer.Resize(replySize); bufferPointer = resizableBuffer.DangerousGetPointer(); // Marshal reply to the output buffer. MarshalingHelper.MarshalObjectsToPointer(bufferPointer, replySize, replyHeader, reply); // And send it to the driver. uint hr = NativeMethods.FilterReplyMessage(this.filterPortHandle, bufferPointer, (uint)replySize); if (hr != NativeMethods.Ok) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to send reply: 0x{0:X8}", hr), Marshal.GetExceptionForHR(unchecked ((int)hr))); } }