Skip to content

Commit 846c123

Browse files
authored
Reduce optional COM usage (#904)
1 parent 274c1cc commit 846c123

8 files changed

Lines changed: 82 additions & 65 deletions

File tree

Build/RegFree.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
<!-- Explicitly list managed assemblies that expose COM types -->
6767
<ManagedComAssemblies Include="$(OutDir)FwUtils.dll" />
6868
<ManagedComAssemblies Include="$(OutDir)SimpleRootSite.dll" />
69-
<ManagedComAssemblies Include="$(OutDir)ManagedLgIcuCollator.dll" />
69+
<ManagedComAssemblies Include="$(OutDir)ManagedVwWindow.dll" />
7070
</ItemGroup>
7171
<ItemGroup>
7272
<ManagedComAssemblies Remove="$(OutDir)*.resources.dll" />

Build/Src/FwBuildTasks/FwBuildTasksTests/RegFreeCreatorTests.cs

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2025 SIL International
1+
// Copyright (c) 2025 SIL International
22
// This software is licensed under the LGPL, version 2.1 or later
33
// (http://www.gnu.org/licenses/lgpl-2.1.html)
44

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

2526
[Test]
2627
public void ProcessManagedAssembly_PlacesClrClassAsChildOfAssembly()
@@ -65,6 +66,76 @@ public void ProcessManagedAssembly_PlacesClrClassAsChildOfAssembly()
6566
}
6667
}
6768

69+
[Test]
70+
public void ProcessManagedAssembly_TargetComVisibleFalseClass_DoesNotEmitClrClass()
71+
{
72+
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
73+
Directory.CreateDirectory(tempDir);
74+
var assemblyPath = Path.Combine(tempDir, "SampleComVisibleFalseClass.dll");
75+
76+
try
77+
{
78+
const string source = @"using System.Runtime.InteropServices;
79+
[assembly: ComVisible(true)]
80+
[assembly: Guid(""3D757DD4-8985-4CA6-B2C4-FA2B950C9F6D"")]
81+
namespace RegFreeCreatorTestAssembly
82+
{
83+
[ComVisible(false)]
84+
[Guid(""e771361c-ff54-4120-9525-98a0b7a9accf"")]
85+
public class SampleComVisibleFalseClass
86+
{
87+
}
88+
}";
89+
CompileAssembly(assemblyPath, source);
90+
91+
var doc = new XmlDocument();
92+
var root = doc.CreateElement("assembly", AsmNamespace);
93+
doc.AppendChild(root);
94+
var logger = new TaskLoggingHelper(new TestBuildEngine(), nameof(RegFreeCreatorTests));
95+
var creator = new RegFreeCreator(doc, logger);
96+
97+
var foundClrClass = creator.ProcessManagedAssembly(root, assemblyPath);
98+
Assert.That(foundClrClass, Is.False, "Assembly with only ComVisible(false) class should not produce clrClass entries.");
99+
100+
var ns = new XmlNamespaceManager(doc.NameTable);
101+
ns.AddNamespace("asmv1", AsmNamespace);
102+
var clrClassUnderAssembly = root.SelectSingleNode("asmv1:clrClass", ns);
103+
Assert.That(clrClassUnderAssembly, Is.Null, "clrClass must NOT be produced for ComVisible(false) class.");
104+
var removedClrClass = root.SelectSingleNode("asmv1:clrClass[@clsid='" + RemovedManagedLgIcuCollatorClsid + "']", ns);
105+
Assert.That(removedClrClass, Is.Null, "Removed ManagedLgIcuCollator CLSID must not appear in generated clrClass entries.");
106+
Assert.That(root.OuterXml, Does.Not.Contain(RemovedManagedLgIcuCollatorClsid));
107+
}
108+
finally
109+
{
110+
if (Directory.Exists(tempDir))
111+
{
112+
Directory.Delete(tempDir, true);
113+
}
114+
}
115+
}
116+
117+
[Test]
118+
public void AddExcludedClsids_NormalizesClsidValues()
119+
{
120+
var doc = new XmlDocument();
121+
var root = doc.CreateElement("assembly", AsmNamespace);
122+
doc.AppendChild(root);
123+
var logger = new TaskLoggingHelper(new TestBuildEngine(), nameof(RegFreeCreatorTests));
124+
var creator = new RegFreeCreator(doc, logger);
125+
126+
creator.AddExcludedClsids(new[] { "e771361c-ff54-4120-9525-98a0b7a9accf", "{3fb0fcd2-ac55-42a8-b580-73b89a2b6215}" });
127+
128+
// Use reflection to verify the private field _excludedClsids was populated and normalized
129+
var field = typeof(RegFreeCreator).GetField("_excludedClsids", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
130+
Assert.That(field, Is.Not.Null, "Should find the private _excludedClsids field.");
131+
132+
var excludedHashSet = (System.Collections.Generic.HashSet<string>)field.GetValue(creator);
133+
Assert.That(excludedHashSet, Is.Not.Null);
134+
Assert.That(excludedHashSet.Count, Is.EqualTo(2));
135+
Assert.That(excludedHashSet.Contains(RemovedManagedLgIcuCollatorClsid), Is.True, "Should normalize Clsid without braces.");
136+
Assert.That(excludedHashSet.Contains("{3fb0fcd2-ac55-42a8-b580-73b89a2b6215}"), Is.True, "Should preserve Clsid with braces.");
137+
}
138+
68139
private static void CompileComVisibleAssembly(string outputPath)
69140
{
70141
const string source = @"using System.Runtime.InteropServices;
@@ -79,7 +150,11 @@ public class SampleComClass
79150
{
80151
}
81152
}";
153+
CompileAssembly(outputPath, source);
154+
}
82155

