转载请标明是引用于 http://blog.csdn.net/chenyujing1234
欢迎大家提出意见,一起讨论!
源码请自行到参考文章中下载。
参考文章<<http://www.codeproject.com/Articles/20866/ClearType-over-Remote-Desktop-in-Windows-XP>>
一、介绍
我所感兴趣的有以下:
1)ClearType字体在XP系统中能光滑;
2)用远程桌面在家里面工作;
3)不运行在Vista;
总结成一句是: 通过在不运行Vista(如XP)系统中用RDP实现远程桌面,并且RDP的效果能达到ClearType的效果。
不幸的是当我们运行RDP来连接到你的XP机器时,用ClearType的字体光滑是无效的。我猜想XP在2001年刚出来时,微软做了决定要让RDP上的ClearType被禁止。
在2007年,然而,百万级带宽非常普遍了,这时不要用户使用ClearType的决定看起来就已经过时了。(虽然后定Vista系统中的RDP支持了ClearType,但XP还是没有)
通过WinDBG,我认为可以通过内核下的bytes来解决这些问题,我决定在XP下使用RDP上的ClearType功能。
二、应用程序概述
这里有两部分的问题:
1、内核驱动,它在win32k.sys中对一些bytes打补丁;
2、用户模式的应用程序,它载入驱动并告诉它运行和做Patch(打补丁);
内核的部分是容易的。基本上,在win32k.sys中有代码路径像这样的(多亏了WinDBG的反汇编)
bf811387 66393550399abf cmp word ptr [win32k!gProtocolType (bf9a3950)],si
bf81138e 0f85c2feffff jne win32k!LFONTOBJ::ppfeMapFont+0x77 (bf811256)
我发现在我的调试器,gProtocolType变量根据你在控制台或通过RDP访问机器是不一样的。
在控制器时,jne 分支 没有被包含;但RDP时它有。
简单的改变(且有一个是产生期望的结果)仅用来忽略jne组件。取而代之的nops意味着这个分支没有被跟从-----非RDP代码路径将一直是包含的一个。
程序的内核部分(RdpClearType.sys)简单地做了nop 的补丁。
然而,那里有一个警告:看起来好像每次登陆都获得win32k.sys的复制并映射到它的地址空间(在一个叫“Session Space”的空间)。当我安装驱动,并且无论我是
在开机时启动或是通过服务手去去启动它,它都不能工作。我猜是因为驱动没有被服务加载。
我的解决方案是创建一个用户模式的加载程序,它把进程分为两个步骤:
1、通过正常的SCM接口启动驱动(CreateService和StartService)。
在SCM的线程中我的驱动的DriverEntry代码将会被调用并执行它自己的登陆段。
这是为什么我的DriverEntry没有做真实的补丁的原因........它简单的创建了一个设备,此设备对用户模式通过调用\DosDevices\RdpClearType
. 是可见的。
SC_HANDLE hSvc = CreateService(
hScm,
L"RdpClearType",
L"RdpClearType",
SERVICE_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
driverPath.c_str(),
NULL,
NULL,
NULL,
NULL,
NULL
);
if (hSvc)
{
SERVICE_STATUS svcStatus = { 0 };
if (StartService(hSvc, 0, NULL))
{
do
{
Sleep(250);
if (!QueryServiceStatus(hSvc, &svcStatus))
{
ShowMessage(MAKE_STRING(L"RdpClearType: ERROR calling QueryServiceStatus(): " << GetLastError() << endl));
break;
}
}
while (svcStatus.dwCurrentState != SERVICE_RUNNING);
HANDLE hFile = CreateFileW(
L"\\\\.\\RdpClearType",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile == INVALID_HANDLE_VALUE)
{
ShowMessage(MAKE_STRING(L"RdpClearType: ERROR opening handle to RdpClearType device: " << GetLastError() << endl));
}
else
{
DWORD bytes = 0;
BOOL result = DeviceIoControl(
hFile,
0,
NULL,
0,
NULL,
0,
&bytes,
NULL
);
if (result)
{
ShowMessage(MAKE_STRING(L"RdpClearType: ClearType should now be working inside of Remote Desktop connections." << endl));
ValidateRect(NULL, NULL);
retval = 0;
}
else
{
ShowMessage(MAKE_STRING(L"RdpClearType: ERROR calling DeviceIoControl: " << GetLastError() << endl));
}
CloseHandle(hFile);
}
if (ControlService(hSvc, SERVICE_CONTROL_STOP, &svcStatus))
{
do
{
Sleep(250);
if (!QueryServiceStatus(hSvc, &svcStatus))
{
ShowMessage(MAKE_STRING(L"RdpClearType: ERROR calling QueryServiceStatus(): " << GetLastError() << endl));
break;
}
}
while (svcStatus.dwCurrentState != SERVICE_STOPPED);
}
else
{
ShowMessage(MAKE_STRING(L"RdpClearType: ERROR stopping service: " << GetLastError() << endl));
}
}
else
{
ShowMessage(MAKE_STRING(L"RdpClearType: ERROR starting service: " << GetLastError() << endl));
}
DeleteService(hSvc);
}
else
{
ShowMessage(MAKE_STRING(L"RdpClearType: ERROR creating service: " << GetLastError() << endl));
}
2、在驱动已经被加载并启动后,用户模式代码打开\DosDevices\RdpClearType设备并调用DeviceIoControl。
当正在服务这次调用时,驱动的代码是运行在我登陆段的我的线程里。在我的段空间里做补丁影响win32k.sys。
3、最后调用Window API ValidateRect引入口屏幕刷新。所有的字体现在是用ClearType来纹理了。
为了让以上容易实现,我嵌入RedpCleargType.sys驱动文件到RdpClearType.exe中的资源里。当运行RdpClearType.exe时,sys文件被解压到临时目录中。
wchar_t buffer[MAX_PATH];
ExpandEnvironmentStrings(L"%SystemDrive%", &buffer[0], sizeof(buffer) / sizeof(wchar_t));
wstring driverPath = &buffer[0];
driverPath.append(L"\\RdpClearType.sys");
DeleteFile(driverPath.c_str());
if (!SaveResource(IDR_BIN1, driverPath))
{
ShowMessage(MAKE_STRING(L"RdpClearType: ERROR extracting driver from exe" << endl));
return 1;
}
// This function will extract one of the embedded PE files in this
// executable image and save it to disk.
bool SaveResource(WORD resId, wstring destPath)
{
HRSRC hRes = FindResource(NULL, MAKEINTRESOURCE(resId), L"BIN");
HGLOBAL hLoadedRes = LoadResource(NULL, hRes);
char *pRes = static_cast<char*>(LockResource(hLoadedRes));
DWORD dwResSize = SizeofResource(NULL, hRes);
ofstream outstream(destPath.c_str(), ios::binary);
if (!outstream)
return false;
outstream.write(pRes, dwResSize);
outstream.close();
return true;
}
三、驱动程序概述
很多人下了工程后不知资源中的RDPClearType.sys哪里来的,其实它是由里面的Driver.c文件通过DDK环境编译得来的。
(VS2008+DDK的编译环境可参考我的文章<<Window XP驱动开发(十六) XP下新建驱动程序工程并编译的第二种方法>>)
1、DriverEntry只是建立设备和链接文件名并分配接口而已。
NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
UNICODE_STRING deviceNameUnicodeString;
UNICODE_STRING linkNameUnicodeString;
NTSTATUS retval = STATUS_SUCCESS;
int i;
pDriverObject->DriverUnload = OnUnload;
RtlInitUnicodeString(&deviceNameUnicodeString, deviceName);
RtlInitUnicodeString(&linkNameUnicodeString, linkName);
retval = IoCreateDevice(
pDriverObject,
0,
&deviceNameUnicodeString,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&rdpClearTypeDevice
);
if (retval == STATUS_SUCCESS)
{
DbgPrint("RdpClearType: device created\n");
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
pDriverObject->MajorFunction[i] = OnStubDispatch;
}
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = RdpClearTypePatch;
retval = IoCreateSymbolicLink(&linkNameUnicodeString, &deviceNameUnicodeString);
if (NT_SUCCESS(retval))
{
DbgPrint("RdpClearType: symbolic link created\n");
}
else
{
// Delete the device since the symbolic link didn't take
IoDeleteDevice(rdpClearTypeDevice);
DbgPrint("RdpClearType: device deleted\n");
rdpClearTypeDevice = NULL;
}
}
else
{
DbgPrint("RdpClearType: ERROR creating device: %x\n", retval);
rdpClearTypeDevice = NULL;
}
DbgPrint("RdpClearType: DriverEntry done\n");
return retval;
}
2、给实现ClearType打补丁
它是所有工作的重点,也是实现的难点。(说句心里话真佩服老外的能力,在驱动里加入汇编)
简单地讲是从5个备选的内存中选择1个存在Byte 的地方,并对它填充nop
NTSTATUS RdpClearTypePatch(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
{
int i = 0;
char *pPatch1 = (char*)0xBF810BD6;
char *pPatch2 = (char*)0xBF81138E;
char *pPatch3 = (char*)0xBF810C0E;
char *pPatch4 = (char*)0xBF810BF6;
char *pPatch5 = (char*)0xBF811251;
char *pPatch = 0;
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
DbgPrint("RdpClearType: patch starting\n");
if (CheckExistingBytes(pPatch1))
pPatch = pPatch1;
else if (CheckExistingBytes(pPatch2))
pPatch = pPatch2;
else if (CheckExistingBytes(pPatch3))
pPatch = pPatch3;
else if (CheckExistingBytes(pPatch4))
pPatch = pPatch4;
else if (CheckExistingBytesAlt1(pPatch5))
pPatch = pPatch5;
if (pPatch)
{
DbgPrint("RdpClearType: applying patch at %08x\n", (int)pPatch);
__asm
{
push eax
mov eax, CR0
and eax, 0FFFEFFFFh
mov CR0, eax
pop eax
}
for (i = 0; i < 6; i++)
{
*pPatch = (char)0x90; // nop
pPatch++;
}
__asm
{
push eax
mov eax, CR0
or eax, NOT 0FFFEFFFFh
mov CR0, eax
pop eax
}
DbgPrint("RdpClearType: patch has been applied\n");
}
else
{
DbgPrint("RdpClearType: didn't find expected bytes in the any target addresses\n");
}
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}