DirectWrite RegisterFontFileLoader:创建一个字体文件加载器,并在Delphi中注册它,



我正在尝试注册一个DirectWrite (Windows 7, Windows 8)基于API的字体文件加载器,并在Delphi中重新创建,从Windows 7 SDK的CustomFont演示,显示如何使用自定义字体集合的DirectWrite API。这允许DirectWrite使用您自己从应用程序资源中加载的字体,这些字体没有在Windows字体系统中全局注册。我被侵犯了权限。下面是一个最小的示例。

首先,我对XE6中附带的Delphi Direct2D界面有疑问。在Delphi单元Winapi。D2D1为IDWriteFactory型。一个特殊的接口方法可以让你注册一个字体文件加载器:RegisterFontFileLoader

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

在比较这与c++ direct2d头文件时,我发现自己想知道上面的翻译是否正确。下面是C/c++ direct2d头文件(dwrite.h)的等效:

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

注意,在普通c++中,你不使用类型为"IDWriteFontFileLoader fontFileLoader"的变量,接口引用类型为"IDWriteFontFileLoader*"。因此,我质疑var关键字在上述接口中的适用性。

下面是我的示例代码,它在dwrite.dll中由于访问冲突而崩溃。我做了什么明显的错误吗?TLoader对象是微不足道的,它是一个TInterfacedObject,我创建它,然而,我不能注册对象。我怀疑这个方法的单个参数没有被正确传递,我不确定我是否做错了什么,或者如果我在Delphi RTL中发现了Direct2D包装器代码中的错误。

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.

在Windows SDK中可以找到c++的工作示例。上面的c++代码出现在c++演示中,是创建DirectWrite工厂和D2D1工厂后要做的第一件事之一:

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

ResourceFontFileLoader::GetLoader()简单地返回一个构造的c++对象,以通常的c++方式转换为接口类型:

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_;
    }
...
}

上面的代码在c++中手动实现了IUnknown,而我的代码使用Delphi TInterfacedObject干净地实现了IUnknown。在接口IDWriteFontFileLoader方法中只有一个方法,CreateStreamFromKey,当注册发生时,它不会在c++演示中调用,因此实际的代码不可能是一个因素,只有调用约定,堆栈和DirectWrite工厂的先决条件状态或设置步骤似乎是可能的原因。

看来我的预感是正确的,并且在Direct2D头转换DirectWrite接口中存在错误,在XE6中。

RTL代码需要在这个错误发生的地方进行修改。VAR关键字在这里不适用:

在IDWriteFactory::RegisterFontFileLoader,在WinAPI。D2D1, circa行4421,删除var关键字,并替换为const

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

请注意,同样的更改必须在IDWriteFactory, IDWriteFontCollectionLoader和这个RTL单元中的任何其他IDWrite*接口的其他地方进行,其中var被错误地选择。这是大多数地方,但不是全部。基本上,对于c++中每个"通过引用传递的传入接口引用,如IDWriteFactory*","const IDWriteFactory"对于Pascal来说是等价的。

在COM中,接口是指向虚参表的指针。在德尔菲,间接性是隐含的。在c++中,间接是显式的。所以Delphi的声明是这样的:

Intf: IUnknown

实际上是指针的声明。到IUnknown虚表。在c++中,相应的声明是

IUnknown *Intf

所以你的假设是正确的。Delphi头文件的翻译是假的。var是错误的,应该删除。使用按值传递或const

相关内容

  • 没有找到相关文章

最新更新