我正在尝试注册一个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
。