

Code Soil
CODE SOIL - コードソイル [by Smalldust]
There are 37 Posts and 21 Comments so far.
Subscribe to Posts (Powered by feedburner)


CODE SOIL - コードソイル [by Smalldust]
There are 37 Posts and 21 Comments so far.
Subscribe to Posts (Powered by feedburner)
关于Windows的License形态,除了市面上盒包装出售的“零售版”(Retail)、随着新电脑捆绑出售的OEM License,还有Volume License等等。不过RDL License倒是很少听说,这里就简单介绍一下。
首先,RDL是“Remote Desktop Licensing”或“Remote Desktop License”的缩写,是用在WIndows XP上的一种附加License形态。基本上一般的用户不会有机会和这种License Mode打交道,因为RDL一般用在Blade PC或者是VDI之类的Thin Client系统当中。
用一句话来概括RDL的用途就是,当用户使用某种终端设备,通过Remote Desktop远程使用Windows XP PC的时候,不仅Windows XP PC本身需要License,用来远程访问的终端(或者用户)也需要一个License——这个License可以是一个普通的Windows XP License,也可以是一个Widows XP RDL。由于RDL比一般的Windows XP License要便宜得多,自然在那些非Windows XP的终端上(例如PDA,Thin Client Terminal)选择RDL是一个廉价的选择。

