public HRESULT GetCurrentFontFile(out IDWriteFontFile fontFile) { DWriteFontFile file; try { file = _enumerator.Current; } catch (Exception e) { ComError.SetError(e.GetAllMessages()); fontFile = null; return(HRESULTS.DISP_E_EXCEPTION); } if (file is DWriteFontStreamFile sf && sf.FilePath != null) { using (var mem = new ComMemory(Marshal.SizeOf <long>())) { if (sf.LastWriteTime.HasValue) { Marshal.WriteInt64(mem.Pointer, sf.LastWriteTime.Value.ToFileTime()); } var ptr = sf.LastWriteTime.HasValue ? mem.Pointer : IntPtr.Zero; return(_factory.CreateFontFileReference(sf.FilePath, ptr, out fontFile)); } } var stream = new FontFileStream(file); _loader.AddStream(stream); return(_factory.CreateCustomFontFileReference(stream.Key, (uint)stream.KeySize, _loader, out fontFile)); }
unsafe internal static IDWriteFontFile CreateFontFile( IDWriteFactory factory, IntPtr fontFileLoader, // IDWriteFontFileLoader* Uri filePathUri ) { IDWriteFontFile dwriteFontFile; bool isLocal = Factory.IsLocalUri(filePathUri); if (isLocal) { // DWrite currently has a slow lookup for the last write time, which // introduced a noticable perf regression when we switched over. // To mitigate this scenario, we will fetch the timestamp ourselves // and cache it for future calls. // // Note: we only do this if a Dispatcher exists for the current // thread. There is a seperate cache for each thread. FILETIME cachedTimeStamp; IntPtr pTimeStamp = IntPtr.Zero; // If something fails, do nothing and let DWrite sort it out. Dispatcher currentDispatcher = Dispatcher.FromThread(Thread.CurrentThread); if (currentDispatcher != null) { try { // One-time initialization per thread. if (_timeStampCache == null) { _timeStampCache = new Dictionary <Uri, FILETIME>(); } if (!_timeStampCache.TryGetValue(filePathUri, out cachedTimeStamp)) { long longFileTime = File.GetLastWriteTime(filePathUri.LocalPath).ToFileTime(); cachedTimeStamp.dwLowDateTime = (int)(longFileTime); cachedTimeStamp.dwHighDateTime = (int)(longFileTime >> 32); _timeStampCache.Add(filePathUri, cachedTimeStamp); // We don't want to hold this cached value for a long time since // all font references will be tied to this timestamp, and any font // update during the lifetime of the application will cause is to // encounter errors. So we use a dispatcher operation to clear // the cache as soon as we get back to pumping messages. if (_timeStampCacheCleanupOp == null) { _timeStampCacheCleanupOp = currentDispatcher.BeginInvoke(new Action(CleanupTimeStampCache)); } } pTimeStamp = Marshal.AllocCoTaskMem(Marshal.SizeOf <FILETIME>()); Marshal.StructureToPtr <FILETIME>(cachedTimeStamp, pTimeStamp, false); } catch { } } try { dwriteFontFile = factory.CreateFontFileReference( filePathUri.LocalPath, pTimeStamp ); } finally { Marshal.FreeCoTaskMem(pTimeStamp); } } else { string filePath = filePathUri.AbsoluteUri; fixed(char *pFilePath = filePath) { dwriteFontFile = factory.CreateCustomFontFileReference( new IntPtr(pFilePath), (uint)(filePath.Length + 1) * 2, fontFileLoader ); } } return(dwriteFontFile); }