156+
private static void CompileAssembly(string outputPath, string source)
157+
{
83158
var provider = new CSharpCodeProvider();
84159
var parameters = new CompilerParameters
85160
{
@@ -94,7 +169,7 @@ public class SampleComClass
94169
if (results.Errors.HasErrors)
95170
{
96171
var message = string.Join(Environment.NewLine, results.Errors.Cast<CompilerError>().Select(e => e.ToString()));
97-
throw new InvalidOperationException($"Failed to compile COM-visible test assembly:{Environment.NewLine}{message}");
172+
throw new InvalidOperationException($"Failed to compile test assembly:{Environment.NewLine}{message}");
98173
}
99174
}
100175
}

Build/mkall.targets

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
<!-- CLSIDs implemented in managed code that must be excluded from native manifests to avoid SxS duplicates -->
4444
<ExcludedClsids Include="{17a2e876-2968-11e0-8046-0019dbf4566e}" /> <!-- ManagedPictureFactory -->
4545
<ExcludedClsids Include="{97199458-10C7-49da-B3AE-EA922EA64859}" /> <!-- VwDrawRootBuffered -->
46-
<ExcludedClsids Include="{e771361c-ff54-4120-9525-98a0b7a9accf}" /> <!-- ManagedLgIcuCollator -->
46+
<ExcludedClsids Include="{3fb0fcd2-ac55-42a8-b580-73b89a2b6215}" /> <!-- ManagedVwWindow -->
47+
<ExcludedClsids Include="{830BAF1F-6F84-46EF-B63E-3C1BFDF9E83E}" /> <!-- ViewInputManager -->
4748
</ItemGroup>
4849
<PropertyGroup>
4950
<NativeMakeOutDir></NativeMakeOutDir>

Src/Common/FieldWorks/BuildInclude.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<!-- Ensure reg-free manifest includes the core managed COM servers used by FieldWorks.exe -->
88
<ManagedComAssemblies Include="$(OutDir)FwUtils.dll" />
99
<ManagedComAssemblies Include="$(OutDir)SimpleRootSite.dll" />
10-
<ManagedComAssemblies Include="$(OutDir)ManagedLgIcuCollator.dll" />
10+
<ManagedComAssemblies Include="$(OutDir)ManagedVwWindow.dll" />
1111
<ManagedComAssemblies Include="$(OutDir)xWorks.dll" />
1212
<ManagedComAssemblies Include="$(OutDir)LexTextDll.dll" />
1313
</ItemGroup>

Src/Common/FwUtils/Win32Wrappers.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2854,22 +2854,6 @@ public enum ToolBarButtonInfoFlags : long
28542854
#endregion
28552855

28562856
#region Ole32.dll
2857-
/// <summary>
2858-
/// Carries out the clipboard shutdown sequence. It also releases the <c>IDataObject</c>
2859-
/// pointer that was previously placed on the clipboard.
2860-
/// </summary>
2861-
/// <returns><c>true</c> if the clipboard has been flushed.</returns>
2862-
[DllImport("ole32.dll")]
2863-
public static extern int OleFlushClipboard();
2864-
2865-
/// <summary>
2866-
/// Determines whether the data object pointer previously placed on the clipboard is
2867-
/// still on the clipboard.
2868-
/// </summary>
2869-
/// <param name="pDataObject">[in] Pointer to the data object previously copied or cut.</param>
2870-
/// <returns><c>true</c> if object still on the clipboard.</returns>
2871-
[DllImport("ole32.dll")]
2872-
public static extern bool OleIsCurrentClipboard([MarshalAs(UnmanagedType.IUnknown)]object pDataObject);
28732857
#endregion
28742858

