我正在尝试注册一个基于 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 工厂的设置步骤似乎是可能的原因。