Skip to content
Open
2 changes: 2 additions & 0 deletions TcBuild.Test/ParserTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ public void Test2()

var MSBuildFrameworkToolsPath = @"C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\";
var FrameworkSDKRoot = @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\";
var libPath = @"..\..\..\..\TcBuild\lib\";

var tools = new Tools(
ilasmPath: Path.Combine(MSBuildFrameworkToolsPath, "ilasm.exe"),
ildasmPath: new DirectoryInfo(FrameworkSDKRoot).GetFiles("ildasm.exe", SearchOption.AllDirectories).OrderByDescending(_ => _.DirectoryName).FirstOrDefault()?.FullName,
rcPath: Path.Combine(libPath, "RC.exe"),
logger
);

Expand Down
1 change: 1 addition & 0 deletions TcBuild.Test/TcBuildTask_Test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public void Test_Run()
MSBuildFrameworkToolsPath = @"C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\",
FrameworkSDKRoot = @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\",
CacheDir = cacheDir.FullName,
LibDirectory = @"..\..\..\..\TcBuild\lib\",
//--
BuildEngine = new FakeBuildEngine(_output)
};
Expand Down
27 changes: 24 additions & 3 deletions TcBuild/Processor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,18 @@ public Task<bool> ExecuteAsync(CancellationToken token = default)

token.ThrowIfCancellationRequested();

// ico resource
FileInfo resFile = new FileInfo(Path.Combine(workDir.FullName, $"{AssemblyFile.Name}.res"));
_tools.TryCreateResFile(AssemblyFile, resFile);

// create: x86
_tools.Assemble(wrapperSource, outFile, false, IsRelease);
_tools.Assemble(wrapperSource, outFile, resFile, false, IsRelease);
_log.LogInfo($"{outFile.FullName}");

token.ThrowIfCancellationRequested();

// create: x64
_tools.Assemble(wrapperSource, outFile64, true, IsRelease);
_tools.Assemble(wrapperSource, outFile64, resFile, true, IsRelease);
_log.LogInfo($"{outFile64.FullName}");

token.ThrowIfCancellationRequested();
Expand All @@ -96,7 +100,8 @@ public Task<bool> ExecuteAsync(CancellationToken token = default)
//.Where(_ => _.Name != "Microsoft.Build.Framework.dll")
//.Where(_ => _.Name != "Microsoft.Build.Utilities.Core.dll")
//.Where(_ => _.Name != "System.Collections.Immutable.dll")
)
),
GetSatelliteAssemblyFiles()
);
if (!success) {
_log.LogWarning("ZIP Archiver is not found - Installation Archive is not created.");
Expand All @@ -114,6 +119,22 @@ public Task<bool> ExecuteAsync(CancellationToken token = default)
}
}

private IEnumerable<FileInfo> GetSatelliteAssemblyFiles()
{
IEnumerable<FileInfo> allAssemblyFiles = new[] { AssemblyFile }.Concat(ReferenceFiles).Where(f => f.Extension.Equals(".dll", StringComparison.InvariantCultureIgnoreCase));
foreach (FileInfo assemblyFile in allAssemblyFiles)
{
foreach (FileInfo satelliteAssemblyFile in GetSatelliteAssemblyFiles(assemblyFile))
{
yield return satelliteAssemblyFile;
}
}
}

private IEnumerable<FileInfo> GetSatelliteAssemblyFiles(FileInfo assemblyFile)
{
return assemblyFile.Directory.EnumerateFiles($"{Path.GetFileNameWithoutExtension(assemblyFile.Name)}.resources{assemblyFile.Extension}", SearchOption.AllDirectories);
}

