diff --git a/.github/workflows/code-style.yaml b/.github/workflows/code-style.yaml
index b266cae..9e64740 100644
--- a/.github/workflows/code-style.yaml
+++ b/.github/workflows/code-style.yaml
@@ -16,15 +16,16 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
- global-json-file: global.json
+ global-json-file: "./global.json"
- name: Restore dependencies
run: dotnet restore
- name: Check dotnet version
run: dotnet --version
- continue-on-error: false
+
+ - name: Check code format (editorconfig)
+ run: dotnet run --project .\test\test.csproj
- name: Check code format (editorconfig)
run: dotnet format --verify-no-changes
- continue-on-error: false
diff --git a/README.md b/README.md
index 4a26ac0..a75c40b 100644
--- a/README.md
+++ b/README.md
@@ -4,20 +4,20 @@
[![Nuget:Lib](https://img.shields.io/nuget/v/dSPACE.Runtime.InteropServices?label=dSPACE.Runtime.InteropServices&style=flat)](https://www.nuget.org/packages/dSPACE.Runtime.InteropServices/)
[![Nuget:LibBuildTask](https://img.shields.io/nuget/v/dSPACE.Runtime.InteropServices?label=dSPACE.Runtime.InteropServices.BuildTasks&style=flat)](https://www.nuget.org/packages/dSPACE.Runtime.InteropServices.BuildTasks/)
-[![Release](https://img.shields.io/github/v/release/dspace-group/dscom?label=release)](https://github.com/dspace-group/dscom/releases)
-![License](https://img.shields.io/github/license/dspace-group/dscom)
-[![dSPACE](https://img.shields.io/badge/-OpenSource%20powered%20by%20dSPACE-blue)](https://www.dspace.com/)
-
[![Unit Tests](https://github.com/dspace-group/dscom/actions/workflows/unit-test.yaml/badge.svg)](https://github.com/dspace-group/dscom/actions/workflows/unit-test.yaml)
[![Example Tests](https://github.com/dspace-group/dscom/actions/workflows/example-test.yaml/badge.svg)](https://github.com/dspace-group/dscom/actions/workflows/example-test.yaml)
[![Code Style Check](https://github.com/dspace-group/dscom/actions/workflows/code-style.yaml/badge.svg)](https://github.com/dspace-group/dscom/actions/workflows/code-style.yaml)
The command line client `dscom` is a replacement for `tlbexp.exe` and creates and registers TLBs from .NET assemblies.
-The `dSPACE.Runtime.InteropServices` library contains various classes and methods for COM.
-It can be used in `net5+` or in `net48` projects. With the library you can register assemblies and classes for COM and programmatically generate TLBs at runtime.
+With the library `dSPACE.Runtime.InteropServices` you can, among other things, register assemblies and classes for COM and programmatically generate TLBs at runtime.
The `dSPACE.Runtime.InteropServices.BuildTasks` library provides build tasks which can be used to automatically generate TLBs at compile time.
-> This is an unstable prerelease. Anything may change at any time!
+Example:
+
+```pwsh
+PS C:\> dotnet tool install --global dscom
+PS C:\> dscom tlbexport myassembly.dll
+```
- [dSPACE COM tools](#dspace-com-tools)
- [Introducing](#introducing)
@@ -271,17 +271,17 @@ This way the build stops, if the type library is not exported.
The build task can be parameterized with the following [properties](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-properties?view=vs-2022):
-| **Name** | **Description** |
-| ---------------------------------------------- | ---------------------------------------------------------------------------------------------- |
-| _DsComTlbExt | Extension of the resulting type library.
Default Value: `.tlb` |
-| _DsComForceToolUsage | Use DsCom Exe files to create the TLB
Default value: `false` | false |
-| DsComTypeLibraryUniqueId | Overwrite the library UUID
Default Value: Empty Guid |
-| DsComOverideLibraryName | Overwrite the IDL name of the library.
Default Value: Empty string |
-| DsComRegisterTypeLibrariesAfterBuild | Use regasm call after the build to register type library after the build
Default value: `false` |
-| DsComTlbExportAutoAddReferences | Add referenced assemblies automatically to type libraries
Default value: `true` |
-| DsComTlbExportIncludeReferencesWithoutHintPath | If a `Reference` assembly does not provide a `HintPath` Metadata, the item spec shall be task.
Default value: `false` |
-| _DsComExportTypeLibraryTargetFile | Path to the resulting file.
Default value: `$(TargetDir)\$(TargetName)$(_DsComTlbExt)` * |
-| _DsComExportTypeLibraryAssemblyFile | Path to the source assembly file.
Default value: `$(TargetPath)` * |
+| **Name** | **Description** |
+| ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
+| _DsComTlbExt | Extension of the resulting type library.
Default Value: `.tlb` |
+| _DsComForceToolUsage | Use DsCom Exe files to create the TLB
Default value: `false` | false |
+| DsComTypeLibraryUniqueId | Overwrite the library UUID
Default Value: Empty Guid |
+| DsComOverideLibraryName | Overwrite the IDL name of the library.
Default Value: Empty string |
+| DsComRegisterTypeLibrariesAfterBuild | Use regasm call after the build to register type library after the build
Default value: `false` |
+| DsComTlbExportAutoAddReferences | Add referenced assemblies automatically to type libraries
Default value: `true` |
+| DsComTlbExportIncludeReferencesWithoutHintPath | If a `Reference` assembly does not provide a `HintPath` Metadata, the item spec shall be task.
Default value: `false` |
+| _DsComExportTypeLibraryTargetFile | Path to the resulting file.
Default value: `$(TargetDir)\$(TargetName)$(_DsComTlbExt)` * |
+| _DsComExportTypeLibraryAssemblyFile | Path to the source assembly file.
Default value: `$(TargetPath)` * |
*) This value cannot be overridden.
diff --git a/global.json b/global.json
index 5eba956..534c5d8 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "6.0.406",
+ "version": "6.0.408",
"rollForward": "latestPatch"
}
}
\ No newline at end of file
diff --git a/test/ProcessRunner.cs b/test/ProcessRunner.cs
new file mode 100644
index 0000000..73ce174
--- /dev/null
+++ b/test/ProcessRunner.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.CodeAnalysis.Tools.Utilities
+{
+ public readonly struct ProcessResult
+ {
+ public Process Process { get; }
+ public int ExitCode { get; }
+ public ReadOnlyCollection OutputLines { get; }
+ public ReadOnlyCollection ErrorLines { get; }
+
+ public ProcessResult(Process process, int exitCode, ReadOnlyCollection outputLines, ReadOnlyCollection errorLines)
+ {
+ Process = process;
+ ExitCode = exitCode;
+ OutputLines = outputLines;
+ ErrorLines = errorLines;
+ }
+ }
+
+ public readonly struct ProcessInfo
+ {
+ public Process Process { get; }
+ public ProcessStartInfo StartInfo { get; }
+ public Task Result { get; }
+
+ public int Id => Process.Id;
+
+ public ProcessInfo(Process process, ProcessStartInfo startInfo, Task result)
+ {
+ Process = process;
+ StartInfo = startInfo;
+ Result = result;
+ }
+ }
+
+ public static class ProcessRunner
+ {
+ public static void OpenFile(string file)
+ {
+ if (File.Exists(file))
+ {
+ Process.Start(file);
+ }
+ }
+
+ public static ProcessInfo CreateProcess(
+ string executable,
+ string arguments,
+ bool lowPriority = false,
+ string? workingDirectory = null,
+ bool captureOutput = false,
+ bool displayWindow = true,
+ Dictionary? environmentVariables = null,
+ Action? onProcessStartHandler = null,
+ CancellationToken cancellationToken = default) =>
+ CreateProcess(
+ CreateProcessStartInfo(executable, arguments, workingDirectory, captureOutput, displayWindow, environmentVariables),
+ lowPriority: lowPriority,
+ onProcessStartHandler: onProcessStartHandler,
+ cancellationToken: cancellationToken);
+
+ public static ProcessInfo CreateProcess(
+ ProcessStartInfo processStartInfo,
+ bool lowPriority = false,
+ Action? onProcessStartHandler = null,
+ CancellationToken cancellationToken = default)
+ {
+ var errorLines = new List();
+ var outputLines = new List();
+ var process = new Process();
+ var tcs = new TaskCompletionSource();
+
+ process.EnableRaisingEvents = true;
+ process.StartInfo = processStartInfo;
+
+ process.OutputDataReceived += (s, e) =>
+ {
+ if (e.Data != null)
+ {
+ outputLines.Add(e.Data);
+ }
+ };
+
+ process.ErrorDataReceived += (s, e) =>
+ {
+ if (e.Data != null)
+ {
+ errorLines.Add(e.Data);
+ }
+ };
+
+ process.Exited += (s, e) =>
+ {
+ // We must call WaitForExit to make sure we've received all OutputDataReceived/ErrorDataReceived calls
+ // or else we'll be returning a list we're still modifying. For paranoia, we'll start a task here rather
+ // than enter right back into the Process type and start a wait which isn't guaranteed to be safe.
+ Task.Run(() =>
+ {
+ process.WaitForExit();
+ var result = new ProcessResult(
+ process,
+ process.ExitCode,
+ new ReadOnlyCollection(outputLines),
+ new ReadOnlyCollection(errorLines));
+ tcs.TrySetResult(result);
+ });
+ };
+
+ _ = cancellationToken.Register(() =>
+ {
+ if (tcs.TrySetCanceled())
+ {
+ // If the underlying process is still running, we should kill it
+ if (!process.HasExited)
+ {
+ try
+ {
+ process.Kill();
+ }
+ catch (InvalidOperationException)
+ {
+ // Ignore, since the process is already dead
+ }
+ }
+ }
+ });
+
+ process.Start();
+ onProcessStartHandler?.Invoke(process);
+
+ if (lowPriority)
+ {
+ process.PriorityClass = ProcessPriorityClass.BelowNormal;
+ }
+
+ if (processStartInfo.RedirectStandardOutput)
+ {
+ process.BeginOutputReadLine();
+ }
+
+ if (processStartInfo.RedirectStandardError)
+ {
+ process.BeginErrorReadLine();
+ }
+
+ return new ProcessInfo(process, processStartInfo, tcs.Task);
+ }
+
+ public static ProcessStartInfo CreateProcessStartInfo(
+ string executable,
+ string arguments,
+ string? workingDirectory = null,
+ bool captureOutput = false,
+ bool displayWindow = true,
+ Dictionary? environmentVariables = null)
+ {
+ var processStartInfo = new ProcessStartInfo(executable, arguments);
+
+ if (!string.IsNullOrEmpty(workingDirectory))
+ {
+ processStartInfo.WorkingDirectory = workingDirectory;
+ }
+
+ if (environmentVariables != null)
+ {
+ foreach (var pair in environmentVariables)
+ {
+ processStartInfo.EnvironmentVariables[pair.Key] = pair.Value;
+ }
+ }
+
+ if (captureOutput)
+ {
+ processStartInfo.UseShellExecute = false;
+ processStartInfo.RedirectStandardOutput = true;
+ processStartInfo.RedirectStandardError = true;
+ }
+ else
+ {
+ processStartInfo.CreateNoWindow = !displayWindow;
+ processStartInfo.UseShellExecute = displayWindow;
+ }
+
+ return processStartInfo;
+ }
+ }
+}
diff --git a/test/Program.cs b/test/Program.cs
new file mode 100644
index 0000000..7f3a9b3
--- /dev/null
+++ b/test/Program.cs
@@ -0,0 +1,22 @@
+using Microsoft.CodeAnalysis.Tools.Utilities;
+
+internal class Program
+{
+ private static int Main(string[] args)
+ {
+ TryGetDotNetCliVersion(out string a);
+ Console.WriteLine(a);
+
+ return 0;
+ }
+
+
+ internal static bool TryGetDotNetCliVersion(out string dotnetVersion)
+ {
+ var processInfo = ProcessRunner.CreateProcess("dotnet", "--version", captureOutput: true, displayWindow: false);
+ var versionResult = processInfo.Result.GetAwaiter().GetResult();
+
+ dotnetVersion = versionResult.OutputLines[0].Trim();
+ return true;
+ }
+}
diff --git a/test/test.csproj b/test/test.csproj
new file mode 100644
index 0000000..74abf5c
--- /dev/null
+++ b/test/test.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+