Grafiktreiber auslesen und eigene Grafik-Schnittstelle schreiben

Technipion

Erfahrenes Mitglied
Hallo allerseits,
ich bin neu hier im Forum und ich habe ein Problem: Ich programmiere seit ein paar Jahren in meiner Freizeit etwas C++ aber jetzt habe ich mir mal vorgenommen eine eigene Grafikschnittstelle zu schreiben. Ich weiß das ist ein großes Projekt aber machbar wenn man halt ein paar Wochenenden bis nachts vorm PC hockt...

Meine Frage war jetzt: Weil ich weder auf DirectX, OpenGL, CUDA, o.a. zurückgreifen möchte wollte ich gerne direkt die Funktionen des Grafiktreibers benutzen. Eigentlich müsste man doch einfach die Treiber Dll "anzapfen" können und dann mit den gleichen Funktionen wie das Betriebssystem oder andere Schnittstellen direkt die GPU in den virtuellen Speicher schreiben lassen, oder? Kennt sich da jemand von euch aus?
Kann man Treiber da "anzapfen"? Oder wenigstens herausfinden welche libs z.B. DirectX benutzt und dazu noch die passenden Funktionsnamen?
Wäre echt super wenn mir jemand helfen würde.
 
Ich denke was relevant ist zu erwähnen ist, dass nicht DirectX die Funktionen des Graphikkartentreibers implementiert, sondern dass DirectX Anforderungen an den Graphikkartentreiber stellt, welche Funktionen er zu implementieren hat. DirectX fordert also gewisse Funktionen und die Graphikkartenhersteller (oder ein unabhängiger Treiberentwickler) müssen diese dann implementieren. So wird IDirect3DDevice9::SetIndices im Endeffekt einfach die Funktion SetIndices, die der Treiber exportiert aufrufen, da dies einer der Funktionen ist, die ein Treiber implementieren muss um DirectX zu unterstützen.

Wenn du also deine wirklich eigene Schnittstelle möchtest müsstest du dafür sorgen, dass die Graphikkartenhersteller in ihren Treibern die Funktionen implementieren, die du benötigst. Wenn du die DirectX-Funktionen des Treibers ohne DirectX verwenden möchtest solltest du dir als erstes mal das Windows DDK herunterladen, damit du all die low level Sachen in einem Header hast und nicht selbst definieren musst. Anschliessend benötigst du den Pfad zum aktuellen user mode display driver (UMD). Dafür exportiert die GDI32.dll die Funktionen D3DKMTOpenAdapterFromHdc und D3DKMTQueryAdapterInfo mit KMTQAITYPE_UMDRIVERNAME. Du kannst via CreateDC und dem Adapternamen den DC erstellen um mit D3DKMTOpenAdapterFromHdc dann den HANDLE für den Adapter bekommen.

In Code sieht das dann so aus (includepfade musst du halt entsprechend anpassen):
C++:
#include <windows.h>
#include <dx/d3d9.h>
#include <api/d3dumddi.h>
#include <api/d3dkmthk.h>

int main()
{
	D3DKMT_UMDFILENAMEINFO kmInfo = 
	{
		KMTUMDVERSION_DX9,
		0
	};

	DISPLAY_DEVICE ddev;
	ddev.cb = sizeof(ddev);
	// Device Nummer 2 ist bei mir die richtige Graphikkarte
	EnumDisplayDevices(NULL, 2, &ddev, 0);

	HDC hDc = CreateDC(NULL, ddev.DeviceName, NULL, NULL);
	// Fehlerbehandlung

	D3DKMT_OPENADAPTERFROMHDC openHdc = 
	{
		hDc,
		0,
		0,
		0
	};

	NTSTATUS status = D3DKMTOpenAdapterFromHdc(&openHdc);
	// Fehlerbehandlung
	DeleteDC(hDc);

	D3DKMT_QUERYADAPTERINFO info =
	{
		openHdc.hAdapter,
		KMTQAITYPE_UMDRIVERNAME,
		&kmInfo,
		sizeof(kmInfo)
	};

	status = D3DKMTQueryAdapterInfo(&info);
	// Fehlerbehandlung
	HMODULE hDriver = LoadLibraryW(kmInfo.UmdFileName);
}

