diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/PipeState.java b/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/PipeState.java deleted file mode 100644 index 81173b42d..000000000 --- a/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/PipeState.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.slimevr.desktop.platform.windows; - -public enum PipeState { - CREATED, - OPEN, - ERROR -} diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsNamedPipeBridge.java b/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsNamedPipeBridge.java deleted file mode 100644 index 5099fccd8..000000000 --- a/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsNamedPipeBridge.java +++ /dev/null @@ -1,333 +0,0 @@ -package dev.slimevr.desktop.platform.windows; - -import com.google.protobuf.CodedOutputStream; -import com.google.protobuf.InvalidProtocolBufferException; -import com.sun.jna.Native; -import com.sun.jna.platform.win32.*; -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.ptr.IntByReference; -import com.sun.jna.win32.W32APIOptions; -import dev.slimevr.VRServer; -import dev.slimevr.bridge.BridgeThread; -import dev.slimevr.desktop.platform.ProtobufMessages.ProtobufMessage; -import dev.slimevr.desktop.platform.SteamVRBridge; -import dev.slimevr.tracking.trackers.Tracker; -import io.eiren.util.ann.ThreadSafe; -import io.eiren.util.logging.LogManager; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.util.List; - - -interface Kernel32IO extends Kernel32 { - Kernel32IO INSTANCE = Native.load("kernel32", Kernel32IO.class, W32APIOptions.DEFAULT_OPTIONS); - - boolean GetOverlappedResult( - /* [in] */ HANDLE hFile, - /* [in] */ WinBase.OVERLAPPED lpOverlapped, - /* [out] */ IntByReference lpNumberOfBytesTransferred, - /* [in] */ boolean bWait - ); -} - - -public class WindowsNamedPipeBridge extends SteamVRBridge { - private static final Kernel32 k32 = Kernel32.INSTANCE; - private static final Kernel32IO k32io = Kernel32IO.INSTANCE; - private static final Advapi32 adv32 = Advapi32.INSTANCE; - - protected final String pipeName; - protected final String bridgeSettingsKey; - private final byte[] buffArray = new byte[2048]; - protected WindowsPipe pipe; - protected WinNT.HANDLE openEvent = k32.CreateEvent(null, false, false, null); - protected WinNT.HANDLE readEvent = k32.CreateEvent(null, false, false, null); - protected WinNT.HANDLE writeEvent = k32.CreateEvent(null, false, false, null); - protected WinNT.HANDLE rxEvent = k32.CreateEvent(null, false, false, null); - protected WinNT.HANDLE txEvent = k32.CreateEvent(null, false, false, null); - protected WinNT.HANDLE[] events = new WinNT.HANDLE[] { rxEvent, txEvent }; - private final WinBase.OVERLAPPED overlappedOpen = new WinBase.OVERLAPPED(); - private final WinBase.OVERLAPPED overlappedWrite = new WinBase.OVERLAPPED(); - private final WinBase.OVERLAPPED overlappedRead = new WinBase.OVERLAPPED(); - private final WinBase.OVERLAPPED overlappedWait = new WinBase.OVERLAPPED(); - private final IntByReference bytesWritten = new IntByReference(0); - private final IntByReference bytesAvailable = new IntByReference(0); - private final IntByReference bytesRead = new IntByReference(0); - private boolean pendingWait = false; - - public WindowsNamedPipeBridge( - VRServer server, - String bridgeSettingsKey, - String bridgeName, - String pipeName, - List shareableTrackers - ) { - super(server, "Named pipe thread", bridgeName, bridgeSettingsKey, shareableTrackers); - this.pipeName = pipeName; - this.bridgeSettingsKey = bridgeSettingsKey; - overlappedWait.hEvent = rxEvent; - } - - @Override - @BridgeThread - public void run() { - try { - createPipe(); - while (true) { - boolean pipesUpdated = false; - if (pipe.state == PipeState.CREATED) { - // Report that our pipe is disconnected right now - reportDisconnected(); - tryOpeningPipe(pipe); - } - if (pipe.state == PipeState.OPEN) { - pipesUpdated = updatePipe(); - if (pipesUpdated) { - reportConnected(); - } - updateMessageQueue(); - } - if (pipe.state == PipeState.ERROR) { - resetPipe(); - } - if (!pipesUpdated) { - if (pipe.state == PipeState.OPEN) { - waitForData(10); - } else { - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - } - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - @Override - @ThreadSafe - protected void signalSend() { - k32.SetEvent(txEvent); - } - - @BridgeThread - private void waitForData(int timeoutMs) { - if (pipe.state != PipeState.OPEN) - return; - if (!pendingWait) { - k32.ReadFile(pipe.pipeHandle, null, 0, null, overlappedWait); - pendingWait = true; - } - int evIdx = k32.WaitForMultipleObjects(events.length, events, false, timeoutMs); - if (evIdx == 0) { - // events[0] == overlappedWait.hEvent == rxEvent - pendingWait = false; - } - } - - @Override - @BridgeThread - protected boolean sendMessageReal(ProtobufMessage message) { - if (pipe.state != PipeState.OPEN) { - return false; - } - try { - int size = message.getSerializedSize(); - CodedOutputStream os = CodedOutputStream.newInstance(buffArray, 4, size); - message.writeTo(os); - size += 4; - buffArray[0] = (byte) (size & 0xFF); - buffArray[1] = (byte) ((size >> 8) & 0xFF); - buffArray[2] = (byte) ((size >> 16) & 0xFF); - buffArray[3] = (byte) ((size >> 24) & 0xFF); - - overlappedWrite.clear(); - overlappedWrite.hEvent = writeEvent; - boolean immediate = k32 - .WriteFile(pipe.pipeHandle, buffArray, size, null, overlappedWrite); - int err = k32.GetLastError(); - if (!immediate && err != WinError.ERROR_IO_PENDING) { - setPipeError("WriteFile failed: " + err); - return false; - } - - if (!k32io.GetOverlappedResult(pipe.pipeHandle, overlappedWrite, bytesWritten, true)) { - setPipeError( - "sendMessageReal/GetOverlappedResult failed: " + k32.GetLastError() - ); - return false; - } - - if (bytesWritten.getValue() != size) { - setPipeError("Bytes written " + bytesWritten.getValue() + ", expected " + size); - return false; - } - - return true; - } catch (IOException e) { - e.printStackTrace(); - } - return false; - } - - private boolean updatePipe() throws IOException { - if (pipe.state != PipeState.OPEN) { - return false; - } - boolean readAnything = false; - while (k32.PeekNamedPipe(pipe.pipeHandle, buffArray, 4, null, bytesAvailable, null)) { - if (bytesAvailable.getValue() < 4) { - return readAnything; // Wait for more data - } - int messageLength = (Byte.toUnsignedInt(buffArray[3]) << 24) - | (Byte.toUnsignedInt(buffArray[2]) << 16) - | (Byte.toUnsignedInt(buffArray[1]) << 8) - | Byte.toUnsignedInt(buffArray[0]); - if (messageLength > 1024) { // Overflow - setPipeError("Pipe overflow. Message length: " + messageLength); - return readAnything; - } - if (bytesAvailable.getValue() < messageLength) { - return readAnything; // Wait for more data - } - - overlappedRead.clear(); - overlappedRead.hEvent = readEvent; - boolean immediate = k32 - .ReadFile(pipe.pipeHandle, buffArray, messageLength, null, overlappedRead); - int err = k32.GetLastError(); - if (!immediate && err != WinError.ERROR_IO_PENDING) { - setPipeError("ReadFile failed: " + err); - return readAnything; - } - - if (!k32io.GetOverlappedResult(pipe.pipeHandle, overlappedRead, bytesRead, true)) { - setPipeError( - "updatePipe/GetOverlappedResult failed: " + k32.GetLastError() - ); - return readAnything; - } - - if (bytesRead.getValue() != messageLength) { - setPipeError( - "Bytes read " + bytesRead.getValue() + ", expected " + messageLength - ); - return readAnything; - } - - try { - ProtobufMessage message = ProtobufMessage - .parser() - .parseFrom(buffArray, 4, messageLength - 4); - messageReceived(message); - readAnything = true; - } catch (InvalidProtocolBufferException parseEx) { - parseEx.printStackTrace(); - setPipeError("Failed to parse message: " + parseEx.getMessage()); - return readAnything; - } - } - - int err = k32.GetLastError(); - if (err == WinError.ERROR_BROKEN_PIPE) { - setPipeError("Pipe closed"); - } else { - setPipeError("Pipe error: " + err); - } - return readAnything; - } - - private void setPipeError(String message) { - pipe.state = PipeState.ERROR; - LogManager.severe("[" + bridgeName + "] " + message); - } - - private void resetPipe() { - WindowsPipe.safeDisconnect(pipe); - pipe.state = PipeState.CREATED; - VRServer.Companion.getInstance().queueTask(this::disconnected); - } - - private void createPipe() throws IOException { - try { - WinNT.SECURITY_DESCRIPTOR descriptor = new WinNT.SECURITY_DESCRIPTOR(64 * 1024); - adv32.InitializeSecurityDescriptor(descriptor, WinNT.SECURITY_DESCRIPTOR_REVISION); - adv32.SetSecurityDescriptorDacl(descriptor, true, null, false); - adv32 - .SetSecurityDescriptorControl( - descriptor, - (short) WinNT.SE_DACL_PROTECTED, - (short) WinNT.SE_DACL_PROTECTED - ); - - WinBase.SECURITY_ATTRIBUTES attributes = new WinBase.SECURITY_ATTRIBUTES(); - attributes.lpSecurityDescriptor = descriptor.getPointer(); - attributes.bInheritHandle = false; - - pipe = new WindowsPipe( - k32 - .CreateNamedPipe( - pipeName, - WinBase.PIPE_ACCESS_DUPLEX | WinNT.FILE_FLAG_OVERLAPPED, // dwOpenMode - WinBase.PIPE_TYPE_BYTE | WinBase.PIPE_READMODE_BYTE | WinBase.PIPE_WAIT, // dwPipeMode - 1, // nMaxInstances, - 1024 * 16, // nOutBufferSize, - 1024 * 16, // nInBufferSize, - 0, // nDefaultTimeOut, - attributes // lpSecurityAttributes - ), - pipeName - ); - LogManager.info("[" + bridgeName + "] Pipe " + pipe.name + " created"); - if (WinBase.INVALID_HANDLE_VALUE.equals(pipe.pipeHandle)) { - throw new IOException("Can't open " + pipeName + " pipe: " + k32.GetLastError()); - } - LogManager.info("[" + bridgeName + "] Pipes are created"); - } catch (IOException e) { - WindowsPipe.safeDisconnect(pipe); - throw e; - } - } - - private boolean tryOpeningPipe(WindowsPipe pipe) { - overlappedOpen.clear(); - overlappedOpen.hEvent = openEvent; - - boolean ok = k32.ConnectNamedPipe(pipe.pipeHandle, overlappedOpen); - int err = k32.GetLastError(); - if (!ok && err != WinError.ERROR_PIPE_CONNECTED) { - if (err != WinError.ERROR_IO_PENDING) { - setPipeError("ConnectNamedPipe failed: " + err); - return false; - } - - if (!k32io.GetOverlappedResult(pipe.pipeHandle, overlappedOpen, bytesRead, true)) { - setPipeError( - "tryOpeningPipe/GetOverlappedResult failed: " + k32.GetLastError() - ); - return false; - } - } - - pipe.state = PipeState.OPEN; - LogManager.info("[" + bridgeName + "] Pipe " + pipe.name + " is open"); - VRServer.Companion.getInstance().queueTask(this::reconnected); - return true; - } - - @Override - public boolean isConnected() { - return pipe != null && pipe.state == PipeState.OPEN; - } - - @NotNull - @Override - public String getBridgeConfigKey() { - return this.bridgeSettingsKey; - } -} diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsNamedPipeBridge.kt b/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsNamedPipeBridge.kt new file mode 100644 index 000000000..8bdb94922 --- /dev/null +++ b/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsNamedPipeBridge.kt @@ -0,0 +1,337 @@ +package dev.slimevr.desktop.platform.windows + +import com.google.protobuf.CodedOutputStream +import com.google.protobuf.InvalidProtocolBufferException +import com.sun.jna.Native +import com.sun.jna.platform.win32.Advapi32 +import com.sun.jna.platform.win32.Kernel32 +import com.sun.jna.platform.win32.WinBase +import com.sun.jna.platform.win32.WinError +import com.sun.jna.platform.win32.WinNT +import com.sun.jna.ptr.IntByReference +import com.sun.jna.win32.W32APIOptions +import dev.slimevr.VRServer +import dev.slimevr.bridge.BridgeThread +import dev.slimevr.desktop.platform.ProtobufMessages.ProtobufMessage +import dev.slimevr.desktop.platform.SteamVRBridge +import dev.slimevr.tracking.trackers.Tracker +import io.eiren.util.ann.ThreadSafe +import io.eiren.util.logging.LogManager +import java.io.IOException +import java.lang.Byte +import kotlin.Boolean +import kotlin.ByteArray +import kotlin.Int +import kotlin.String +import kotlin.Suppress +import kotlin.Throws +import kotlin.arrayOf + +internal interface Kernel32IO : Kernel32 { + @Suppress("FunctionName") + fun GetOverlappedResult( + hFile: WinNT.HANDLE?, /* [in] */ + lpOverlapped: WinBase.OVERLAPPED?, /* [in] */ + lpNumberOfBytesTransferred: IntByReference?, /* [out] */ + bWait: Boolean, /* [in] */ + ): Boolean + + companion object { + val INSTANCE: Kernel32IO = + Native.load("kernel32", Kernel32IO::class.java, W32APIOptions.DEFAULT_OPTIONS) + } +} + +enum class PipeState { + CREATED, + OPEN, + ERROR, +} +class WindowsPipe(val pipeHandle: WinNT.HANDLE?, val name: String) { + var state: PipeState = PipeState.CREATED + + fun safeDisconnect() { + if (pipeHandle != null && pipeHandle != WinBase.INVALID_HANDLE_VALUE) { + Kernel32.INSTANCE.DisconnectNamedPipe(pipeHandle) + } + } +} + +class WindowsNamedPipeBridge( + server: VRServer, + bridgeSettingsKey: String, + bridgeName: String, + private val pipeName: String, + shareableTrackers: List, +) : SteamVRBridge(server, "Named pipe thread", bridgeName, bridgeSettingsKey, shareableTrackers) { + lateinit var pipe: WindowsPipe + private val buffArray = ByteArray(2048) + private val openEvent: WinNT.HANDLE? = k32.CreateEvent(null, false, false, null) + private val readEvent: WinNT.HANDLE? = k32.CreateEvent(null, false, false, null) + private val writeEvent: WinNT.HANDLE? = k32.CreateEvent(null, false, false, null) + private val rxEvent: WinNT.HANDLE? = k32.CreateEvent(null, false, false, null) + private val txEvent: WinNT.HANDLE? = k32.CreateEvent(null, false, false, null) + private val events = arrayOf(rxEvent, txEvent) + private val overlappedOpen = WinBase.OVERLAPPED() + private val overlappedWrite = WinBase.OVERLAPPED() + private val overlappedRead = WinBase.OVERLAPPED() + private val overlappedWait = WinBase.OVERLAPPED() + private val bytesWritten = IntByReference(0) + private val bytesAvailable = IntByReference(0) + private val bytesRead = IntByReference(0) + private var pendingWait = false + + init { + overlappedWait.hEvent = rxEvent + } + + @BridgeThread + override fun run() { + try { + createPipe() + while (true) { + var pipesUpdated = false + if (pipe.state == PipeState.CREATED) { + // Report that our pipe is disconnected right now + reportDisconnected() + tryOpeningPipe(pipe) + } + if (pipe.state == PipeState.OPEN) { + pipesUpdated = updatePipe() + if (pipesUpdated) { + reportConnected() + } + updateMessageQueue() + } + if (pipe.state == PipeState.ERROR) { + resetPipe() + } + if (!pipesUpdated) { + if (pipe.state == PipeState.OPEN) { + waitForData(10) + } else { + try { + Thread.sleep(10) + } catch (_: InterruptedException) { + Thread.currentThread().interrupt() + } + } + } + } + } catch (e: IOException) { + LogManager.severe("[$bridgeName] Exception while running bridge", e) + } + } + + @ThreadSafe + override fun signalSend() { + k32.SetEvent(txEvent) + } + + @BridgeThread + private fun waitForData(timeoutMs: Int) { + if (pipe.state != PipeState.OPEN) return + if (!pendingWait) { + k32.ReadFile(pipe.pipeHandle, null, 0, null, overlappedWait) + pendingWait = true + } + val evIdx: Int = k32.WaitForMultipleObjects(events.size, events, false, timeoutMs) + if (evIdx == 0) { + // events[0] == overlappedWait.hEvent == rxEvent + pendingWait = false + } + } + + @BridgeThread + override fun sendMessageReal(message: ProtobufMessage): Boolean { + if (pipe.state != PipeState.OPEN) return false + try { + var size = message.getSerializedSize() + val os = CodedOutputStream.newInstance(buffArray, 4, size) + message.writeTo(os) + size += 4 + buffArray[0] = (size and 0xFF).toByte() + buffArray[1] = ((size shr 8) and 0xFF).toByte() + buffArray[2] = ((size shr 16) and 0xFF).toByte() + buffArray[3] = ((size shr 24) and 0xFF).toByte() + + overlappedWrite.clear() + overlappedWrite.hEvent = writeEvent + val immediate: Boolean = k32 + .WriteFile(pipe.pipeHandle, buffArray, size, null, overlappedWrite) + val err: Int = k32.GetLastError() + if (!immediate && err != WinError.ERROR_IO_PENDING) { + setPipeError("WriteFile failed: $err") + return false + } + + if (!k32io.GetOverlappedResult(pipe.pipeHandle, overlappedWrite, bytesWritten, true)) { + setPipeError("sendMessageReal/GetOverlappedResult failed: ${k32.GetLastError()}") + return false + } + + if (bytesWritten.value != size) { + setPipeError("Bytes written ${bytesWritten.value}, expected $size") + return false + } + + return true + } catch (e: IOException) { + LogManager.severe("[$bridgeName] Failed to send message", e) + } + return false + } + + @Throws(IOException::class) + private fun updatePipe(): Boolean { + if (pipe.state != PipeState.OPEN) { + return false + } + var readAnything = false + while (k32.PeekNamedPipe(pipe.pipeHandle, buffArray, 4, null, bytesAvailable, null)) { + if (bytesAvailable.value < 4) { + return readAnything // Wait for more data + } + val messageLength = ( + (Byte.toUnsignedInt(buffArray[3]) shl 24) + or (Byte.toUnsignedInt(buffArray[2]) shl 16) + or (Byte.toUnsignedInt(buffArray[1]) shl 8) + or Byte.toUnsignedInt(buffArray[0]) + ) + if (messageLength > 1024) { // Overflow + setPipeError("Pipe overflow. Message length: $messageLength") + return readAnything + } + if (bytesAvailable.value < messageLength) { + return readAnything // Wait for more data + } + + overlappedRead.clear() + overlappedRead.hEvent = readEvent + val immediate: Boolean = k32 + .ReadFile(pipe.pipeHandle, buffArray, messageLength, null, overlappedRead) + val err: Int = k32.GetLastError() + if (!immediate && err != WinError.ERROR_IO_PENDING) { + setPipeError("ReadFile failed: $err") + return readAnything + } + + if (!k32io.GetOverlappedResult(pipe.pipeHandle, overlappedRead, bytesRead, true)) { + setPipeError("updatePipe/GetOverlappedResult failed: ${k32.GetLastError()}") + return readAnything + } + + if (bytesRead.value != messageLength) { + setPipeError("Bytes read ${bytesRead.value}, expected $messageLength") + return readAnything + } + + try { + val message = ProtobufMessage + .parser() + .parseFrom(buffArray, 4, messageLength - 4) + messageReceived(message) + readAnything = true + } catch (e: InvalidProtocolBufferException) { + setPipeError("Failed to parse message: ${e.message}") + e.printStackTrace() + return readAnything + } + } + + val err = k32.GetLastError() + if (err == WinError.ERROR_BROKEN_PIPE) { + setPipeError("Pipe closed") + } else { + setPipeError("Pipe error: $err") + } + return readAnything + } + + private fun setPipeError(message: String) { + pipe.state = PipeState.ERROR + LogManager.severe("[$bridgeName] $message") + } + + private fun resetPipe() { + pipe.safeDisconnect() + pipe.state = PipeState.CREATED + server.queueTask { this.disconnected() } + } + + @Throws(IOException::class) + private fun createPipe() { + try { + val descriptor = WinNT.SECURITY_DESCRIPTOR(64 * 1024) + adv32.InitializeSecurityDescriptor(descriptor, WinNT.SECURITY_DESCRIPTOR_REVISION) + adv32.SetSecurityDescriptorDacl(descriptor, true, null, false) + adv32 + .SetSecurityDescriptorControl( + descriptor, + WinNT.SE_DACL_PROTECTED.toShort(), + WinNT.SE_DACL_PROTECTED.toShort(), + ) + + val attributes = WinBase.SECURITY_ATTRIBUTES() + attributes.lpSecurityDescriptor = descriptor.pointer + attributes.bInheritHandle = false + + pipe = WindowsPipe( + k32 + .CreateNamedPipe( + pipeName, + WinBase.PIPE_ACCESS_DUPLEX or WinNT.FILE_FLAG_OVERLAPPED, // dwOpenMode + WinBase.PIPE_TYPE_BYTE or WinBase.PIPE_READMODE_BYTE or WinBase.PIPE_WAIT, // dwPipeMode + 1, // nMaxInstances, + 1024 * 16, // nOutBufferSize, + 1024 * 16, // nInBufferSize, + 0, // nDefaultTimeOut, + attributes, // lpSecurityAttributes + ), + pipeName, + ) + LogManager.info("[$bridgeName] Pipe ${pipe.name} created") + if (WinBase.INVALID_HANDLE_VALUE == pipe.pipeHandle) { + throw IOException("Can't open $pipeName pipe: ${k32.GetLastError()}") + } + LogManager.info("[$bridgeName] Pipes are created") + } catch (e: IOException) { + pipe.safeDisconnect() + throw e + } + } + + private fun tryOpeningPipe(pipe: WindowsPipe): Boolean { + overlappedOpen.clear() + overlappedOpen.hEvent = openEvent + + val ok = k32.ConnectNamedPipe(pipe.pipeHandle, overlappedOpen) + val err = k32.GetLastError() + if (!ok && err != WinError.ERROR_PIPE_CONNECTED) { + if (err != WinError.ERROR_IO_PENDING) { + setPipeError("ConnectNamedPipe failed: $err") + return false + } + + if (!k32io.GetOverlappedResult(pipe.pipeHandle, overlappedOpen, bytesRead, true)) { + setPipeError("tryOpeningPipe/GetOverlappedResult failed: ${k32.GetLastError()}") + return false + } + } + + pipe.state = PipeState.OPEN + LogManager.info("[$bridgeName] Pipe ${pipe.name} is open") + server.queueTask { this.reconnected() } + return true + } + + override fun isConnected() = pipe.state == PipeState.OPEN + + override fun getBridgeConfigKey() = this.bridgeSettingsKey + + companion object { + private val k32: Kernel32 = Kernel32.INSTANCE + private val k32io: Kernel32IO = Kernel32IO.INSTANCE + private val adv32: Advapi32 = Advapi32.INSTANCE + } +} diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsPipe.java b/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsPipe.java deleted file mode 100644 index 6aabd03ad..000000000 --- a/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsPipe.java +++ /dev/null @@ -1,24 +0,0 @@ -package dev.slimevr.desktop.platform.windows; - -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.platform.win32.WinNT.HANDLE; - - -public class WindowsPipe { - - public final String name; - public final HANDLE pipeHandle; - public PipeState state = PipeState.CREATED; - - public WindowsPipe(HANDLE pipeHandle, String name) { - this.pipeHandle = pipeHandle; - this.name = name; - } - - public static void safeDisconnect(WindowsPipe pipe) { - try { - if (pipe != null && pipe.pipeHandle != null) - Kernel32.INSTANCE.DisconnectNamedPipe(pipe.pipeHandle); - } catch (Exception ignored) {} - } -}