forked from winsw/winsw
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
352 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#pragma warning disable SA1310 // Field names should not contain underscore | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace WinSW.Native | ||
{ | ||
internal static class ResourceApis | ||
{ | ||
internal static readonly IntPtr VS_VERSION_INFO = new IntPtr(1); | ||
|
||
internal static readonly IntPtr RT_VERSION = new IntPtr(16); | ||
|
||
[DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode)] | ||
internal static extern IntPtr BeginUpdateResourceW(string fileName, bool deleteExistingResources); | ||
|
||
[DllImport(Libraries.Kernel32, SetLastError = true)] | ||
internal static extern bool EndUpdateResourceW(IntPtr update, bool discard); | ||
|
||
[DllImport(Libraries.Kernel32, SetLastError = true)] | ||
internal static extern IntPtr FindResourceW(IntPtr module, IntPtr name, IntPtr type); | ||
|
||
[DllImport(Libraries.Kernel32)] | ||
internal static extern bool FreeLibrary(IntPtr libModule); | ||
|
||
[DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode)] | ||
internal static extern IntPtr LoadLibraryW(string libFileName); | ||
|
||
[DllImport(Libraries.Kernel32, SetLastError = true)] | ||
internal static extern IntPtr LoadResource(IntPtr module, IntPtr resInfo); | ||
|
||
[DllImport(Libraries.Kernel32)] | ||
internal static extern IntPtr LockResource(IntPtr resData); | ||
|
||
[DllImport(Libraries.Kernel32, SetLastError = true)] | ||
internal static extern bool UpdateResourceW(IntPtr update, IntPtr type, IntPtr name, ushort language, IntPtr data, int size); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Runtime.InteropServices; | ||
using static WinSW.Native.ResourceApis; | ||
|
||
namespace WinSW.Native | ||
{ | ||
internal static class Resources | ||
{ | ||
/// <exception cref="CommandException" /> | ||
internal static unsafe bool UpdateCompanyName(string path, string outputPath, string companyName) | ||
{ | ||
IntPtr module = LoadLibraryW(path); | ||
try | ||
{ | ||
IntPtr verInfo = FindResourceW(module, VS_VERSION_INFO, RT_VERSION); | ||
if (verInfo == IntPtr.Zero) | ||
{ | ||
Exit(); | ||
} | ||
|
||
IntPtr resData = LoadResource(module, verInfo); | ||
if (resData == IntPtr.Zero) | ||
{ | ||
Exit(); | ||
} | ||
|
||
IntPtr resAddr = LockResource(resData); | ||
|
||
IntPtr address = resAddr; | ||
int offset = 0; | ||
|
||
short length = ((short*)address)[0]; | ||
short valueLength = ReadHeaderAndAdvance(); | ||
string key = ReadKeyAndAdvance(); | ||
Debug.Assert(key == "VS_VERSION_INFO"); | ||
offset += valueLength; | ||
Align(); | ||
|
||
valueLength = ReadHeaderAndAdvance(); | ||
key = ReadKeyAndAdvance(); | ||
Debug.Assert(key == "VarFileInfo"); | ||
offset += valueLength; | ||
Align(); | ||
|
||
valueLength = ReadHeaderAndAdvance(); | ||
key = ReadKeyAndAdvance(); | ||
Debug.Assert(key == "Translation"); | ||
ushort language = ((ushort*)address)[0]; | ||
ushort codePage = ((ushort*)address)[1]; | ||
offset += valueLength; | ||
address = resAddr + offset; | ||
|
||
valueLength = ReadHeaderAndAdvance(); | ||
key = ReadKeyAndAdvance(); | ||
Debug.Assert(key == "StringFileInfo"); | ||
offset += valueLength; | ||
Align(); | ||
|
||
short stringTableLength = ((short*)address)[0]; | ||
int stringTableEndOffset = offset + stringTableLength; | ||
valueLength = ReadHeaderAndAdvance(); | ||
key = ReadKeyAndAdvance(); | ||
Debug.Assert(key == $"{language:x4}{codePage:x4}"); | ||
|
||
do | ||
{ | ||
int valueLengthOffset = offset + sizeof(short); | ||
valueLength = ReadHeaderAndAdvance(); | ||
key = ReadKeyAndAdvance(); | ||
|
||
if (key != "CompanyName") | ||
{ | ||
offset += sizeof(short) * valueLength; | ||
Align(); // ? | ||
continue; | ||
} | ||
|
||
// int oldLength = "CloudBees, Inc.".Length + 1; // 16 | ||
int newLength = companyName.Length + 1; | ||
Debug.Assert(newLength > 12 && newLength <= 16); | ||
|
||
IntPtr newAddress = Marshal.AllocHGlobal(length); | ||
try | ||
{ | ||
Buffer.MemoryCopy((void*)resAddr, (void*)newAddress, length, length); | ||
|
||
*(short*)(newAddress + valueLengthOffset) = (short)newLength; | ||
fixed (char* ptr = companyName) | ||
{ | ||
Buffer.MemoryCopy(ptr, (void*)(newAddress + offset), newLength * sizeof(char), newLength * sizeof(char)); | ||
} | ||
|
||
File.Copy(path, outputPath, true); | ||
|
||
IntPtr update = BeginUpdateResourceW(outputPath, false); | ||
if (update == IntPtr.Zero) | ||
{ | ||
Exit(); | ||
} | ||
|
||
try | ||
{ | ||
if (!UpdateResourceW(update, RT_VERSION, VS_VERSION_INFO, language, newAddress, length)) | ||
{ | ||
Exit(); | ||
} | ||
|
||
if (!EndUpdateResourceW(update, false)) | ||
{ | ||
Exit(); | ||
} | ||
|
||
return true; | ||
} | ||
catch | ||
{ | ||
_ = EndUpdateResourceW(update, true); | ||
throw; | ||
} | ||
} | ||
finally | ||
{ | ||
Marshal.FreeHGlobal(newAddress); | ||
} | ||
} | ||
while (offset < stringTableEndOffset); | ||
|
||
return false; | ||
|
||
static void Exit() | ||
{ | ||
Throw.Command.Win32Exception(); | ||
} | ||
|
||
void Align() | ||
{ | ||
if ((offset & 3) != 0) | ||
{ | ||
offset &= ~3; | ||
offset += 4; | ||
} | ||
|
||
address = resAddr + offset; | ||
} | ||
|
||
short ReadHeaderAndAdvance() | ||
{ | ||
valueLength = ((short*)address)[1]; | ||
offset += sizeof(short) * 3; | ||
address = resAddr + offset; | ||
return valueLength; | ||
} | ||
|
||
string ReadKeyAndAdvance() | ||
{ | ||
string key = Marshal.PtrToStringUni(address)!; | ||
offset += sizeof(char) * (key.Length + 1); | ||
Align(); | ||
return key; | ||
} | ||
} | ||
finally | ||
{ | ||
_ = FreeLibrary(module); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using System; | ||
using System.IO; | ||
using Xunit; | ||
|
||
namespace WinSW.Tests.Util | ||
{ | ||
internal static class Layout | ||
{ | ||
private static string repositoryRoot; | ||
private static string artifactsDirectory; | ||
|
||
internal static string RepositoryRoot | ||
{ | ||
get | ||
{ | ||
if (repositoryRoot != null) | ||
{ | ||
return repositoryRoot; | ||
} | ||
|
||
string directory = Environment.CurrentDirectory; | ||
while (true) | ||
{ | ||
if (File.Exists(Path.Combine(directory, ".gitignore"))) | ||
{ | ||
break; | ||
} | ||
|
||
directory = Path.GetDirectoryName(directory); | ||
Assert.NotNull(directory); | ||
} | ||
|
||
return repositoryRoot = directory; | ||
} | ||
} | ||
|
||
internal static string ArtifactsDirectory => artifactsDirectory ??= Path.Combine(RepositoryRoot, "artifacts"); | ||
} | ||
} |
Oops, something went wrong.