Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Build/RegFree.targets
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
<!-- Explicitly list managed assemblies that expose COM types -->
<ManagedComAssemblies Include="$(OutDir)FwUtils.dll" />
<ManagedComAssemblies Include="$(OutDir)SimpleRootSite.dll" />
<ManagedComAssemblies Include="$(OutDir)ManagedLgIcuCollator.dll" />
<ManagedComAssemblies Include="$(OutDir)ManagedVwWindow.dll" />
</ItemGroup>
<ItemGroup>
<ManagedComAssemblies Remove="$(OutDir)*.resources.dll" />
Expand Down
79 changes: 77 additions & 2 deletions Build/Src/FwBuildTasks/FwBuildTasksTests/RegFreeCreatorTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2025 SIL International
// Copyright (c) 2025 SIL International
// This software is licensed under the LGPL, version 2.1 or later
// (http://www.gnu.org/licenses/lgpl-2.1.html)

Expand All @@ -21,6 +21,7 @@ namespace SIL.FieldWorks.Build.Tasks.FwBuildTasksTests
public sealed class RegFreeCreatorTests
{
private const string AsmNamespace = "urn:schemas-microsoft-com:asm.v1";
private const string RemovedManagedLgIcuCollatorClsid = "{e771361c-ff54-4120-9525-98a0b7a9accf}";

[Test]
public void ProcessManagedAssembly_PlacesClrClassAsChildOfAssembly()
Expand Down Expand Up @@ -65,6 +66,76 @@ public void ProcessManagedAssembly_PlacesClrClassAsChildOfAssembly()
}
}

[Test]
public void ProcessManagedAssembly_TargetComVisibleFalseClass_DoesNotEmitClrClass()
{
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(tempDir);
var assemblyPath = Path.Combine(tempDir, "SampleComVisibleFalseClass.dll");

try
{
const string source = @"using System.Runtime.InteropServices;
[assembly: ComVisible(true)]
[assembly: Guid(""3D757DD4-8985-4CA6-B2C4-FA2B950C9F6D"")]
namespace RegFreeCreatorTestAssembly
{
[ComVisible(false)]
[Guid(""e771361c-ff54-4120-9525-98a0b7a9accf"")]
public class SampleComVisibleFalseClass
{
}
}";
CompileAssembly(assemblyPath, source);

var doc = new XmlDocument();
var root = doc.CreateElement("assembly", AsmNamespace);
doc.AppendChild(root);
var logger = new TaskLoggingHelper(new TestBuildEngine(), nameof(RegFreeCreatorTests));
var creator = new RegFreeCreator(doc, logger);

var foundClrClass = creator.ProcessManagedAssembly(root, assemblyPath);
Assert.That(foundClrClass, Is.False, "Assembly with only ComVisible(false) class should not produce clrClass entries.");

var ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("asmv1", AsmNamespace);
var clrClassUnderAssembly = root.SelectSingleNode("asmv1:clrClass", ns);
Assert.That(clrClassUnderAssembly, Is.Null, "clrClass must NOT be produced for ComVisible(false) class.");
var removedClrClass = root.SelectSingleNode("asmv1:clrClass[@clsid='" + RemovedManagedLgIcuCollatorClsid + "']", ns);
Assert.That(removedClrClass, Is.Null, "Removed ManagedLgIcuCollator CLSID must not appear in generated clrClass entries.");
Assert.That(root.OuterXml, Does.Not.Contain(RemovedManagedLgIcuCollatorClsid));
}
finally
{
if (Directory.Exists(tempDir))
{
Directory.Delete(tempDir, true);
}
}
}