Nachdem du den UMD geladen hast kannst du die Funktion OpenAdapter verwenden um eine Liste von Funktionen zu erhalten, die du dann weiter verwenden kannst. Das ist relativ umständlich mit den Callbacks und den teilweise inkompatiblen Typen aber danach hast du eine Liste mit Funktionen, die unter anderem CreateDevice beinhält. Da erhälst du dann wiederum eine lange Liste mit Funktionen wie SetIndices, DrawPrimitive, usw (siehe hier). Dann bist du ready um mit der Graphikkarte zu kommunizieren. Bis und mit OpenAdapter sieht das dann so aus:
C++:
#include <windows.h>
#include <dx/d3d9.h>
#include <api/d3dumddi.h>
#include <api/d3dkmthk.h>

HRESULT APIENTRY CALLBACK QueryAdapterInfoCallback(
  __in     HANDLE hAdapter,
  __inout  const D3DDDICB_QUERYADAPTERINFO *pData
)
{
	D3DKMT_QUERYADAPTERINFO info =
	{
		(D3DKMT_HANDLE)hAdapter,
		KMTQAITYPE_UMDRIVERPRIVATE,
		pData->pPrivateDriverData,
		pData->PrivateDriverDataSize
	};

	NTSTATUS status = D3DKMTQueryAdapterInfo(&info);
	return (status >= 0) ? S_OK : E_FAIL;
}

HRESULT APIENTRY CALLBACK GetMultisampleMethodListCallback(
  __in     HANDLE hAdapter,
  __inout  D3DDDICB_GETMULTISAMPLEMETHODLIST *pData
)
{
	D3DKMT_MULTISAMPLEMETHOD* pMethods = new D3DKMT_MULTISAMPLEMETHOD[pData->MethodCount];
	D3DKMT_GETMULTISAMPLEMETHODLIST list = 
	{
		(D3DKMT_HANDLE)hAdapter,
		pData->VidPnSourceId,
		pData->Width,
		pData->Height,
		pData->Format,
		pMethods,
		pData->MethodCount,
	};

	NTSTATUS ret = D3DKMTGetMultisampleMethodList(&list);
	
	if(BCRYPT_SUCCESS(ret))
	{
		for(int i = 0; i < pData->MethodCount; ++i)
		{
			pData->pMethodList[i].NumQualityLevels = pMethods[i].NumQualityLevels;
			pData->pMethodList[i].NumSamples = pMethods[i].NumSamples;
		}
	}

	delete [] pMethods;
	return (ret >= 0) ? S_OK : E_FAIL;
}

int main()
{
	D3DKMT_UMDFILENAMEINFO kmInfo = 
	{
		KMTUMDVERSION_DX9,
		0
	};

	DISPLAY_DEVICE ddev;
	ddev.cb = sizeof(ddev);
	// Device Nummer 2 ist bei mir die Graphikkarte
	EnumDisplayDevices(NULL, 2, &ddev, 0);

	HDC hDc = CreateDC(NULL, ddev.DeviceName, NULL, NULL);
	// Fehlerbehandlung

	D3DKMT_OPENADAPTERFROMHDC openHdc = 
	{
		hDc,
		0,
		0,
		0
	};

	NTSTATUS status = D3DKMTOpenAdapterFromHdc(&openHdc);
	// Fehlerbehandlung
	DeleteDC(hDc);

	D3DKMT_QUERYADAPTERINFO info =
	{
		openHdc.hAdapter,
		KMTQAITYPE_UMDRIVERNAME,
		&kmInfo,
		sizeof(kmInfo)
	};

	status = D3DKMTQueryAdapterInfo(&info);
	// Fehlerbehandlung
	HMODULE hDriver = LoadLibraryW(kmInfo.UmdFileName);
	PFND3DDDI_OPENADAPTER pfnOpenAdapter = (PFND3DDDI_OPENADAPTER)GetProcAddress(hDriver, "OpenAdapter");
	// Fehlerbehandlung

	D3DDDI_ADAPTERCALLBACKS adapterCallbacks = 
	{
		QueryAdapterInfoCallback,
		GetMultisampleMethodListCallback
	};

	D3DDDI_ADAPTERFUNCS adapterFunctions = { 0 };

	D3DDDIARG_OPENADAPTER argOpenAdapter =
	{
		(HANDLE)openHdc.hAdapter,
		9,
		32,
		&adapterCallbacks,
		&adapterFunctions,
		0
	};

	HRESULT hRes = pfnOpenAdapter(&argOpenAdapter);
	// Fehlerbehandlung
}

Gruss
Muepe
 