RDL还具体可分为Device RDL和User RDL。顾名思义,Device RDL就是每个Thin Client终端,无论有多少用户使用它,都要有一个RDL;而User RDL则是每个用户,无论它使用多少Thin Client终端,都需要一个RDL。这和微软一贯的License方针是很相似的,熟悉MS SQL Server的CAL的朋友肯定立刻就会联想起来。
如果您是Blade PC系统或者虚拟化系统的业内人士,可以参考微软的技术文章:
上述两篇文章均在Microsoft Product Licensing Web- RDL有直接链接可以下载。
1 comment smalldust | VDI & Virtualization | rdl, thin client
首先介绍一下什么是HBA。
这里所说的HBA,全称FC HBA,也就是Fibre Channel Host Bus Adapter。在FC网络中,主机(如服务器)需要和FC网络、FC存储设备(如SAN)连接时,需要使用一种接口卡,就如同连接以太网需要以太网卡一样。这种接口卡就叫做FC HBA,简称HBA
和以太网卡的MAC地址一样,HBA上也有独一无二的标识,这就是WWN(World Wide Name)。HBA上的WWN有两种:
WWN的长度为8bytes,用16进制表示并用冒号分隔。例如:50:06:04:81:D6:F3:45:42
0 comments smalldust | Server & Storage | fc, hba, storage, wwn
有时候在编程当中我们需要获取临时目录的路径。临时目录在DOS/Win9x时代一直是C:\Temp之类形式,以环境变量来指定;到了多用户的WinXP,虽然默认路径变成了C:\Documents and Settings\Username\Local Settings\Temp,但是仍然可以通过TMP或者TEMP环境变量来指定。因此我们首先可以通过直接读取环境变量的方式找到临时目录:
或者,
这时就有一个问题随之而来,万一TMP和TEMP两个环境变量的值不同时,应该以哪个为准呢?此外,万一两个变量都不存在时,应该以哪个目录为临时目录呢? 看来,这个方法不够“标准”。
实际上,Win32中关于文件系统的API里,已经为我们提供了这样一个API:
GetTempPath函数会按照如下的顺序确定临时目录:
1. The path specified by the TMP environment variable.
2. The path specified by the TEMP environment variable.
3. The path specified by the USERPROFILE environment variable.
4. The Windows directory.
而在.Net里面,对于这个API已经有了封装,这就是Path.GetTempPath()方法。因此在.Net当中获取临时目录时,只需用如下语句:
早在Java到来之际,程序员们都已逐渐接受并乐于接受String的这一特性:immutable。从C/C++转到.Net/C#的程序员们,在最初可能非常不适应把char[]和string分开对待,但是一旦习惯了就会觉得非常方便,尤其是直接以+进行连接,以及支持switch…case等。
这个时候问题来了,string真的是immutable的吗?
cbrumme的blog上给出了一个例子:
该程序的执行结果也许正在你的预料之中,输出的是类似MYCOMPUTERNAMElways immutable之类的字符串,也就是说原字符串的前面一部分被计算机名覆盖掉了。
对上面的程序,我们可以做出如下分析:
1,computerName和otherString 的文本相同,因此由于编译器的Interning的结果,二者其实指向同一个字符串,用Object.ReferenceEquals()可以验证其相等。
2,红色部分标出的Marshal指令,使得该string被marshal为一个unmanaged pointer(LPWSTR)传递给了GetComputerName函数;
3,GetComputerName函数直接改写了computerName指向的缓冲区,string的immutable特性即被破坏。
由此我们可以看到,在与Unamanaged代码进行交互操作时必须额外小心,因为从某种意义上来说Unmanaged代码权限更大,破坏力也就更大,也就更容易引起意想不到的问题。
那么,上面那段使用GetComputerName的代码中,对该函数的包装要如何改进呢?
首先,在使用一个API之前应该注意其各个参数的in, out性质,例如关于GetComputerName,MSDN上有如下一段:
很显然,lpBuffer应该是用来输出的缓冲区,因此不应该用string,而是用byte[],StringBuilder之类的类型与之对应;即便一定要用String,也绝对不能Marshal为LPWSTR/LPTSTR,而是Marshal为VBByRefStr,以确保Managed代码侧string的immutable性质。
此外,使用.Net中unsafe代码也可以打破String的immutable,这和使用Unmanaged代码的本质是相同的。
在C#中,用什么语句可以捕获所有的异常?——对于这个问题,很多人可能很习惯地用写出下面的程序:
这条语句的捕获对象是“System.Exception”。由于.Net中所有的异常必须直接或间接继承自这个类,因此理论上讲似乎是的确可以捕获所有异常的。但是仔细想想,真的没有办法抛出一个不是继承自System.Exception的对象吗?如果答案是可以的,那么显然这种异常就无法由上面的语句捕获。
抛出不是异常的异常……这种想起来都觉得不可能的事情,可能吗?事实上,关于这个问题.Net 1.x 和 2.0有一些细微差别,下面分开考虑。
■在.Net 1.x当中,抛出不继承自System.Exception的对象是可能的
例如,在C++当中,我们可以用throw "Error!"这样的语句,将一个字符串作为“异常”抛出;在IL当中,我们甚至可以用下面的方式,将任何对象作为异常抛出:
因此,在.Net 1.x当中,经常使用下面这种最保险的方式:
其中第二个catch的效果就是,拦截“不是Exception及其子类的异常”。
■在.Net 2.0中,默认所有异常都必须继承自System.Exception;亦可和1.x相同方式运作
在.Net2.0当中,为了确保跨语言的兼容性(在C#下无法throw一个字符串,在C++下则可以),CLR会自动将不是继承自System.Exception的异常包裹在RuntimeWrappedException对象中——例如,在C++中throw一个字符串,该字符串将被包裹起来,实际上抛出的则是一个RuntimeWrappedException。这样的结果就是,所有语言抛出的异常将都是System.Exception的子类了。
但是同时,为了保证和1.x版本的兼容性,.Net 2.0提供了RuntimeCompatibilityAttribute类,指定CLR不要对异常进行包装:
附:测试用程序
执行结果是,第一个异常将被catch(System.Exception ex){}捕获;第二个异常由于不是System.Exception的子类,将落到catch{}中。
但是如果把第一行的属性去掉,编译时将出现下面的警告:
执行的话,两个异常都将被catch(System.Exception ex){}捕获,看消息可知第二个异常的类型为RuntimeWrappedException。
习惯了.Net编程,尤其是以前几乎没有用过Basic,Pascal,C/C++等“较古老”的语言的程序员,遇到PInvoke,尤其是COM interoperation的时候,往往是一头雾水不得要领。相信在在将来,一方面是从C#,.Net以及Java开始学习编程的人会越来越多,一方面整个Windows也逐渐往Managed平台迁移,懂得如何直接同Win32API打交道的程序员就会越来越少(当然绝对数量肯定还是很多的,至少比用DDK写驱动程序的多吧……)
但是,对于.Net程序员来说,虽然不用直接使用C++调用API,但是PInvoke的知识还是必不可少的,对于Handle,Unmanaged Thread,COM等等最基本的东西仍然是必修课。
前一段写程序时遇到这么一个PInvoke的实际例子:
某个C#写的WindowsForm中,要对资源DLL进行读取(Resource DLL,Win32格式的只含有资源的DLL)。其中要用到这么一个API函数:
其中,ENUMRESTYPEPROC,其定义为:
其中的LPTSTR,在C#里应该怎么写呢?我当时想也没想,既然是LPTSTR (Long Pointer to String),那肯定就是字符串了。于是就有了:
结果,这段代码在执行的时候出错了——用Marshal.GetLastWin32Error()查看,发现是ERROR_NOACCESS(998)。说明,程序访问了禁止访问的地址空间。经测试,其他几个参数:hModule是正确无误的DLL库的Handle,最后的参数也被设置为NULL,那么原因只能是这个strType了。
再回过头来仔细想想……该参数的类型是LPTSTR,在Unicode环境下也即LPWSTR,也即WCHAR * ——没错啊,不就是字符串么?
但是我们仔细看一下MSDN的说明,就会发现,Windows提供了一些标准的资源类型;例如RT_ICON,RT_STRING,RT_VERSION,……仔细看这些类型的定义就会发现它们都是数字。
查阅了相关资料才明白,lpszType这个参数,在值小于某个数值(例如256)的情况下应该被当作数字来处理;而在大于这个数值时,则作为指针,其值是内存中的地址值,也即应该将其当作指针处理。这种情况下,显然就不能用.Net的String类型来一概而论。正确的做法是,使用IntPtr,如果判断其值不是在256以下,再将其Marshal为指向字符串的指针,获取字符串。
结论:在.Net中,数据类型是“强类型”的,不同类型之间无法被强制转换,因此保证了类型安全;但是在C/C++等语言为基础的Win32中,类型却是可以任意变换的,各种Object“一切皆指针”,你可以把一个int* 当作char*,可以把HGDI当作HMODULE……没有人来保证这种变换的安全性,但是也正因此带来了灵活性。Windows的各种数据类型当中,很多类型都是名称不同,但是实际上的数据结构是相同的(例如关于Handle,就有HWND,HMENU,HINSTANCE,HHOOK等等许多种)。相反,同一个数据类型,时而代表数值,时而代表指针的例子也不少见。因此在处理Win32API的时候,必须特别注意这些特点。
有时,我们需在我们的程序中模拟鼠标的移动、点击等动作。——比如,一个再现用户操作的宏,或者一个演示操作方法的Demo程序。那么,我们在.Net中如何实现呢?
.Net并没有提供改变鼠标指针位置、模拟点击操作的函数;但是Windows API提供了。其中一个是SetCursorPos,在.Net里调用形式为:
该函数可以改变鼠标指针的位置。其中X,Y是相对于屏幕左上角的绝对位置。另一个函数是mouse_event:
这个函数更强一些,不仅可以设置鼠标指针绝对的位置,而且可以以相对坐标来设置。另外,该函数还可以模拟鼠标左右键点击、鼠标滚轮操作等。其中的MouseEventFlag是一个基于uint类型的枚举,定义如下:
利用这些这些函数,再配合一些其他的API函数,就可以自动寻找窗口、控件,用程序对其进行鼠标点击、键盘输入等等了。
« Prev -