实现在XP中使用Remote Desktop时也能得到ClearType 效果

2023-11-06

转载请标明是引用于 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;
}


 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

实现在XP中使用Remote Desktop时也能得到ClearType 效果 的相关文章

  • 从空整数到逗号列表中的指针的转换

    我知道在我们的现代世界中 NULL 和 0 并不是指针操作的最佳实践 根据 cppreference 指针转换 空指针常量 参见 NULL 可以是 转换为任意指针类型 结果为空指针 该类型的值 这种转换 称为空指针转换 允许作为单个转换转换
  • 如何消除SQL中的NULL字段

    我正在为 SQL Server 2008 R2 开发 TSQL 查询 我正在尝试开发此查询来识别一条记录 客户 由于其中一些值为 NULL 因此我目前正在对大多数表执行 LEFT JOINS 但 LEFT JOIN 的问题是 现在我为某些客
  • 在 Windows Server 2008 上安装 Tomcat 7 作为服务

    我想将 tomcat v7 0 12 作为服务安装在 Windows 2008 Server 上 在tomcat页面上我发现本教程 http tomcat apache org tomcat 7 0 doc windows service
  • 为什么 is.null 对于不存在的列表元素返回 false?

    我正在尝试测试列表是否包含对象 github源码 https raw github com PecanProject pecan 073235d9117faf8719038e095ba589bdaca1e402 settings R rea
  • 我想向我的销售点的用户授予特权,但我不知道如何做,有什么建议吗?

    我有一个问题 我有一个用netBeans制作的销售点系统 2个用户可以在我的系统中注册 管理员 和 供应商 系统中有几个模块 包括 Inventario 模块允许您在数据库中输入 修改 删除和搜索文章 问题是我希望只有管理员有权限进入该模块
  • 从命令行将 java_opts 设置为 tomcat 服务

    我有一个在Windows上运行的tomcat7服务 通常为了配置 Java 选项 我会转到 Tomcat 7 0 bin tomcat7w exe 并在 java 选项卡中 在 java 选项中打印我想要的定义 例如 javaagent 我
  • java方法中的可选参数

    我想制作一个需要 1 个必需参数和 1 个可选参数的方法 但我发现如何制作一个可选数组 方法是在参数 int b 中制作 但这是一个数组 我想制作它只是这个值是 null 或用户输入它 我可以通过创建 2 个同名的方法来实现它 但一个具有单
  • 作为后台进程/服务运行命令

    我有一个 Shell 命令 我想在后台运行 并且我读到这可以通过添加后缀来完成 到导致它作为后台进程运行的命令 但我需要一些更多的功能 并且想知道如何去做 我希望每次系统重新启动时该命令都在后台启动并运行 我希望能够像人们一样在需要时启动和
  • 在C语言中,NULL指针和指向0的指针有区别吗?如果是这样,那又怎样?

    在C语言中 NULL指针和指向0的指针有什么区别 ISO IEC 9899 TC2 中规定6 3 2 3 Pointers 3 值为 0 的整型常量表达式 或这样的表达式 强制转换为 void 类型 称为空指针常量 55 如果 null 指
  • 如何从与桌面交互的应用程序与 Windows 服务进行通信?

    使用 Net 与服务交互的最佳方式是什么 即大多数托盘应用程序如何与其服务器通信 如果这个方法也是跨平台的 那就更好了 在 Mono 中工作 所以我猜远程处理已经过时了 Edit 忘了说了 我们仍然需要在现场支持 Windows 2000
  • Shell Linux:grep 带有 NULL 字符的精确句子

    我有一个像这样的文件 key 0value n akey 0value n key2 0value n 我必须创建一个以单词作为参数的脚本 我必须返回具有与参数完全相同的键的每一行 I tried grep aF key x0 但 grep
  • MVC 模型在 OnExecuted 操作过滤器中为 null ...或者设置模型的更优雅的方式?

    我有一个 ActionFilter 它覆盖了 OnActionExecuted 方法 在 POST 操作中 filterContext Controller ViewData Model 始终为 null 我确实发现下面的文章似乎在说它不应
  • C 中的 NULL 是否需要/定义为零?

    在我的 GCC 测试程序中 NULL 似乎为零 但维基百科说NULL只需要指向不可寻址的内存 有编译器做吗NULL非零 我很好奇是否if ptr NULL 是比更好的练习if ptr NULL is guaranteed to be zer
  • 如何从 tcl 列表中删除空元素

    你好 我有以下清单 设置 qprList 12345 12345
  • 如何在 jQuery 中检查 null 对象

    我正在使用 jQuery 我想检查页面中是否存在某个元素 我写了以下代码 但它不起作用 if btext i null alert btext i text btext i text Branch i 如何检查元素是否存在 检查jQuery
  • pandas 使用查询功能检查列是否为空

    我有 pandas 数据框 我想在它的查询函数上执行 isnull 或 not isnull 条件 如下所示 In 67 df data pd DataFrame a 1 20 None 40 50 In 68 df data Out 68
  • Android - 为服务实现startForeground?

    所以我不确定在哪里 如何实现此方法以使我的服务在前台运行 目前我在另一项活动中通过以下方式开始我的服务 Intent i new Intent context myService class context startService i 然
  • SQL - 用 varchar 替换 is null 整数

    我正在尝试用新的列替换列varchar如果 select 语句中存在空值 则为字符串 personid ISNULL personid no person 我不想更新它 只是在查询结果中将值显示为 无人 但我收到一条错误消息 将 varch
  • android widget 和 localservice 绑定

    我编写播放器 它的主要活动是运行本地服务 我找不到如何将本地服务绑定到小部件 当我尝试像在活动中一样绑定它时 它失败了 请帮助我 添加 1 何时可以联系后台服务 http www developer com ws data article
  • 如何在视图中调用 Grails 服务?

    简单的问题 我有一个服务类 比方说helpersService 和一个方法def constructURI params 如何从模板视图调用此方法 我尝试了以下代码但没有成功 img src 但我得到以下结果 No signature of

随机推荐