28752859
#region Shell32.dll

Src/Generic/ModuleEntry.cpp

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ bool ModuleEntry::s_fPerUserRegistration = false;
5757
#endif // WIN32
5858

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

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

87-
void ModuleEntry::SetClipboard(IDataObject * pdobjClipboard)
88-
{
89-
s_qdobjClipboard = pdobjClipboard;
90-
}
91-
9286
/*----------------------------------------------------------------------------------------------
9387
For an exe, we post a WM_QUIT message to the main thread when the module reference
9488
count goes to zero.
@@ -154,7 +148,6 @@ bool ModuleEntry::Startup(HINSTANCE hinst, LPSTR pszCmdLine)
154148

155149
// Initialize COM
156150
HRESULT hr = OleInitialize(NULL);
157-
s_qdobjClipboard.Clear();
158151

159152
if (FAILED(hr))
160153
{
@@ -230,17 +223,6 @@ void ModuleEntry::ShutDown()
230223
}
231224
}
232225

233-
// Uninitialize COM, first shutting down the clipboard.
234-
if (s_qdobjClipboard.Ptr())
235-
{
236-
hr = OleIsCurrentClipboard(s_qdobjClipboard.Ptr());
237-
WarnHr(hr);
238-
if (hr == S_OK)
239-
{
240-
WarnHr(OleFlushClipboard());
241-
}
242-
s_qdobjClipboard.Clear();
243-
}
244226
OleUninitialize();
245227
}
246228

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

267249
// Initialize COM
268250
HRESULT hr = OleInitialize(NULL);
269-
s_qdobjClipboard.Clear();
270251

271252
if (FAILED(hr))
272253
{
@@ -324,17 +305,6 @@ int ModuleEntry::WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR pszCmdLine,
324305
WarnHr(hr);
325306
}
326307

327-
// Uninitialize COM, first shutting down the clipboard.
328-
if (s_qdobjClipboard.Ptr())
329-
{
330-
hr = OleIsCurrentClipboard(s_qdobjClipboard.Ptr());
331-
WarnHr(hr);
332-
if (hr == S_OK)
333-
{
334-
WarnHr(OleFlushClipboard());
335-
}
336-
s_qdobjClipboard.Clear();
337-
}
338308
OleUninitialize();
339309

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

350-
/*----------------------------------------------------------------------------------------------
351-
For a DLL, we don't have a global place to record this, so ignore it.
352-
----------------------------------------------------------------------------------------------*/
353-
void ModuleEntry::SetClipboard(IDataObject * pdobjClipboard)
354-
{
355-
}
356-
357320
/*----------------------------------------------------------------------------------------------
358321
For a DLL, we just decrement the count. (But DON'T put this inline in the header! It
359322
messes up the strategy for linking in the right version of ModuleEntry for DLLs versus

Src/Generic/ModuleEntry.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,6 @@ class ModuleEntry : public LLBase<ModuleEntry>
166166
}
167167

168168
#ifdef EXE_MODULE
169-
// Data placed on the clipboard by this program.
170-
static IDataObjectPtr s_qdobjClipboard;
171169
#ifdef USING_MFC
172170

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

207-
static void SetClipboard(IDataObject * pdobjClipboard);
208-
209205
// These methods increment and decrement the reference count for the module. A module
210206
// will not be unloaded from memory as long as something is still referencing it.
211207
// If these are not called properly, the module might be unloaded from memory too early,

Src/ManagedLgIcuCollator/LgIcuCollator.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ namespace SIL.FieldWorks.Language
1717
/// Direct port of the C++ class LgIcuCollator
1818
/// </summary>
1919
[Serializable]
20-
[ComVisible(true)]
21-
[ClassInterface(ClassInterfaceType.None)]
22-
[Guid("e771361c-ff54-4120-9525-98a0b7a9accf")]
20+
[ComVisible(false)]
2321
public class ManagedLgIcuCollator : ILgCollatingEngine, IDisposable
2422
{
2523
#region Member variables

0 commit comments

Comments
 (0)