/**************************************************************************** ** ** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt VS Tools. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ using System; using System.Diagnostics; using System.IO; using System.IO.Pipes; using System.Linq; using System.Text; namespace QtVsTools.Test { public class QtVsTestClient : IDisposable { public NamedPipeClientStream Stream { get; } public QtVsTestClient(int vsProcId) { Stream = new NamedPipeClientStream(".", $"QtVSTest_{vsProcId}", PipeDirection.InOut); } public static QtVsTestClient Attach(int? vsProcId = null) { if (vsProcId == null) { var procs = Process.GetProcessesByName("devenv"); var vsProc = procs .Where(p => p.Id != Process.GetCurrentProcess().Id && !p.MainWindowTitle.StartsWith("vstools")) .FirstOrDefault(); if (vsProc == null) throw new InvalidOperationException("VS process not found"); vsProcId = vsProc.Id; } var client = new QtVsTestClient(vsProcId.Value); client.Connect(); return client; } public void Connect() => Stream.Connect(); public void Dispose() => Stream?.Dispose(); public string RunMacro(string macroCode) { if (!Stream.IsConnected) Connect(); var macroData = Encoding.UTF8.GetBytes(macroCode); int macroDataSize = macroData.Length; byte[] sizeData = BitConverter.GetBytes(macroDataSize); Stream.Write(sizeData, 0, sizeof(int)); Stream.Write(macroData, 0, macroData.Length); Stream.Flush(); if (!Stream.IsConnected) return Error("Disconnected"); Stream.WaitForPipeDrain(); if (!Stream.IsConnected) return Error("Disconnected"); for (int i = 0; i < sizeof(int); i++) { int c = Stream.ReadByte(); if (c == -1) return Error("Disconnected"); if (c < Byte.MinValue || c > Byte.MaxValue) return Error("Pipe error"); sizeData[i] = (byte)c; } int replyDataSize = BitConverter.ToInt32(sizeData, 0); byte[] replyData = new byte[replyDataSize]; int bytesRead = 0; while (bytesRead < replyDataSize) { if (!Stream.IsConnected) return Error("Disconnected"); bytesRead += Stream.Read(replyData, bytesRead, replyDataSize - bytesRead); } return Encoding.UTF8.GetString(replyData); } public string RunMacroFile(string macroPath) { return LoadAndRunMacro(macroPath); } public string StoreMacro(string macroName, string macroCode) { if (string.IsNullOrEmpty(macroName)) return Error("Invalid macro name"); return RunMacro($"//# macro {macroName}\r\n{macroCode}"); } public string StoreMacroFile(string macroName, string macroPath) { if (string.IsNullOrEmpty(macroName)) return Error("Invalid macro name"); return LoadAndRunMacro(macroPath, $"//# macro {macroName}"); } string LoadAndRunMacro(string macroPath, string macroHeader = null) { var macroCode = File.ReadAllText(macroPath, Encoding.UTF8); if (string.IsNullOrEmpty(macroCode)) return Error("Macro load failed"); if (!string.IsNullOrEmpty(macroHeader)) return RunMacro($"{macroHeader}\r\n{macroCode}"); else return RunMacro(macroCode); } public const string MacroOk = "(ok)"; public const string MacroWarn = "(warn)"; public const string MacroError = "(error)"; static string Error(string msg) => $"{MacroError}\r\n{msg}"; } }