If I create a new WinForms project targeting .NET Framework 4.7.2, and replace Program with the following code
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
foo();
}
static void foo()
{
MessageBox.Show("a");
}
}
When I set a breakpoint on the call to foo and then step in, there is a delay of over 300ms in entering the function. The delay is caused by the JIT attempting to load System.Windows.Forms.dll.
Investigating with Process Monitor, I've observed that over 300ms is spent making multiple attempts to load a file C:\Windows\assembly\pubpol636.dat that doesn't exist
10:51:16.8555650 PM WindowsFormsApp1.exe 48888 RegQueryValue HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default\Latest SUCCESS
10:51:16.8556539 PM WindowsFormsApp1.exe 48888 CreateFile C:\Windows\assembly\pubpol636.dat NAME NOT FOUND
10:51:16.8970666 PM WindowsFormsApp1.exe 48888 RegQueryValue HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default\Latest SUCCESS
10:51:16.8971453 PM WindowsFormsApp1.exe 48888 CreateFile C:\Windows\assembly\pubpol636.dat NAME NOT FOUND
10:51:16.9285253 PM WindowsFormsApp1.exe 48888 RegQueryValue HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default\Latest SUCCESS
10:51:16.9286231 PM WindowsFormsApp1.exe 48888 CreateFile C:\Windows\assembly\pubpol636.dat NAME NOT FOUND
10:51:16.9606527 PM WindowsFormsApp1.exe 48888 RegQueryValue HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default\Latest SUCCESS
10:51:16.9607522 PM WindowsFormsApp1.exe 48888 CreateFile C:\Windows\assembly\pubpol636.dat NAME NOT FOUND
10:51:16.9938988 PM WindowsFormsApp1.exe 48888 RegQueryValue HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default\Latest SUCCESS
10:51:16.9940084 PM WindowsFormsApp1.exe 48888 CreateFile C:\Windows\assembly\pubpol636.dat NAME NOT FOUND
10:51:17.0387184 PM WindowsFormsApp1.exe 48888 RegQueryValue HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default\Latest SUCCESS
10:51:17.0389674 PM WindowsFormsApp1.exe 48888 CreateFile C:\Windows\assembly\pubpol636.dat NAME NOT FOUND
10:51:17.0691301 PM WindowsFormsApp1.exe 48888 RegQueryValue HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default\Latest SUCCESS
10:51:17.0692622 PM WindowsFormsApp1.exe 48888 CreateFile C:\Windows\assembly\pubpol636.dat NAME NOT FOUND
10:51:17.1004577 PM WindowsFormsApp1.exe 48888 RegQueryValue HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default\Latest SUCCESS
10:51:17.1005328 PM WindowsFormsApp1.exe 48888 CreateFile C:\Windows\assembly\pubpol636.dat NAME NOT FOUND
10:51:17.1319618 PM WindowsFormsApp1.exe 48888 RegQueryValue HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default\Latest SUCCESS
10:51:17.1324102 PM WindowsFormsApp1.exe 48888 CreateFile C:\Windows\assembly\pubpol636.dat NAME NOT FOUND
10:51:17.1635977 PM WindowsFormsApp1.exe 48888 RegQueryValue HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default\Latest SUCCESS
10:51:17.1636861 PM WindowsFormsApp1.exe 48888 CreateFile C:\Windows\assembly\pubpol636.dat NAME NOT FOUND
The stack trace of these calls is as follows
KernelBase!CreateFileW + 0x6d
clr!CPublisherPolicy::LoadBitmaskData + 0xbd
clr!CPublisherPolicy::Init + 0x1d3
clr!CPublisherPolicy::Create + 0x52
clr!CAppCtxPolicyConfigs::Create + 0x34
clr!CAsmDownloadMgr::PreDownloadCheck + 0x186
clr!CAssemblyDownload::PreDownload + 0x108
clr!CAssemblyName::BindToObject + 0x121f
clr!FusionBind::RemoteLoad + 0x269
clr!AssemblySpec::LoadAssembly + 0x244
clr!AssemblySpec::FindAssemblyFile + 0xf1
clr!AppDomain::BindAssemblySpec + 0x1296
clr!PEFile::LoadAssembly + 0xc3
clr!Module::LoadAssembly + 0x14c
clr!Assembly::FindModuleByTypeRef + 0x248
clr!ClassLoader::LoadTypeDefOrRefThrowing + 0x115
clr!MemberLoader::GetDescFromMemberRef + 0x2e8
clr!CEEInfo::resolveToken + 0x351
clr!Compiler::impResolveToken + 0x48
...
The source of clr!CPublisherPolicy::LoadBitmaskData does not appear to be available on GitHub, however decompiling this function with IDA Pro, I can see that it makes 10 attempts to load a pubpol policy file, waiting 30ms between each attempt! .NET Core does not have this problem, however I am multi-targeting both .NET Framework and .NET Core, and I just can't believe that the CLR has a forced 300ms delay that occurs when trying to load an assembly.
I tried to put a publisher policy in my App.config file, however this did not seem to prevent this 300ms delay. This could theoretically affect any DLL, however in practice it does not. I haven't figured out what's special about System.Windows.Forms.dll yet that triggers the code path that leads to the 300ms delay, when other DLLs clearly don't have this issue. I haven't found any evidence of anyone else ever having noticed this issue, which makes me seriously question whether I'm somehow doing something wrong - surely someone would have noticed such a big delay before!
--
Further testing seems to show that every .NET Framework application on my computer is wasting 300ms at startup looking for a C:\Windows\assembly\pubpolXXX.dat file. The issue is not specific to WinForms. Creating an empty file with the name it's after "fixes" the issue, but this isn't really a proper solution. The issue also does not occur if you do not have the "Latest" item under HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default
HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Defaulton your machine by the way? On mine, it's0x5cand I don't seem to have this problem. (I have no idea what that means though.)HKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default\Default:index636,Latest,LegacyPolicyTimeStamp,OldestandUsageMask. The value ofLatest(aREG_DWORD) is636(hence why it's looking forpubpol636.dat). However, I checked a server running Server 2022 and it didn't have any values underHKLM\SOFTWARE\Microsoft\Fusion\PublisherPolicy\Default. My computer has .NET 533320 (4.8.1 or later) while the Server 2022 system has 528449 (4.8)