[Test]
public void AddExcludedClsids_NormalizesClsidValues()
{
var doc = new XmlDocument();
var root = doc.CreateElement("assembly", AsmNamespace);
doc.AppendChild(root);
var logger = new TaskLoggingHelper(new TestBuildEngine(), nameof(RegFreeCreatorTests));
var creator = new RegFreeCreator(doc, logger);

creator.AddExcludedClsids(new[] { "e771361c-ff54-4120-9525-98a0b7a9accf", "{3fb0fcd2-ac55-42a8-b580-73b89a2b6215}" });

// Use reflection to verify the private field _excludedClsids was populated and normalized
var field = typeof(RegFreeCreator).GetField("_excludedClsids", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
Assert.That(field, Is.Not.Null, "Should find the private _excludedClsids field.");

var excludedHashSet = (System.Collections.Generic.HashSet<string>)field.GetValue(creator);
Assert.That(excludedHashSet, Is.Not.Null);
Assert.That(excludedHashSet.Count, Is.EqualTo(2));
Assert.That(excludedHashSet.Contains(RemovedManagedLgIcuCollatorClsid), Is.True, "Should normalize Clsid without braces.");
Assert.That(excludedHashSet.Contains("{3fb0fcd2-ac55-42a8-b580-73b89a2b6215}"), Is.True, "Should preserve Clsid with braces.");
}

private static void CompileComVisibleAssembly(string outputPath)
{
const string source = @"using System.Runtime.InteropServices;
Expand All @@ -79,7 +150,11 @@ public class SampleComClass
{
}
}";
CompileAssembly(outputPath, source);
}