Zuletzt bearbeitet:
Wow vielen Dank,
du scheinst dich wirklich gut auszukennen...
Ich guck mal ob ich das so in etwa hinkriege, sieht halt schon ziemlich aufwendig aus... aber danke für die step-by-step anleitung.
Ich denke damit wären dann auch alle Fragen beantwortet.
 
Wenn du ganz auf die DirectX Runtime verzichten möchtest müsstest du dann auch teilweise die D3DKMTXxxxxx Funktionen selber implementieren, da diese manchmal nichts weiter machen als einen System Call in den DirectX Kernel Mode Treiber auszuführen. Da ich gerade schon bisschen mit dem WDK wieder bisschen rumgespielt hatte für den Code oben hat mich die Lust dazu bisschen gepackt und ich habe mal den Treiber geschrieben, der Teile der Funktionalitäten von D3DKMTQueryAdapterInfo bereit stellt (z.B. den UMD-Treiber abfragen). Wenn ich wieder zu Hause bin und die letzten kleinen Fehler aus dem Code entfernt habe kann ich dir das mal als Beispiel zeigen, vielleicht packt dich ja auch das Interesse daran ;)
 
Ja also ich habe Interesse an dem Thema.
Ich meine eine neue Grafikschnittstelle würde im Endeffekt eine Menge komfort mit sich bringen. Und wenn das möglich ist, wie du schon geschrieben hast, völlig auf die DirectX Runtime zu verzichten und trotzdem ne halbwegs gute Grafikausgabe hinzubekommen wäre natürlich fantastisch.
Wenn du mir mal das Beispiel zeigen würdest... wäre ich dankbar für...
;-D
 
Eigentlich schreibt man ja die Treiber meistens in C, da die C++ Runtime wenn möglich nicht geladen werden kann/soll. Zudem lässt sich natürlich darüber streiten, ob der opake Zeiger, der bei DeviceIOControl für IOCTL_OPEN_ADAPTER zurückgegeben wird ideal ist, da er die Adresse des vom Treiber allozierten Speichers beinhält, aber vom Prinzip her sollte er zeigen, wohin es geht. Es ist klar, dass es sich beim Auslesen des UMD um eine einfache Aufgabe handelt und die Implementation von Funktionen wie D3DKMTPresent deutlich komplexer und schwerer wird schon alleine daher, weil praktisch 0 Dokumentation davon vorhanden ist.

Der Hauptcode des Treibers sieht folgendermassen aus:
C++:
#include "Common.h"
#include "VideoDevice.h"

DRIVER_DISPATCH UnsupportedFunction;
DRIVER_DISPATCH IOControlFunction;

NTSTATUS UnsupportedFunction(PDEVICE_OBJECT devObj, PIRP irp)
{
	return STATUS_NOT_SUPPORTED;
}

__drv_dispatchType(IRP_MJ_DEVICE_CONTROL)
NTSTATUS IOControlFunction(PDEVICE_OBJECT devObj, PIRP irp)
{
	DbgPrint(__FUNCTION__);
	PIO_STACK_LOCATION loc = IoGetCurrentIrpStackLocation(irp);
	irp->IoStatus.Status = (NTSTATUS)0xC0000001;
	irp->IoStatus.Information = 0;

	if(loc == NULL)
	{
		IoCompleteRequest(irp, IO_NO_INCREMENT);
		return (NTSTATUS)0xC0000001;
	}

	if(!VideoDevice::IsValidIOCode(loc->Parameters.DeviceIoControl.IoControlCode))
	{
		IoCompleteRequest(irp, IO_NO_INCREMENT);
		return (NTSTATUS)0xC0000001;
	}

	ULONG numRet = 0;

	if(!VideoDevice::HandleIOControl(
		loc->Parameters.DeviceIoControl.IoControlCode,
		irp->AssociatedIrp.SystemBuffer,
		loc->Parameters.DeviceIoControl.InputBufferLength,
		irp->AssociatedIrp.SystemBuffer,
		loc->Parameters.DeviceIoControl.OutputBufferLength,
		&numRet
		))
	{
		irp->IoStatus.Status = (NTSTATUS)0xC0000001;
		irp->IoStatus.Information = 0;
		IoCompleteRequest(irp, IO_NO_INCREMENT);
		return STATUS_UNSUCCESSFUL;
	}
	else
	{
		irp->IoStatus.Status = STATUS_SUCCESS;
		irp->IoStatus.Information = numRet;
		IoCompleteRequest(irp, IO_NO_INCREMENT);
		return STATUS_SUCCESS;
	}
}