private void CreatePluginstFile(FileInfo iniFile, FileInfo outFile, PluginType pluginType)
{
Expand Down
9 changes: 9 additions & 0 deletions TcBuild/TcBuild.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@
<Pack>true</Pack>
<PackagePath>build</PackagePath>
</Content>
<!-- lib target -->
<Content Include=".\lib\rcdll.dll">
<Pack>true</Pack>
<PackagePath>lib</PackagePath>
</Content>
<Content Include=".\lib\RC.Exe">
<Pack>true</Pack>
<PackagePath>lib</PackagePath>
</Content>
</ItemGroup>

<PropertyGroup>
Expand Down
3 changes: 3 additions & 0 deletions TcBuild/TcBuildTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public class TcBuildTask : AppDomainIsolatedTask, ICancelableTask, ITask {
[Required]
public string CacheDir { get; set; }

internal string LibDirectory { get; set; }

//[Output]
//public string TargetExt { get; private set; }

Expand All @@ -47,6 +49,7 @@ public override bool Execute()
var tools = new Tools(
ilasmPath: Path.Combine(MSBuildFrameworkToolsPath, "ilasm.exe"),
ildasmPath: new DirectoryInfo(FrameworkSDKRoot).GetFiles("ildasm.exe", SearchOption.AllDirectories).OrderByDescending(_ => _.DirectoryName).FirstOrDefault()?.FullName,
rcPath: Path.Combine(LibDirectory ?? new FileInfo(TcPluginBase).Directory.Parent.FullName, "RC.Exe"),
_log
);

Expand Down
Binary file added TcBuild/lib/RC.Exe
Binary file not shown.
Binary file added TcBuild/lib/rcdll.dll
Binary file not shown.
115 changes: 115 additions & 0 deletions TcBuild/src/IconExtractor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;

namespace TcBuild
{
internal static class IconExtractor
{
[UnmanagedFunctionPointer(CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
private delegate bool ENUMRESNAMEPROC(IntPtr hModule, IntPtr lpszType, IntPtr lpszName, IntPtr lParam);

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FreeLibrary(IntPtr hModule);

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LockResource(IntPtr hResData);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
private static extern bool EnumResourceNames(IntPtr hModule, IntPtr lpszType, ENUMRESNAMEPROC lpEnumFunc, IntPtr lParam);


private const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
private readonly static IntPtr RT_ICON = (IntPtr)3;
private readonly static IntPtr RT_GROUP_ICON = (IntPtr)14;

public static bool ExtractIconFromExecutable(FileInfo sourceFile, FileInfo targetFile)
{
IntPtr hModule = LoadLibraryEx(sourceFile.FullName, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE);
if (hModule == IntPtr.Zero)
throw new FileNotFoundException($"Library {sourceFile.Name} not found.", sourceFile.FullName);

ENUMRESNAMEPROC callback = (h, t, name, l) =>
{
var dir = GetDataFromResource(hModule, RT_GROUP_ICON, name);

// Calculate the size of an entire .icon file.

int count = BitConverter.ToUInt16(dir, 4); // GRPICONDIR.idCount
int len = 6 + 16 * count; // sizeof(ICONDIR) + sizeof(ICONDIRENTRY) * count
for (int i = 0; i < count; ++i)
len += BitConverter.ToInt32(dir, 6 + 14 * i + 8); // GRPICONDIRENTRY.dwBytesInRes

using (FileStream targetStream = targetFile.Create())
using (var dst = new BinaryWriter(targetStream))
{
// Copy GRPICONDIR to ICONDIR.

dst.Write(dir, 0, 6);

int picOffset = 6 + 16 * count; // sizeof(ICONDIR) + sizeof(ICONDIRENTRY) * count

for (int i = 0; i < count; ++i)
{
// Load the picture.

ushort id = BitConverter.ToUInt16(dir, 6 + 14 * i + 12); // GRPICONDIRENTRY.nID
var pic = GetDataFromResource(hModule, RT_ICON, (IntPtr)id);

// Copy GRPICONDIRENTRY to ICONDIRENTRY.

dst.Seek(6 + 16 * i, 0);

dst.Write(dir, 6 + 14 * i, 8); // First 8bytes are identical.
dst.Write(pic.Length); // ICONDIRENTRY.dwBytesInRes
dst.Write(picOffset); // ICONDIRENTRY.dwImageOffset

// Copy a picture.

dst.Seek(picOffset, 0);
dst.Write(pic, 0, pic.Length);

picOffset += pic.Length;
}
}
return true;
};
bool ok = EnumResourceNames(hModule, RT_GROUP_ICON, callback, IntPtr.Zero);
FreeLibrary(hModule);
return ok;
}
private static byte[] GetDataFromResource(IntPtr hModule, IntPtr type, IntPtr name)
{
// Load the binary data from the specified resource.

IntPtr hResInfo = FindResource(hModule, name, type);

IntPtr hResData = LoadResource(hModule, hResInfo);

IntPtr pResData = LockResource(hResData);

uint size = SizeofResource(hModule, hResInfo);

byte[] buf = new byte[size];
Marshal.Copy(pResData, buf, 0, buf.Length);

return buf;
}

}
}
46 changes: 37 additions & 9 deletions TcBuild/src/Tools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ namespace TcBuild {
public class Tools {
private readonly string _ilasmPath;
private readonly string _ildasmPath;
private readonly string _rcPath;
private readonly ILogger _log;

public Tools(string ilasmPath, string ildasmPath, ILogger log)
public Tools(string ilasmPath, string ildasmPath, string rcPath, ILogger log)
{
_log = log;
_ilasmPath = ilasmPath;
_ildasmPath = ildasmPath;
_rcPath = rcPath;

// MSBuildFrameworkToolsPath + ilasm.exe
// FrameworkSDKRoot + ildasm.exe
Expand All @@ -35,8 +37,14 @@ public Tools(string ilasmPath, string ildasmPath, ILogger log)
throw new Exception("Cannot locate IL Disassembler ildasm.exe!");
}

_log.LogInfo($"IL Disassembler: '{_ildasmPath}'");
_log.LogInfo($"IL Assembler : '{_ilasmPath}'");
if (_rcPath != null && !File.Exists(_rcPath)) {
_log.LogError(_rcPath);
throw new Exception("Cannot locate Resource compiler rc.exe!");
}

_log.LogInfo($"IL Disassembler : '{_ildasmPath}'");
_log.LogInfo($"IL Assembler : '{_ilasmPath}'");
_log.LogInfo($"Resource compiler : '{_rcPath}'");
}


Expand All @@ -62,7 +70,7 @@ public void Disassemble(FileInfo assemblyFile, FileInfo sourcePath, bool emitDeb
}


public void Assemble(FileInfo inFile, FileInfo outFile, bool x64, bool release = false)
public void Assemble(FileInfo inFile, FileInfo outFile, FileInfo resFile, bool x64, bool release = false)
{
outFile.Delete();

Expand All @@ -72,9 +80,9 @@ public void Assemble(FileInfo inFile, FileInfo outFile, bool x64, bool release =
args.Add($"/out:{Quote(outFile.FullName)}");
args.Add($"/dll");

//if (resFile.Exists()) {
// args.Add($"/res:{Quote(resFile.FullName)}");
//}
if (resFile?.Exists == true) {
args.Add($"/res:{Quote(resFile.FullName)}");
}

if (x64) {
args.Add($"/x64");
Expand All @@ -92,7 +100,7 @@ public void Assemble(FileInfo inFile, FileInfo outFile, bool x64, bool release =
}


public bool CreateZip(FileInfo zipFile, IEnumerable<FileInfo> files)
public bool CreateZip(FileInfo zipFile, IEnumerable<FileInfo> files, IEnumerable<FileInfo> satelliteAssemblyFiles)
{
try {
zipFile.Delete();
Expand All @@ -104,8 +112,13 @@ public bool CreateZip(FileInfo zipFile, IEnumerable<FileInfo> files)
fileContents.CopyTo(entry);
}
}
foreach (var file in satelliteAssemblyFiles.Where(_ => _.Exists)) {
using (var entry = zip.CreateEntry($"{file.Directory.Name}/{file.Name}").Open())
using (var fileContents = file.OpenRead()) {
fileContents.CopyTo(entry);
}
}
}

return true;
}
catch (Exception e) {
Expand All @@ -114,6 +127,21 @@ public bool CreateZip(FileInfo zipFile, IEnumerable<FileInfo> files)
}
}

public bool TryCreateResFile(FileInfo assemblyFile, FileInfo resFile)
{
if (_rcPath == null)
throw new InvalidOperationException("RC.exe path not specified.");
FileInfo icoFile = new FileInfo(Path.ChangeExtension(resFile.FullName, ".ico"));
FileInfo rcFile = new FileInfo(Path.ChangeExtension(resFile.FullName, ".rc"));
if (IconExtractor.ExtractIconFromExecutable(assemblyFile, icoFile))
{
File.WriteAllText(rcFile.FullName, $"1 ICON \"{icoFile.Name}\"");
if (!TryRun(_rcPath, Quote(rcFile.FullName)))
throw new Exception($"RC.exe has failed create resource!\r\n{_rcPath} \"{rcFile.FullName}\"");
return true;
}
return false;
}

private static string Quote(string arg)
{
Expand Down
3 changes: 2 additions & 1 deletion WfxWrapper/FsWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -522,10 +522,11 @@ private static ExecResult ExecuteFileInternal(IntPtr mainWin, RemotePath remoteN

var resStr = result.Type.ToString();
if (result.Type == ExecResult.ExecEnum.SymLink && result.SymlinkTarget.HasValue) {
resStr += " (" + result.SymlinkTarget + ")";
resStr += " (" + result.SymlinkTarget.Path + ")";
}

TraceCall(TraceLevel.Warning, resStr);
return result;
}
catch (Exception ex) {
ProcessException(ex);
Expand Down