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.