DRIVER_UNLOAD UnloadDriver;

void UnloadDriver(PDRIVER_OBJECT devObj)
{
	UNICODE_STRING dosDeviceName;
	RtlInitUnicodeString(&dosDeviceName, L"\\DosDevices\\GraphicsAdapter"); 

	IoDeleteSymbolicLink(&dosDeviceName);
	IoDeleteDevice(devObj->DeviceObject);
}

extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
	NTSTATUS NtStatus = STATUS_SUCCESS;
	unsigned int uiIndex = 0;
	PDEVICE_OBJECT pDeviceObject = NULL;
	UNICODE_STRING driverName, dosDeviceName;

	DbgPrint("Driver started => DriverEntry\n");

	RtlInitUnicodeString(&driverName, L"\\Device\\GraphicsAdapter");
	RtlInitUnicodeString(&dosDeviceName, L"\\DosDevices\\GraphicsAdapter"); 

	NtStatus = IoCreateDevice(pDriverObject, 0,
								&driverName, 
								FILE_DEVICE_UNKNOWN,
								FILE_DEVICE_SECURE_OPEN, 
								FALSE, &pDeviceObject);

	for(uiIndex = 0; uiIndex < IRP_MJ_MAXIMUM_FUNCTION; ++uiIndex)
		pDriverObject->MajorFunction[uiIndex] = UnsupportedFunction;

	pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IOControlFunction;
	pDriverObject->DriverUnload = UnloadDriver;

	pDeviceObject->Flags |= DO_BUFFERED_IO;
	pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

	IoCreateSymbolicLink(&dosDeviceName, &driverName);

	return STATUS_SUCCESS;
}

Wie man sehen kann werden alle Aktionen aus dem User Mode über DeviceIOControl abgewickelt mit dem entsprechenden IOCTL-Code. VideoDevice::HandleIOControl wird nur aufgerufen für control codes, die METHOD_BUFFERED verwenden, dafür sorgt VideoDevice::IsValidIOControl. Daher können auch die Parameter immer gleich gewählt werden. Der Rest ist einfach Standard für die Treiberinitialisierung.

Die Common.h beinhält dabei nur die entsprechenden statischen Includes:
C++:
#pragma once

#define _AMD64_
#include <ntifs.h>
#include <Ntstrsafe.h>

typedef unsigned __int32 uint32;

Wie du siehst ist mein Treiber für 64 Bit kompiliert (AMD64). Die VideoDevice.h beinhält die Definition der Klasse VideoDevice und die Control Codes (die alle Buffered I/O verwenden):
C++:
#pragma once

enum VideoControlCodes
{
	IOCTL_OPEN_ADAPTER			= CTL_CODE(FILE_DEVICE_UNKNOWN, 3000, METHOD_BUFFERED, FILE_ANY_ACCESS),
	IOCTL_GET_UMD_DRIVER_NAME	= CTL_CODE(FILE_DEVICE_UNKNOWN, 3001, METHOD_BUFFERED, FILE_ANY_ACCESS),
	IOCTL_CLOSE_ADAPTER			= CTL_CODE(FILE_DEVICE_UNKNOWN, 3002, METHOD_BUFFERED, FILE_ANY_ACCESS),
};

class VideoDevice
{
	ULONG m_signature;
	UNICODE_STRING m_deviceName;
	PFILE_OBJECT m_fileObj;
	PDEVICE_OBJECT m_devObj, m_pdo;
	HANDLE m_hDrvReg;
	NTSTATUS m_lastActionStatus;
	bool OpenDevice();
	bool LoadPhysicalDevice();

	static bool HandleGetUMDName(PVOID in, ULONG inLen, PVOID out, ULONG outLen, PULONG bytesReturned);
	static bool HandleOpenAdapter(PVOID in, ULONG inLen, PVOID out, ULONG outLen, PULONG bytesReturned);
	static bool HandleCloseAdapter(PVOID in, ULONG inLen, PVOID out, ULONG outLen, PULONG bytesReturned);
public:
	bool Constructor(uint32 num);
	void Destructor();

	bool LoadDriverRegistry();
	bool QueryDriverRegistryKey(WCHAR* keyName, PUNICODE_STRING dstValue);
	NTSTATUS GetLastStatus() { return m_lastActionStatus; }
	void SetLastStatus(NTSTATUS status) { m_lastActionStatus = status; }