private static void CompileAssembly(string outputPath, string source)
{
var provider = new CSharpCodeProvider();
var parameters = new CompilerParameters
{
Expand All @@ -94,7 +169,7 @@ public class SampleComClass
if (results.Errors.HasErrors)
{
var message = string.Join(Environment.NewLine, results.Errors.Cast<CompilerError>().Select(e => e.ToString()));
throw new InvalidOperationException($"Failed to compile COM-visible test assembly:{Environment.NewLine}{message}");
throw new InvalidOperationException($"Failed to compile test assembly:{Environment.NewLine}{message}");
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion Build/mkall.targets
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
<!-- CLSIDs implemented in managed code that must be excluded from native manifests to avoid SxS duplicates -->
<ExcludedClsids Include="{17a2e876-2968-11e0-8046-0019dbf4566e}" /> <!-- ManagedPictureFactory -->
<ExcludedClsids Include="{97199458-10C7-49da-B3AE-EA922EA64859}" /> <!-- VwDrawRootBuffered -->
<ExcludedClsids Include="{e771361c-ff54-4120-9525-98a0b7a9accf}" /> <!-- ManagedLgIcuCollator -->
<ExcludedClsids Include="{3fb0fcd2-ac55-42a8-b580-73b89a2b6215}" /> <!-- ManagedVwWindow -->
<ExcludedClsids Include="{830BAF1F-6F84-46EF-B63E-3C1BFDF9E83E}" /> <!-- ViewInputManager -->
</ItemGroup>
<PropertyGroup>
<NativeMakeOutDir></NativeMakeOutDir>
Expand Down
2 changes: 1 addition & 1 deletion Src/Common/FieldWorks/BuildInclude.targets
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<!-- Ensure reg-free manifest includes the core managed COM servers used by FieldWorks.exe -->
<ManagedComAssemblies Include="$(OutDir)FwUtils.dll" />
<ManagedComAssemblies Include="$(OutDir)SimpleRootSite.dll" />
<ManagedComAssemblies Include="$(OutDir)ManagedLgIcuCollator.dll" />
<ManagedComAssemblies Include="$(OutDir)ManagedVwWindow.dll" />
<ManagedComAssemblies Include="$(OutDir)xWorks.dll" />
<ManagedComAssemblies Include="$(OutDir)LexTextDll.dll" />
</ItemGroup>
Expand Down
16 changes: 0 additions & 16 deletions Src/Common/FwUtils/Win32Wrappers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2854,22 +2854,6 @@ public enum ToolBarButtonInfoFlags : long
#endregion

#region Ole32.dll
/// <summary>
/// Carries out the clipboard shutdown sequence. It also releases the <c>IDataObject</c>
/// pointer that was previously placed on the clipboard.
/// </summary>
/// <returns><c>true</c> if the clipboard has been flushed.</returns>
[DllImport("ole32.dll")]
public static extern int OleFlushClipboard();

/// <summary>
/// Determines whether the data object pointer previously placed on the clipboard is
/// still on the clipboard.
/// </summary>
/// <param name="pDataObject">[in] Pointer to the data object previously copied or cut.</param>
/// <returns><c>true</c> if object still on the clipboard.</returns>
[DllImport("ole32.dll")]
public static extern bool OleIsCurrentClipboard([MarshalAs(UnmanagedType.IUnknown)]object pDataObject);
#endregion

#region Shell32.dll
Expand Down
37 changes: 0 additions & 37 deletions Src/Generic/ModuleEntry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ bool ModuleEntry::s_fPerUserRegistration = false;
#endif // WIN32

#ifdef EXE_MODULE
IDataObjectPtr ModuleEntry::s_qdobjClipboard; // data stored in clipboard by this app.
bool ModuleEntry::s_fIsExe = true;

#else // EXE_MODULE
Expand All @@ -84,11 +83,6 @@ ModuleEntry::~ModuleEntry()
The code in this section only gets included for EXE servers.
/**********************************************************************************************/

void ModuleEntry::SetClipboard(IDataObject * pdobjClipboard)
{
s_qdobjClipboard = pdobjClipboard;
}

/*----------------------------------------------------------------------------------------------
For an exe, we post a WM_QUIT message to the main thread when the module reference
count goes to zero.
Expand Down Expand Up @@ -154,7 +148,6 @@ bool ModuleEntry::Startup(HINSTANCE hinst, LPSTR pszCmdLine)

// Initialize COM
HRESULT hr = OleInitialize(NULL);
s_qdobjClipboard.Clear();

if (FAILED(hr))
{
Expand Down Expand Up @@ -230,17 +223,6 @@ void ModuleEntry::ShutDown()
}
}

// Uninitialize COM, first shutting down the clipboard.
if (s_qdobjClipboard.Ptr())
{
hr = OleIsCurrentClipboard(s_qdobjClipboard.Ptr());
WarnHr(hr);
if (hr == S_OK)
{
WarnHr(OleFlushClipboard());
}
s_qdobjClipboard.Clear();
}
OleUninitialize();
}

Expand All @@ -266,7 +248,6 @@ int ModuleEntry::WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR pszCmdLine,

// Initialize COM
HRESULT hr = OleInitialize(NULL);
s_qdobjClipboard.Clear();

if (FAILED(hr))
{
Expand Down Expand Up @@ -324,17 +305,6 @@ int ModuleEntry::WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR pszCmdLine,
WarnHr(hr);
}

// Uninitialize COM, first shutting down the clipboard.
if (s_qdobjClipboard.Ptr())
{
hr = OleIsCurrentClipboard(s_qdobjClipboard.Ptr());
WarnHr(hr);
if (hr == S_OK)
{
WarnHr(OleFlushClipboard());
}
s_qdobjClipboard.Clear();
}
OleUninitialize();

return nRet;
Expand All @@ -347,13 +317,6 @@ int ModuleEntry::WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR pszCmdLine,
The code in this section only gets included for DLLs.
/**********************************************************************************************/

/*----------------------------------------------------------------------------------------------
For a DLL, we don't have a global place to record this, so ignore it.
----------------------------------------------------------------------------------------------*/
void ModuleEntry::SetClipboard(IDataObject * pdobjClipboard)
{
}

/*----------------------------------------------------------------------------------------------
For a DLL, we just decrement the count. (But DON'T put this inline in the header! It
messes up the strategy for linking in the right version of ModuleEntry for DLLs versus
Expand Down
4 changes: 0 additions & 4 deletions Src/Generic/ModuleEntry.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,6 @@ class ModuleEntry : public LLBase<ModuleEntry>
}

#ifdef EXE_MODULE
// Data placed on the clipboard by this program.
static IDataObjectPtr s_qdobjClipboard;
#ifdef USING_MFC

static bool Startup(HINSTANCE hinst, LPSTR pszCmdLine);
Expand Down Expand Up @@ -204,8 +202,6 @@ class ModuleEntry : public LLBase<ModuleEntry>
{ return s_hmod; }
static LPCTSTR GetModulePathName(void);

static void SetClipboard(IDataObject * pdobjClipboard);

// These methods increment and decrement the reference count for the module. A module
// will not be unloaded from memory as long as something is still referencing it.
// If these are not called properly, the module might be unloaded from memory too early,
Expand Down
4 changes: 1 addition & 3 deletions Src/ManagedLgIcuCollator/LgIcuCollator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ namespace SIL.FieldWorks.Language
/// Direct port of the C++ class LgIcuCollator
/// </summary>
[Serializable]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("e771361c-ff54-4120-9525-98a0b7a9accf")]
[ComVisible(false)]
public class ManagedLgIcuCollator : ILgCollatingEngine, IDisposable
{
#region Member variables
Expand Down
Loading