DirectWrite RegisterFontFileLoader: create a font file loader and register it in Delphi without violating access rights

I am trying to register a DirectWrite API font file downloader (Windows 7, Windows 8) based on the DirectWrite API and update in Delphi, a CustomFont demo from the Windows 7 SDK that shows how to use the DirectWrite API with a custom font collection. This allows DirectWrite to use fonts that you downloaded from internal application resources that are not globally registered with the Windows font system. I am stuck with access violation. The minimum sample is below.

At first, I doubt the Delphi Direct2D interface that ships with XE6. In a Delphi block, Winapi.D2D1 is an IDWriteFactory type. A special interface method allows you to register a font file loader: RegisterFontFileLoader

 IDWriteFactory = interface(IUnknown) [SID_IDWriteFactory] .... function RegisterFontFileLoader( var fontFileLoader: IDWriteFontFileLoader): HResult; stdcall; .... end; 

Comparing this to the Direct2d headers in C ++, I wonder if the above translates correctly. Here is the C / C ++ header equivalent of direct2d (dwrite.h):

 interface DWRITE_DECLARE_INTERFACE("b859ee5a-d838-4b5b-a2e8-1adc7d93db48") IDWriteFactory : public IUnknown { ... STDMETHOD(RegisterFontFileLoader)( IDWriteFontFileLoader* fontFileLoader ) PURE; ... } 

Please note that in normal C ++ you do not work with variables of the type "IDWriteFontFileLoader fontFileLoader", the link to the interface is of the type "IDWriteFontFileLoader *". Thus, I ask the applicability of the var keyword in the interface above.

Here is my sample code that breaks inside dwrite.dll with access violation. Am I doing something obviously wrong here? The TLoader object is trivial, it is a TInterfacedObject, I create it, and yet I cannot register the object. I suspect that the only parameter for this method is not passed correctly, and I'm not sure if I did something wrong, or if I found an error in the code of the Direct2D shell in Delphi RTL.

 unit DirectWriteBugMain; interface uses WinApi.Windows, System.Types, Vcl.Direct2D, WinAPI.D2D1, System.SysUtils; type TLoader =class(TInterfacedObject,IDWriteFontFileLoader) function CreateStreamFromKey( fontFileReferenceKey: Pointer; fontFileReferenceKeySize: Cardinal; out fontFileStream: IDWriteFontFileStream): HResult; stdcall; end; procedure main; { called from dpr, in a console app } implementation function TLoader.CreateStreamFromKey( fontFileReferenceKey: Pointer; fontFileReferenceKeySize: Cardinal; out fontFileStream: IDWriteFontFileStream): HResult; stdcall; begin fontFileStream := nil; result := E_FAIL; end; procedure main; var Loader:IDWriteFontFileLoader; begin try Loader := TLoader.Create as IDWriteFontFileLoader; DWriteFactory.RegisterFontFileLoader( Loader); except on E: Exception do begin Writeln(E.ClassName, ': ', E.Message); ReadLn; end; end; end; end. 

A working sample in C ++ is in the Windows SDK. The code above in C ++ appears in a C ++ demo, as one of the first things done after creating the DirectWrite factory and D2D1 factory:

 if (FAILED(hr = g_dwriteFactory->RegisterFontFileLoader(ResourceFontFileLoader::GetLoader()))) return hr; 

ResourceFontFileLoader::GetLoader() simply returns the constructed C ++ object, converted to an interface type, in the usual C ++ way:

 class ResourceFontFileLoader : public IDWriteFontFileLoader { public: ResourceFontFileLoader() : refCount_(0) { } // IUnknown methods virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject); virtual ULONG STDMETHODCALLTYPE AddRef(); virtual ULONG STDMETHODCALLTYPE Release(); // IDWriteFontFileLoader methods virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey( void const* fontFileReferenceKey, // [fontFileReferenceKeySize] in bytes UINT32 fontFileReferenceKeySize, OUT IDWriteFontFileStream** fontFileStream ); // Gets the singleton loader instance. static IDWriteFontFileLoader* GetLoader() { return instance_; } ... } 

This code implements IUnknown manually in C ++, while my code uses Delphi TInterfacedObject , which implements IUnknown purely. There is only one method in the IDWriteFontFileLoader interface, the CreateStreamFromKey method, and it is NOT called in a C ++ demonstration when registration occurs, so the actual code cannot be a factor, but only calling conventions, stacks and preconditions or configuration steps for DirectWrite factory, appear to be possible causes.

+1
source share
2 answers

It seems that my guess is correct and that there is an error in the Direct2D conversions for DirectWrite interfaces in XE6.

The RTL code needs to be changed if this error was made. The VAR keyword is not applicable here:

In IDWriteFactory :: RegisterFontFileLoader, in WinAPI.D2D1, near line 4421, delete the var keyword and replace it with const .

  function RegisterFontFileLoader( var fontFileLoader: IDWriteFontFileLoader): HResult; stdcall; 

Note that the same change must be made everywhere in IDWriteFactory, IDWriteFontCollectionLoader, and any other IDWrite* interface in this RTL module, where var was not selected correctly. These are most places, but not all of them. Basically for every "inbound interface link that is passed by reference as IDWriteFactory *" in C ++, "const IDWriteFactory" is the right equivalent for Pascal.

+1
source

In COM, the interface is a pointer to a vtable. In Delphi indirect indirect. In C ++, indirectness is explicit. So, the Delphi declaration of this form:

 Intf: IUnknown 

really is a pointer declaration. For IUnknown vtable. In C ++, the corresponding declaration

 IUnknown *Intf 

So your assumption is correct. Translation of Delphi headers is fictitious. var is erroneous and should be removed. Use either a simple pass by value or const .

+1
source

Source: https://habr.com/ru/post/973998/


All Articles