	static bool HandleIOControl(ULONG ioCode, PVOID inBuffer, ULONG inLen, PVOID outBuffer, ULONG outLen, PULONG bytesReturned);
	static bool IsValidIOCode(ULONG ioCode);
};

Der relevante Code ist dann in der VideoDevice.cpp, da hab ich auch bisschen Kommentare eingefügt, was die Funktionen machen:
C++:
#include "Common.h"
#include "VideoDevice.h"

// Ist ein Ersatz für den richtigen Konstruktor VideoDevice::VideoDevice, der nie aufgerufen
// wird, da wir keine C++-Runtime haben und daher der bekannte operator new schlicht nicht
// existiert, der dann den Konstruktor normalerweise aufruft. Ist aber nicht weiter tragisch
// da dieser ja auch nur eine simple Funktion ist wie diese hier.
bool VideoDevice::Constructor(uint32 id)
{
	DbgPrint(__FUNCTION__);
	m_signature = 'VDDV';
	m_pdo = NULL;
	m_devObj = NULL;
	m_fileObj = NULL;
	m_hDrvReg = NULL;
	m_lastActionStatus = STATUS_SUCCESS;
	WCHAR devString[50] = { 0 };
	RtlStringCchPrintfW(devString, 50, L"\\Device\\Video%u", id);
	RtlInitUnicodeString(&m_deviceName, devString);
	return OpenDevice();
}

// Siehe Kommentar von VideoDevice::Constructur. Gilt auch hier, da delete fehlt
void VideoDevice::Destructor()
{
	DbgPrint(__FUNCTION__);
	if(m_fileObj != NULL)
		ObDereferenceObject(m_fileObj);
}

// Versucht einen Zeiger auf das entsprechende Videodevice zu erhalten mit Read access.
// Falls das möglich war wird das physikalische Device Object (PDO) gesucht, dass wir
// für alle späteren Funktionen brauchen.
bool VideoDevice::OpenDevice()
{
	DbgPrint(__FUNCTION__);
	NTSTATUS status = IoGetDeviceObjectPointer(&m_deviceName, FILE_READ_DATA, &m_fileObj, &m_devObj);
	if(NT_SUCCESS(status) == FALSE)
	{
		SetLastStatus(status);
		return false;
	}

	return LoadPhysicalDevice();
}

// Sucht das PDO mit der major function IRP_MJ_PNP und der minor function
// IRP_MN_QUERY_DEVICE_RELATIONS. Wenn das Video device dies nicht unterstützt
// funktioniert das ganze entsprechend nicht.
bool VideoDevice::LoadPhysicalDevice()
{
	DbgPrint(__FUNCTION__);
	KEVENT hEvent;
	IO_STATUS_BLOCK ioStatus;
	KeInitializeEvent(&hEvent, NotificationEvent, FALSE);
	PIRP irp = IoBuildSynchronousFsdRequest(
		IRP_MJ_PNP,
		m_devObj,
		NULL,
		0,
		NULL,
		&hEvent,
		&ioStatus
	);

	if(irp == NULL)
	{
		SetLastStatus(STATUS_DATA_ERROR);
		return false;
	}

	PIO_STACK_LOCATION irpLoc;
	irpLoc = IoGetNextIrpStackLocation(irp);
	irpLoc->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
	irpLoc->MajorFunction = IRP_MJ_PNP;
	irpLoc->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
	irp->IoStatus.Status = STATUS_NOT_SUPPORTED;

	NTSTATUS stat = IofCallDriver(m_devObj, irp);
	if(stat == STATUS_PENDING)
	{
		KeWaitForSingleObject(&hEvent, Executive, KernelMode, FALSE, NULL);
		stat = ioStatus.Status;
	}

	if(NT_SUCCESS(stat) == FALSE)
	{
		SetLastStatus(stat);
		return false;
	}

	PDEVICE_RELATIONS rel = (PDEVICE_RELATIONS)ioStatus.Information;
	if(rel == NULL || rel->Count == 0)
	{
		SetLastStatus(STATUS_DATA_ERROR);
		return false;
	}

	m_pdo = rel->Objects[0];
	ExFreePool(rel);
	SetLastStatus(STATUS_SUCCESS);
	return true;
}

