仅使用指针调用dll函数
本文关键字:dll 函数 调用 指针 | 更新日期: 2023-09-27 18:11:40
我正在尝试使用openVR dll从delphi。但是,这个dll只导出了有限的函数,很多函数都留在接口中。
因为有一些使用openVR的示例,所以我看了看c版本头和c#版本头,看看他们是如何做到的。
我没有从c头中获得足够的知识,而在c#头中,我注意到他们正在使用一些结构体(如delphi中的接口)来存储函数表,并有一个类(如delphi中的实现类)用于该结构体,类内部有一个创建函数,似乎可以hack所有这些函数的指针。
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头文件是如何加载的?
多亏了Remy的建议,我想我找到了解决办法。
我把c#头文件翻译成delphi,现在工作得很好。
我以VRSystem为例。
首先,我们需要一些基本的enum, const, struct translate
enum确实需要一个Z4标签来使大小匹配c样式enum。
{$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
内部的函数
通过这种方式,我们使用结构体作为函数表,我想知道是否会有更棘手的方法来破解虚拟方法表