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

為{幸葍}努か 提交于 2019-12-13 18:40:07

问题


I am trying to register a DirectWrite (Windows 7, Windows 8) API based font file loader, and recreate in Delphi, the CustomFont demo from the Windows 7 SDK that shows how to use DirectWrite APIs with a custom font collection. This allows DirectWrite to use fonts you loaded yourself from inside your application resources that are not globally registered in the Windows font system. I am stuck with an access violation. A minimal sample is below.

First a doubt I have about the Delphi Direct2D interface that ships in XE6. In Delphi unit Winapi.D2D1 is the IDWriteFactory type. A particular interface method lets you register a font file loader: RegisterFontFileLoader

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

In comparing this to the C++ direct2d headers, I find myself wondering if the above is correctly translated. Here is the C/C++ direct2d header (dwrite.h) equivalent:

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

Note that in ordinary C++ you do not work with variables of type "IDWriteFontFileLoader fontFileLoader", an interface-reference is of type "IDWriteFontFileLoader*". Thus I question the applicability of the var keyword in the interface above.

Here is my sample code which crashes inside dwrite.dll with an access violation. Am I doing something obviously wrong here? The TLoader object is trivial, it is a TInterfacedObject, I create it, and yet, I can not register the object. I suspect that the single parameter to this method is not being passed in correctly, and I'm not sure if I have done something wrong, or if I have found a bug in the Direct2D wrapper code in the 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 found in the Windows SDK. The code above, in C++ appears in the C++ demo, as one of the first things done after the DirectWrite factory and D2D1 factory are created:

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

The ResourceFontFileLoader::GetLoader() simply returns a constructed C++ object cast to 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_;
    }
...
}

The above code implements IUnknown manually in C++, whereas my code uses Delphi TInterfacedObject which implements IUnknown cleanly. There is only one method in the interface IDWriteFontFileLoader method, CreateStreamFromKey, and it is NOT invoked in the C++ demo when the registration occurs, therefore the actual code there can not be a factor, only the calling conventions, stack, and pre-requisite state or setup steps for the DirectWrite factory seem to be possible reasons.


回答1:


It appears my hunch is correct and that there is an error in the Direct2D header conversions for the DirectWrite interfaces, in XE6.

The RTL code needs to be modified where this mistake has been made. VAR keyword is NOT applicable here:

In IDWriteFactory::RegisterFontFileLoader, in WinAPI.D2D1, circa line 4421, remove the var keyword, and replace with const.

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

Note that the same change has to be made everywhere else in IDWriteFactory, IDWriteFontCollectionLoader, and any other IDWrite* interface within this RTL unit, where var was chosen incorrectly. That is most of the places, but not all of them. Basically for each "incoming interface reference that is passed by reference, as IDWriteFactory*" in C++, "const IDWriteFactory" is the right equivalent for Pascal.




回答2:


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

Intf: IUnknown

is really a declaration of a pointer. To an IUnknown vtable. In C++ the corresponding declaration is

IUnknown *Intf

So your supposition is correct. The Delphi header translation is bogus. The var is erroneous and should be removed. Use either plain pass by value, or const.



来源:https://stackoverflow.com/questions/25448355/directwrite-registerfontfileloader-create-a-font-file-loader-and-register-it-in

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!