// Jeder Treiber für jedes Device für das er verwendet wird kann einen Pfad in der 
// Registry haben in dem er seine Daten speichert. Dies ist in der Regel dann in
// HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Video\{TreiberGUID}\DeviceID\
// IoOpenDeviceRegistryKey gibt einen HANDLE zurück, der den entsprechenden Pfad 
// geöffnet hat.
bool VideoDevice::LoadDriverRegistry()
{
	DbgPrint(__FUNCTION__);
	if(m_hDrvReg != NULL)
		ZwClose(m_hDrvReg);

	m_hDrvReg = NULL;
	NTSTATUS status = IoOpenDeviceRegistryKey(m_pdo, PLUGPLAY_REGKEY_DRIVER, KEY_READ, &m_hDrvReg);
	SetLastStatus(status);
	return NT_SUCCESS(status);
}

// Versucht einen __STRING__ Wert aus dem Registryset des Devicetreibers zu laden.
bool VideoDevice::QueryDriverRegistryKey(WCHAR* keyName, PUNICODE_STRING dstString)
{
	DbgPrint(__FUNCTION__);
	if(m_hDrvReg == NULL || dstString == NULL || keyName == NULL)
	{
		SetLastStatus(STATUS_INVALID_PARAMETER);
		return false;
	}

	UNICODE_STRING keyString;
	RtlInitUnicodeString(&keyString, keyName);

	ULONG fullLen = 0;
	NTSTATUS status = ZwQueryValueKey(m_hDrvReg, &keyString, KeyValuePartialInformation, NULL, 0, &fullLen);
	if((status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) || fullLen == 0)
	{
		SetLastStatus(status);
		return false;
	}

	PKEY_VALUE_PARTIAL_INFORMATION vpi = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, fullLen, 'VDDC');
	if(vpi == NULL)
	{
		SetLastStatus(STATUS_NO_MEMORY);
		return false;
	}

	status = ZwQueryValueKey(m_hDrvReg, &keyString, KeyValuePartialInformation, vpi, fullLen, &fullLen);
	if(NT_SUCCESS(status) == false || vpi->DataLength == 0)
	{
		SetLastStatus(status);
		ExFreePool(vpi);
		return false;
	}

	if(vpi->Type != REG_SZ && vpi->Type != REG_MULTI_SZ && vpi->Type != REG_EXPAND_SZ)
	{
		SetLastStatus(STATUS_DATA_ERROR);
		ExFreePool(vpi);
		return false;
	}

	RtlInitUnicodeString(&keyString, (WCHAR*)vpi->Data);
	dstString->Length = (vpi->DataLength - sizeof(WCHAR)) / sizeof(WCHAR);
	dstString->MaximumLength = dstString->Length + 1;
	dstString->Buffer = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, dstString->MaximumLength * sizeof(WCHAR), 'VDDC');
	RtlCopyUnicodeString(dstString, &keyString);
	ExFreePool(vpi);
	SetLastStatus(status);
	return true;
}

// Ruft eine für den I/O-Control passende Funktion auf
bool VideoDevice::HandleIOControl(ULONG ioCode, PVOID inBuffer, ULONG inLen, PVOID outBuffer, ULONG outLen, PULONG bytesReturned)
{
	DbgPrint(__FUNCTION__);
	switch(ioCode)
	{
	case IOCTL_GET_UMD_DRIVER_NAME:
		{
			return VideoDevice::HandleGetUMDName(inBuffer, inLen, outBuffer, outLen, bytesReturned);
		}

	case IOCTL_OPEN_ADAPTER:
		{
			return VideoDevice::HandleOpenAdapter(inBuffer, inLen, outBuffer, outLen, bytesReturned);
		}

	case IOCTL_CLOSE_ADAPTER:
		{
			return VideoDevice::HandleCloseAdapter(inBuffer, inLen, outBuffer, outLen, bytesReturned);
		}

	default:
		return false;
	}
}

bool VideoDevice::IsValidIOCode(ULONG code)
{
	return (code == IOCTL_GET_UMD_DRIVER_NAME || code == IOCTL_OPEN_ADAPTER || code == IOCTL_CLOSE_ADAPTER);
}

bool VideoDevice::HandleCloseAdapter(PVOID in, ULONG inLen, PVOID out, ULONG outLen, PULONG bytesReturned)
{
	DbgPrint(__FUNCTION__);
	*bytesReturned = 0;
	if(inLen < sizeof(HANDLE*) || in == NULL)
		return false;

	VideoDevice* dev = *(VideoDevice**)in;
	if(dev->m_signature != 'VDDV')
		return false;

	dev->Destructor();
	ExFreePool(dev);
	return true;
}

