代码之家  ›  专栏  ›  技术社区  ›  Warren P

DirectWriteRegisterFontFileLoader:创建字体文件加载器并在Delphi中注册它,而不违反访问权限

  •  1
  • Warren P  · 技术社区  · 12 年前

    我正在尝试注册基于DirectWrite(Windows 7、Windows 8)API的字体文件加载程序,并在Delphi中重新创建 CustomFont demo from the Windows 7 SDK 显示了如何使用 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,我创建了它,但我无法注册该对象。我怀疑这个方法的单个参数没有正确传递,我不确定我是否做错了什么,或者我是否在DelphiRTL中的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工厂的先决条件状态或设置步骤似乎是可能的原因。

    2 回复  |  直到 12 年前
        1
  •  1
  •   Warren P    12 年前

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

    如果出现此错误,则需要修改RTL代码。VAR关键字在此处不适用:

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

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

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

        2
  •  1
  •   David Heffernan    12 年前

    在COM中,接口是指向vtable的指针。在Delphi中,间接是隐式的。在C++中,间接寻址是显式的。因此,Delphi声明如下:

    Intf: IUnknown
    

    实际上是指针的声明。至 IUnknown vtable的。在C++中,对应的声明是

    IUnknown *Intf
    

    所以你的假设是正确的。Delphi标头翻译是假的。这个 var 错误,应删除。使用纯传递值,或 const .