1

我正在尝试注册一个基于 DirectWrite(Windows 7、Windows 8)API 的字体文件加载器,并在 Delphi 中重新创建,这是来自 Windows 7 SDK 的 CustomFont 演示,它展示了如何将DirectWriteAPI 与自定义字体集合一起使用。这允许 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,而我的代码使用TInterfacedObject了干净地实现 IUnknown 的 Delphi。接口IDWriteFontFileLoader方法中只有一个方法,CreateStreamFromKey,在注册的时候C++demo中没有调用,所以实际代码不可能有影响,只有调用约定,栈,前置状态或 DirectWrite 工厂的设置步骤似乎是可能的原因。

4

2 回答 2

1

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

出现此错误的地方需要修改 RTL 代码。VAR 关键字在这里不适用:

在 IDWriteFactory::RegisterFontFileLoader 中,在 WinAPI.D2D1 中,大约第 4421 行,删除var关键字,并替换为const.

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

IDWrite*请注意,必须在 IDWriteFactory、IDWriteFontCollectionLoader 和此 RTL 单元中的任何其他接口中的其他任何地方进行相同的更改,其中var选择不正确。这是大多数地方,但不是全部。基本上对于每个“通过引用传递的传入接口引用,如 C++ 中的 IDWriteFactory*”,“const IDWriteFactory”是 Pascal 的正确等价物。

于 2014-08-22T13:49:09.220 回答
1

在 COM 中,接口是指向 vtable 的指针。在 Delphi 中,间接是隐含的。在 C++ 中,间接是显式的。所以这种形式的德尔福声明:

Intf: IUnknown

实际上是一个指针的声明。到一个IUnknownvtable。在 C++ 中,相应的声明是

IUnknown *Intf

所以你的假设是正确的。Delphi 标头翻译是伪造的。这var是错误的,应该删除。使用简单的按值传递,或const.

于 2014-08-22T19:55:47.307 回答