1

我正在尝试使用 delphi 的 openVR dll。然而,这个 dll 只导出了有限的功能,很多功能都留在接口内。

因为有一些使用 openVR 的示例,所以我看一下 c 版本标头c# 版本标头,看看它们是如何做到的。

我从 c 标头中获得的知识并不多,而在 c# 标头中,我注意到他们正在使用一些结构(如 delphi 中的接口)来存储函数表,并有一个用于该结构的类(如 delphi 中的实现类) ,在类内部有一个创建函数,女巫似乎破解了指向所有这些函数的指针。

IVRSystem FnTable;
internal CVRSystem(IntPtr pInterface)
{
    FnTable = (IVRSystem)Marshal.PtrToStructure(pInterface, typeof(IVRSystem));
}

指针在pInterface一个包含一组实现类的大类中给出。

public CVRSystem VRSystem()
{
    CheckClear();
    if (m_pVRSystem == null)
    {
        var eError = EVRInitError.None;
        var pInterface = OpenVRInterop.GetGenericInterface(FnTable_Prefix+IVRSystem_Version, ref eError);
        if (pInterface != IntPtr.Zero && eError == EVRInitError.None)
            m_pVRSystem = new CVRSystem(pInterface);
    }
    return m_pVRSystem;
}

哪里OpenVRInterop.GetGenericInterface是 dll 导出的函数之一。

所以我的问题是:

(1) delphi 可以做 C# 做的事情吗?似乎他只是通过原始指针(地址?偏移量?)调用这些函数我搜索了delphi处理dll,只有两种方式(静态和动态)都需要函数名。

function someFunction(a : integer) :integer; stdcall; external ’someDll.dll’;

dllHandle := LoadLibrary(’someDll.dll’);
@someFunction := GetProcAddress(dllHandle,'someFunction');

(2) 库的c头是怎么加载的?我在那里没有找到相关的代码。

4

1 回答 1

1

感谢 Remy 的建议,我想我找到了解决方案。

我确实将 C# 标头转换为 delphi,现在它工作正常。

我将以 VRSystem 为例。

首先,我们需要一些基本的 enum、const、struct translate。

枚举确实需要一个 Z4 标签来使大小匹配 c 样式枚举。

{$Z4}
ETrackingResult = (
    ETrackingResult_Uninitialized = 1,
    ETrackingResult_Calibrating_InProgress = 100,
    ETrackingResult_Calibrating_OutOfRange = 101,
    ETrackingResult_Running_OK = 200,
    ETrackingResult_Running_OutOfRange = 201
);

对于结构,记录是完美的匹配。

TrackedDevicePose_t = record
    mDeviceToAbsoluteTracking : HmdMatrix34_t;
    vVelocity : HmdVector3_t;
    vAngularVelocity : HmdVector3_t;
    eTrackingResult : ETrackingResult;
    bPoseIsValid : boolean;
    bDeviceIsConnected : boolean;
end;

然后我们需要像这样为接口内部的每个函数清除委托函数。

_GetRecommendedRenderTargetSize = procedure(var pnWidth : uint32; var pnHeight : uint32); stdcall;
_GetProjectionMatrix = function(eEye : EVREye; fNearZ : single; fFarZ : single; eProjType : EGraphicsAPIConvention) : HmdMatrix44_t; stdcall;
...
_AcknowledgeQuit_UserPrompt = procedure(); stdcall;

和一个结构来保存它们,但是这次我们需要一个完美的大小匹配,所以我们需要打包记录

PIVRSystem = ^IVRSystem;
IVRSystem = packed record
    GetRecommendedRenderTargetSize : _GetRecommendedRenderTargetSize;
    GetProjectionMatrix : _GetProjectionMatrix;
    ....
    AcknowledgeQuit_UserPrompt : _AcknowledgeQuit_UserPrompt;
end;

最后一个类持有该结构,并将通过给它一个指针来初始化这个结构。

CVRSystem = class
    FnTable : PIVRSystem;
    Constructor Create(FNPointer : IntPtr);

    procedure GetRecommendedRenderTargetSize(var pnWidth : uint32; var pnHeight : uint32);
    function GetProjectionMatrix(eEye : EVREye; fNearZ : single; fFarZ : single; eProjType : EGraphicsAPIConvention) : HmdMatrix44_t;
    ...
    procedure AcknowledgeQuit_UserPrompt();
end;

所以现在我们可以通过调用里面的函数来使用这些函数,里面的函数CVRSystem直接指向里面的函数FNTable

通过这种方式,我们使用结构作为函数表,我想知道是否会有更棘手的方法来破解虚拟方法表。

于 2016-10-13T00:34:10.723 回答