bool VideoDevice::HandleOpenAdapter(PVOID in, ULONG inLen, PVOID out, ULONG outLen, PULONG bytesReturned)
{
	DbgPrint(__FUNCTION__);
	*bytesReturned = 0;
	if(outLen < sizeof(HANDLE) || out == NULL)
		return false;

	VideoDevice* retValue = (VideoDevice*)ExAllocatePoolWithTag(NonPagedPool, sizeof(VideoDevice), 'VDDC');
	if(retValue == NULL)
	{
		*(HANDLE*)out = NULL;
		return false;
	}

	if(!retValue->Constructor(6))
	{
		*(HANDLE*)out = NULL;
		ExFreePool(retValue);
		return true;
	}

	*(HANDLE*)out = (HANDLE)retValue;
	*bytesReturned = sizeof(HANDLE);
	return true;
}

bool VideoDevice::HandleGetUMDName(PVOID in, ULONG inLen, PVOID out, ULONG outLen, PULONG bytesReturned)
{
	DbgPrint(__FUNCTION__);
	*bytesReturned = 0;
	if(inLen < sizeof(HANDLE*) || in == NULL || out == NULL || outLen == 0)
		return false;

	VideoDevice* dev = *(VideoDevice**)in;
	if(dev->m_signature != 'VDDV')
		return false;

	if(!dev->LoadDriverRegistry())
		return false;

	UNICODE_STRING strDriver;
	if(!dev->QueryDriverRegistryKey(L"UserModeDriverNameWoW", &strDriver))
		return false;

	RtlStringCchCopyNW((PWSTR)out, outLen, strDriver.Buffer, strDriver.MaximumLength);
	ExFreePool(strDriver.Buffer);

	*bytesReturned = outLen;
	return true;
}

Eine Usermode Applikation kann dann den Treiber folgendermassen verwendet (nachdem er installiert wurde):
C++:
#include <windows.h>
#include <iostream>

enum VideoControlCodes
{
	IOCTL_OPEN_ADAPTER			= CTL_CODE(FILE_DEVICE_UNKNOWN, 3000, METHOD_BUFFERED, FILE_ANY_ACCESS),
	IOCTL_GET_UMD_DRIVER_NAME	= CTL_CODE(FILE_DEVICE_UNKNOWN, 3001, METHOD_BUFFERED, FILE_ANY_ACCESS),
	IOCTL_CLOSE_ADAPTER			= CTL_CODE(FILE_DEVICE_UNKNOWN, 3002, METHOD_BUFFERED, FILE_ANY_ACCESS),
};

int main(void)
{
    HANDLE hFile = CreateFile("\\\\.\\GraphicsAdapter", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
	if(hFile != INVALID_HANDLE_VALUE)
	{
		HANDLE hAdapter = 0;
		DWORD bytesRet = 0;
		DeviceIoControl(hFile, IOCTL_OPEN_ADAPTER, NULL, 0, &hAdapter, sizeof(HANDLE*), &bytesRet, NULL);
		if(hAdapter != NULL)
		{
			WCHAR strUmd[MAX_PATH];
			BOOL ret = DeviceIoControl(hFile, IOCTL_GET_UMD_DRIVER_NAME, &hAdapter, sizeof(HANDLE*), strUmd, MAX_PATH, &bytesRet, NULL);
			if(ret == FALSE)
				std::cout << "Unable to get the user mode driver!" << std::endl;
			else
				std::wcout << L"UserModeDriver: " << strUmd << std::endl;
		}

		DeviceIoControl(hFile, IOCTL_CLOSE_ADAPTER, &hAdapter, sizeof(HANDLE*), NULL, 0, &bytesRet, NULL);
	}
	CloseHandle(hFile);
	std::cin.get();
}

Ein Anfang, und es stünde noch seeeehr viel vor dir um einen eigenen Treiber für den Part zu schreiben, den der Treiber nicht selber macht. Ein Teil der folgenden Funktionen müsstest du selber implementieren (wenn du richtig motiviert bist und drin bist gehen ja auch alle, wobei das dann redundant wäre, da sie schon im Treiber win32k.sys definiert sind). http://msdn.microsoft.com/en-us/library/ff544512(v=vs.85).aspx

Gruss
Muepe
 
Zuletzt bearbeitet:
Zurück