forked from Ivasoft/DllExports
Command-line interface
This commit is contained in:
@@ -26,6 +26,9 @@ steps:
|
||||
# Re-pack
|
||||
- rm DllExports.MSBuild/bin/Release/IvDllExports.0.1.1.nupkg
|
||||
- (cd DllExports.MSBuild/bin/Release/nupkg; zip -r ../IvDllExports.0.1.1.nupkg ./)
|
||||
# Cli
|
||||
- dotnet build --configuration Release DllExports.Cli/DllExports.Cli.csproj
|
||||
- dotnet pack --configuration Release DllExports.Cli/DllExports.Cli.csproj
|
||||
|
||||
- name: release
|
||||
image: mcr.microsoft.com/dotnet/sdk:8.0
|
||||
@@ -35,5 +38,6 @@ steps:
|
||||
nuget_uri: https://git.ivasoft.cz/api/packages/Ivasoft/nuget/index.json
|
||||
commands:
|
||||
- dotnet nuget push DllExports.MSBuild/bin/Release/IvDllExports.0.1.1.nupkg --api-key $$PLUGIN_NUGET_APIKEY --source $$PLUGIN_NUGET_URI --skip-duplicate
|
||||
- dotnet nuget push DllExports.Cli/bin/Release/IvDllExports-Cli.0.1.1.nupkg --api-key $$PLUGIN_NUGET_APIKEY --source $$PLUGIN_NUGET_URI --skip-duplicate
|
||||
when:
|
||||
event: tag
|
||||
59
DllExports.Cli/DllExports.Cli.csproj
Normal file
59
DllExports.Cli/DllExports.Cli.csproj
Normal file
@@ -0,0 +1,59 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\Version.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472;net5.0</TargetFrameworks>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
||||
<Title>IvDllExports-Cli</Title>
|
||||
<PackageId>IvDllExports-Cli</PackageId>
|
||||
<Authors>lordmilko</Authors>
|
||||
<Description>Unmanaged Exports for legacy/SDK style projects</Description>
|
||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||
<Copyright>(c) 2023 lordmilko. All rights reserved.</Copyright>
|
||||
|
||||
<PackageReleaseNotes>DllExports provides unmanaged exports for both legacy and SDK style projects.
|
||||
|
||||
Unlike other libraries that rely on a wacky series of external dependencies, DllExports has everything it needs to do its job built in.
|
||||
|
||||
DllExports is entirely driven by its MSBuild task, and provides a number of knobs you can adjust to customize the resulting assemblies, including converting AnyCPU assemblies into both x86 and x64 outputs.
|
||||
|
||||
In order to be able to debug your exports in Visual Studio you must be targeting .NET Framework and be using Visual Studio 2019 or newer. .NET Standard exports work but you can't debug them. .NET Core applications can't truly have unmanaged exports as you can't use mscoree to load their runtime. Consider using a library such as DNNE for proper .NET Core support.</PackageReleaseNotes>
|
||||
<OutputType>EXE</OutputType>
|
||||
<RollForward>Major</RollForward>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\LICENSE" Pack="true" PackagePath="" Visible="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="dnlib" Version="3.6.0" IncludeAssets="all" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\DllExports.MSBuild\IsolatedTaskRunner.cs" />
|
||||
<Compile Include="..\DllExports.MSBuild\NetCoreAssemblyLoadContext.cs" />
|
||||
<Compile Include="..\DllExports.MSBuild\NetFrameworkAssemblyLoadContext.cs" />
|
||||
<Compile Include="..\DllExports.MSBuild\RemoteHelper.cs" />
|
||||
|
||||
<ProjectReference Include="..\DllExports\DllExports.csproj" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Copy dependencies to output dir -->
|
||||
<PropertyGroup>
|
||||
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="BuildOnlySettings;ResolveReferences">
|
||||
<ItemGroup>
|
||||
<_ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Add file to package with consideration of sub folder. If empty, the root folder is chosen. -->
|
||||
<BuildOutputInPackage Include="@(_ReferenceCopyLocalPaths)" TargetPath="%(_ReferenceCopyLocalPaths.DestinationSubDirectory)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
209
DllExports.Cli/Program.cs
Normal file
209
DllExports.Cli/Program.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using DllExports;
|
||||
using DllExports.MSBuild;
|
||||
|
||||
// Based on GenerateDllExports.cs
|
||||
|
||||
class A
|
||||
{
|
||||
private static ExportOptions options = new ExportOptions();
|
||||
|
||||
enum MessageImportance {
|
||||
High,
|
||||
Normal
|
||||
}
|
||||
static class Log {
|
||||
internal static void LogMessage(MessageImportance level, string msg) {
|
||||
LogMessage(msg);
|
||||
}
|
||||
internal static void LogMessage(string msg) {
|
||||
Console.Out.WriteLine(msg);
|
||||
}
|
||||
internal static void LogError(string msg) {
|
||||
Console.Error.WriteLine(msg);
|
||||
}
|
||||
internal static void LogErrorFromException(Exception ex) {
|
||||
LogError(ex.GetType().Name + ": " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
options.Enabled = true;
|
||||
List<string> architectures = new List<string>();
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
string arg = args[i];
|
||||
if (i + 1 < args.Length && (arg.StartsWith("-") || arg.StartsWith("/")))
|
||||
{
|
||||
switch (arg.ToLowerInvariant().Substring(1))
|
||||
{
|
||||
case "inputfile":
|
||||
options.InputFile = args[++i];
|
||||
break;
|
||||
case "outputfile":
|
||||
options.OutputFile = args[++i];
|
||||
break;
|
||||
case "architecture":
|
||||
architectures.Add(args[++i]);
|
||||
break;
|
||||
case "architecturenameformat":
|
||||
options.ArchitectureNameFormat = args[++i];
|
||||
break;
|
||||
case "removeinputfile":
|
||||
if (!bool.TryParse(args[++i], out bool a))
|
||||
{
|
||||
Console.Error.WriteLine($"Expected boolean 'true' or 'false' for option 'RemoveInputFile'.");
|
||||
return 3;
|
||||
}
|
||||
else
|
||||
options.RemoveInputFile = a;
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.Error.WriteLine($"Unknown option '{arg}'.");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error.WriteLine($"Unexpected command-line argument '{arg}'.");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
options.Architectures = architectures.ToArray();
|
||||
|
||||
bool isOk = Execute(options);
|
||||
return isOk ? 0 : 1;
|
||||
}
|
||||
|
||||
public static bool Enabled
|
||||
{
|
||||
get => options.Enabled;
|
||||
set => options.Enabled = value;
|
||||
}
|
||||
|
||||
public static string InputFile
|
||||
{
|
||||
get => options.InputFile;
|
||||
set => options.InputFile = value;
|
||||
}
|
||||
|
||||
public static string OutputFile
|
||||
{
|
||||
get => options.OutputFile;
|
||||
set => options.OutputFile = value;
|
||||
}
|
||||
|
||||
public static string[] Architectures
|
||||
{
|
||||
get => options.Architectures;
|
||||
set => options.Architectures = value;
|
||||
}
|
||||
|
||||
public static string ArchitectureNameFormat
|
||||
{
|
||||
get => options.ArchitectureNameFormat;
|
||||
set => options.ArchitectureNameFormat = value;
|
||||
}
|
||||
|
||||
public static bool RemoveInputFile
|
||||
{
|
||||
get => options.RemoveInputFile;
|
||||
set => options.RemoveInputFile = value;
|
||||
}
|
||||
|
||||
public static bool Execute(ExportOptions options)
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
Log.LogMessage($"DllExports{nameof(Enabled)} was false. Nothing to do");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(InputFile))
|
||||
{
|
||||
Log.LogError($"DllExports{nameof(InputFile)} must be specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.LogMessage(MessageImportance.Normal, $"Processing file '{InputFile}'");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(OutputFile))
|
||||
{
|
||||
Log.LogError($"DllExports{nameof(OutputFile)} must be specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.Architectures != null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.ArchitectureNameFormat))
|
||||
{
|
||||
Log.LogError($"DllExports{nameof(ArchitectureNameFormat)} must be specified when DllExports{nameof(Architectures)} is specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
var validArchs = new[]
|
||||
{
|
||||
"I386",
|
||||
"AMD64"
|
||||
};
|
||||
|
||||
foreach (var arch in options.Architectures)
|
||||
{
|
||||
if (!validArchs.Contains(arch, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
Log.LogError($"Invalid architecture '{arch}' specified. Valid architectures: {string.Join(", ", validArchs)}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var outputs = options.CalculateOutputFiles();
|
||||
|
||||
if (options.RemoveInputFile && outputs.Any(o => StringComparer.OrdinalIgnoreCase.Equals(o.Path, options.InputFile)))
|
||||
{
|
||||
Log.LogError($"Cannot set option DllExports{nameof(RemoveInputFile)} to true when inputs and outputs are the same file");
|
||||
return false;
|
||||
}
|
||||
|
||||
var taskRunner = new IsolatedTaskRunner();
|
||||
try
|
||||
{
|
||||
taskRunner.Execute(options);
|
||||
}
|
||||
catch (TargetInvocationException ex)
|
||||
{
|
||||
Log.LogErrorFromException(ex.InnerException);
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.LogErrorFromException(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.RemoveInputFile)
|
||||
File.Delete(options.InputFile);
|
||||
|
||||
var directory = Path.GetDirectoryName(options.InputFile);
|
||||
var dllExportsDll = Path.Combine(directory, "DllExports.dll");
|
||||
|
||||
if (File.Exists(dllExportsDll))
|
||||
File.Delete(dllExportsDll);
|
||||
|
||||
Log.LogMessage(MessageImportance.High, string.Empty);
|
||||
|
||||
foreach (var output in outputs)
|
||||
Log.LogMessage(MessageImportance.High, $"DllExports ({output.Name}) -> {output.Path}");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DllExports", "DllExports\Dl
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DllExports.MSBuild", "DllExports.MSBuild\DllExports.MSBuild.csproj", "{0F2800DF-65E6-47F8-949F-E401881D5970}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DllExports.Cli", "DllExports.Cli\DllExports.Cli.csproj", "{574001C6-8F9D-11EF-9F40-77C5A17AB591}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DllExports.Tests", "DllExports.Tests\DllExports.Tests.csproj", "{D8920F8A-90C4-4D1E-9750-8ED1D79086C9}"
|
||||
EndProject
|
||||
Global
|
||||
@@ -27,6 +29,10 @@ Global
|
||||
{D8920F8A-90C4-4D1E-9750-8ED1D79086C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D8920F8A-90C4-4D1E-9750-8ED1D79086C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D8920F8A-90C4-4D1E-9750-8ED1D79086C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{574001C6-8F9D-11EF-9F40-77C5A17AB591}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{574001C6-8F9D-11EF-9F40-77C5A17AB591}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{574001C6-8F9D-11EF-9F40-77C5A17AB591}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{574001C6-8F9D-11EF-9F40-77C5A17AB591}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -10,6 +10,7 @@ using dnlib.PE;
|
||||
using CallingConvention = System.Runtime.InteropServices.CallingConvention;
|
||||
|
||||
[assembly: InternalsVisibleTo("DllExports.MSBuild")]
|
||||
[assembly: InternalsVisibleTo("DllExports.Cli")]
|
||||
[assembly: InternalsVisibleTo("DllExports.Tests")]
|
||||
|
||||
namespace DllExports
|
||||
|
||||
Reference in New Issue
Block a user