DotNet 异步Socket的实例

在非Dotnet环境,直接使用Winsock的COM空间作这块,而最初在.net作相关开发的时候为了省事,也这么作,但是后来发现COM存在相当多问题,于是只要去研究.net自带的Socket模式,之前一直想写这个程序,但是查阅很多资料,细节都不是很清晰,于是只好自己研究完成了,当然,这边用了许多偷懒的方法,于是还请各位多多指教。
程序是一个一个类的形式完成的。
代码如下:
首先StateObject类,是用作异步信息传递用的,

这边声明了事件,是为了切合原先Winsock的使用习惯(Oh My God…..)
过程Connect,实现主动连接的部分。

监听的处理,这部分是使用同步模式。

开始接受和处理数据的部分

终于要到发送数据了,当然,这边只考虑了发送文本,如果发送文件,直接用文件流写到NetStream的缓冲区,注意一下大小就可以了。

然后补上一些小细节的代码:

Live SDK,想说爱你不容易

都说比尔时代的微软不懂互联网,目前微软开始在互联网Live起来,也云起来(目前微软云系统CTP已经发布)开始学做web2.0的公司,也开始OPENAPI起来
于是目前微软的LIVE或多或少开发SDK,网络上也多出关于借此敲板腾迅的文章。当然,经过酱油党确认,恩,这真的“影响不大”,确实,微软与此相关的英文SDK在MSDN上可以找到了,当然,这是在微软多次修改地址,让我们碰上404之后找到,在LIVE ID和SEARCH上下足公夫,虽然不能说完美,但很便利的可以直接使用,对于MESSAGE部分,已经可以看到完善的SOAP,JS api已经2.5版了,MSDN重点都在提这个,而忽视了WINFORM开发者,虽然也有给CLASSLIBRARY,但SDK等相关文档确不多,当然也可能我没找到,然而相比之下,第三方开源的DotMsn虽然是基于微软协议的作品,但易用性上却很好,当然,本文仅代表个人愚见。LIVE&DOTNET路还很长,我们有理由相信,将来一切都会更好!

关于我与NBX

很久没更新了,最近确实很忙,于是还是抽空写些,加之之前看了“删除单纯”写的NB系列的历史的帖子,加之近期一些朋友问我一些问题,于是我就说说我的历史问题- –
部分内容会与之前所述之贴有所重复,但为了完整,我还是一一说来,orz。。
Begining
于是,这是一个夏天的故事。。。。这段时间发生两件大事
1、中中因为Code遗失问题,暂停卡查更新
也许这就是我开始关注游戏王相关开发的原因,其实很早就接触VB的我,一直没有相像的作品,长期涉及ASP开发也没有成熟,于是那段时间,我在信息组训练,某人让我做出了关于开发卡查的决定,也就是那时,MC在NW发布时用的名字,cx24,应该从初中到目前的高中,他都一直与我一起,玩牌,编程,测试软件,可以说,he is my best friend and partner,自然我第一进行数据库开放的作品,便是MiniCard。
2、冰伦NB的发布
也基本是在差不多的时候,在NW上我们注意到了NB,便下载下来玩,因为在同一个局域网,连接自然不是难事,但我们产生许多神奇的问题,他的卡片资料在哪里?他是如何对战的?于是最后测试得出结论,所有卡片效果都要手动实现。于是便与冰伦取得联系,合作开发,那时,为了方便对战时的卡组构成,我对 MiniCard进行改进,便提出了当时看似无聊,现在许多人都在做的——组卡,由于冰伦长期比较忙,因此NB的更新缓慢,所以我也专注与MC的设计和 NB的数据结构处理。当然,这也是我目前都热衷Socket模式对战的原因
PS:需要注意,当时NB的开发之初便是全场的,因此当初NB设计已经相当不简单了- –
暂时的离开
也就是在那时,MC的开发停止了3个月左右,因为中考,所以只能选择了离开,而就在这时,应该就是NBQ的时代,这段时间的故事,炎更了解,从他那听来许多,于是我只能简单说说
1、为什么有NBQ
NBQ是基于QQ进行信息传输的,这也是之前NBX的模式,他自然是由冰伦首先提出这个概念,但由于他的处理方式不够完善,因此稳定性不是很好。
2、DK与炎的加入
传说中的炎创建了这个团队,当然DK也基本随炎加入,而我的加入则是在中考结束之后了。当然,他们的加入给NBQ带来的影响就是稳定性更好(orz)界面更美观,当然,这也和炎用了另外一种(也就是目前NBX在用的)截取QQ消息的方式,当然,这次的更新,使得NBQ火爆起来,于是这个团队也便有所名目出来。
在工作室成立的日子里 Legend
关于NBQ全场版与NBX。
当然,此时开始对于NBQ就需要区分炎版和冰版了(因为冰版确实还在更新),当然炎的版本更为大家所知。
自然也是冰首先做出全场版,于是“NBQ全场版”就诞生了,但依然原先的处理方式的稳定性缘故,所以有些不便(当然目前稳定性好了许多)
NBX的计划来源与DK的NB,NBQ大综合的方案,当然,这个名字被炎采用,于是便产生了,炎的全场版NBX。于是很神奇的,原先的计划就那么被无视了- –

[Pascal]Move 在程序设计时的应用

System单元的move过程用来把指定内存段的数据整块复制到另一内存段中,它通常用来成批移动数组元素,使用move进行移动,往往比For一遍来得快些(个人感觉)Move的格式是 move(var Source, Dest; Count: Word)Source是数据源,Dest是目标内存段,Count是复制的字节数。你不必担心源内存段会和目标内存段重叠或者重合,在这种情况下move仍然能够正确工作,参考下面的样例:

非常感谢AI_Player介绍这种方法,copymem movemem在C语言中非常常见,其实其在Pascal中也很好用

var:

  I: Integer;

  P: array[0..9] of Longint;

begin

  for I := 0 to 9 do P[I] := I;

  move(P[4], P[2], 4 * 6);          P[2]6longint P[4]

  for I := 0 to 9 do Writeln(P[I]: 4);

0123456789 
0145678989

值得注意的是Count的大小,这与identifier的类型(所占字节数)有关,以下给出参考

byte,shortint

1

word,integer

2

longint

4

 

single

4

real

6

double,comp

8

extended

10

 

char

1

boolean

1

string

1(per char)*

\n

\n

* Var a:string  ''a:=''12345'';  move(a[4],a[1],2);  a="45345"

\n

 另外,所占字节数不同不能move(不是数组,Count选字节数大的,有时也是可以的),不同类型不能move,否则会搞得乱七八糟,而往往因move错误地使用造成的 答案错误、死循环 很难发现!

[常用API收藏] 注册表编辑

这几天整理注册表相关的,于是收藏了此模块,相当完善的编辑方法。
标准模块代码:

Option Explicit
Option Compare Text
'---------------------------------------------------------------
'- 注册表 API 声明...
'---------------------------------------------------------------
Private Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long
Private Declare Function RegCreateKeyEx Lib "advapi32.dll" Alias "RegCreateKeyExA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal Reserved As Long, ByVal lpClass As String, ByVal dwOptions As Long, ByVal samDesired As Long, lpSecurityAttributes As SECURITY_ATTRIBUTES, phkResult As Long, lpdwDisposition As Long) As Long
Private Declare Function RegDeleteKey Lib "advapi32.dll" Alias "RegDeleteKeyA" (ByVal hKey As Long, ByVal lpSubKey As String) As Long
Private Declare Function RegDeleteValue Lib "advapi32.dll" Alias "RegDeleteValueA" (ByVal hKey As Long, ByVal lpValueName As String) As Long
Private Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, ByVal samDesired As Long, phkResult As Long) As Long
Private Declare Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, lpData As Any, lpcbData As Long) As Long
Private Declare Function RegRestoreKey Lib "advapi32.dll" Alias "RegRestoreKeyA" (ByVal hKey As Long, ByVal lpFile As String, ByVal dwFlags As Long) As Long
Private Declare Function RegSaveKey Lib "advapi32.dll" Alias "RegSaveKeyA" (ByVal hKey As Long, ByVal lpFile As String, lpSecurityAttributes As SECURITY_ATTRIBUTES) As Long
Private Declare Function RegSetValueEx Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, lpData As Any, ByVal cbData As Long) As Long
Private Declare Function RegQueryInfoKey Lib "advapi32.dll" Alias "RegQueryInfoKeyA" (ByVal hKey As Long, ByVal lpClass As String, lpcbClass As Long, ByVal lpReserved As Long, lpcSubKeys As Long, lpcbMaxSubKeyLen As Long, lpcbMaxClassLen As Long, lpcValues As Long, lpcbMaxValueNameLen As Long, lpcbMaxValueLen As Long, lpcbSecurityDescriptor As Long, lpftLastWriteTime As FILETIME) As Long
Private Declare Function RegEnumValue Lib "advapi32.dll" Alias "RegEnumValueA" (ByVal hKey As Long, ByVal dwIndex As Long, ByVal lpValueName As String, lpcbValueName As Long, ByVal lpReserved As Long, lpType As Long, lpData As Byte, lpcbData As Long) As Long
Private Declare Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" (ByVal hKey As Long, ByVal dwIndex As Long, ByVal lpName As String, lpcbName As Long, ByVal lpReserved As Long, ByVal lpClass As String, lpcbClass As Long, lpftLastWriteTime As FILETIME) As Long
Private Declare Function AdjustTokenPrivileges Lib "advapi32.dll" (ByVal TokenHandle As Long, ByVal DisableAllPriv As Long, NewState As TOKEN_PRIVILEGES, ByVal BufferLength As Long, PreviousState As TOKEN_PRIVILEGES, ReturnLength As Long) As Long                'Used to adjust your program's security privileges, can't restore without it!
Private Declare Function LookupPrivilegeValue Lib "advapi32.dll" Alias "LookupPrivilegeValueA" (ByVal lpSystemName As Any, ByVal lpName As String, lpLuid As LUID) As Long          'Returns a valid LUID which is important when making security changes in NT.
Private Declare Function OpenProcessToken Lib "advapi32.dll" (ByVal ProcessHandle As Long, ByVal DesiredAccess As Long, TokenHandle As Long) As Long
Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
'---------------------------------------------------------------
'- 注册表 Api 常数...
'---------------------------------------------------------------
' 注册表创建类型值...
Const REG_OPTION_NON_VOLATILE = 0        ' 当系统重新启动时,关键字被保留
' 注册表关键字安全选项...
Const READ_CONTROL = &H20000
Const KEY_QUERY_VALUE = &H1
Const KEY_SET_VALUE = &H2
Const KEY_Create_SUB_KEY = &H4
Const KEY_ENUMERATE_SUB_KEYS = &H8
Const KEY_NOTIFY = &H10
Const KEY_Create_LINK = &H20
Const KEY_READ = KEY_QUERY_VALUE + KEY_ENUMERATE_SUB_KEYS + KEY_NOTIFY + READ_CONTROL
Const KEY_WRITE = KEY_SET_VALUE + KEY_Create_SUB_KEY + READ_CONTROL
Const KEY_EXECUTE = KEY_READ
Const KEY_ALL_ACCESS = KEY_QUERY_VALUE + KEY_SET_VALUE + KEY_Create_SUB_KEY + KEY_ENUMERATE_SUB_KEYS + KEY_NOTIFY + KEY_Create_LINK + READ_CONTROL
' 返回值...
Const ERROR_NONE = 0
Const ERROR_BADKEY = 2
Const ERROR_ACCESS_DENIED = 8
Const ERROR_SUCCESS = 0
' 有关导入/导出的常量
Const REG_FORCE_RESTORE As Long = 8&
Const TOKEN_QUERY As Long = &H8&
Const TOKEN_ADJUST_PRIVILEGES As Long = &H20&
Const SE_PRIVILEGE_ENABLED As Long = &H2
Const SE_RESTORE_NAME = "SeRestorePrivilege"
Const SE_BACKUP_NAME = "SeBackupPrivilege"
'---------------------------------------------------------------
'- 注册表类型...
'---------------------------------------------------------------
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Boolean
End Type
Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Private Type LUID
lowpart As Long
highpart As Long
End Type
Private Type LUID_AND_ATTRIBUTES
pLuid As LUID
Attributes As Long
End Type
Private Type TOKEN_PRIVILEGES
PrivilegeCount As Long
Privileges As LUID_AND_ATTRIBUTES
End Type
'---------------------------------------------------------------
'- 自定义枚举类型...
'---------------------------------------------------------------
' 注册表数据类型...
Public Enum ValueType
REG_SZ = 1                         ' 字符串值
REG_EXPAND_SZ = 2                  ' 可扩充字符串值
REG_BINARY = 3                     ' 二进制值
REG_DWORD = 4                      ' DWORD值
REG_MULTI_SZ = 7                   ' 多字符串值
End Enum
' 注册表关键字根类型...
Public Enum KeyRoot
HKEY_CLASSES_ROOT =\n&H80000000
HKEY_CURRENT_USER = &H80000001
HKEY_LOCAL_MACHINE = &H80000002
HKEY_USERS = &H80000003
HKEY_PERFORMANCE_DATA = &H80000004
HKEY_CURRENT_CONFIG = &H80000005
HKEY_DYN_DATA = &H80000006
End Enum
Private hKey As Long                   ' 注册表打开项的句柄
Private i As Long, j As Long           ' 循环变量
Private Success As Long                ' API函数的返回值, 判断函数调用是否成功
'-------------------------------------------------------------------------------------------------------------
'- 新建注册表关键字并设置注册表关键字的值...
'- 如果 ValueName 和 Value 都缺省, 则只新建 KeyName 空项, 无子键...
'- 如果只缺省 ValueName 则将设置指定 KeyName 的默认值
'- 参数说明: KeyRoot--根类型, KeyName--子项名称, ValueName--值项名称, Value--值项数据, ValueType--值项类型
'-------------------------------------------------------------------------------------------------------------
Public Function SetKeyValue(KeyRoot As KeyRoot, KeyName As String, Optional ValueName As String, Optional Value As Variant = "", Optional ValueType As ValueType = REG_SZ) As Boolean
Dim lpAttr As SECURITY_ATTRIBUTES                   ' 注册表安全类型
lpAttr.nLength = 50                                 ' 设置安全属性为缺省值...
lpAttr.lpSecurityDescriptor = 0                     ' ...
lpAttr.bInheritHandle = True                        ' ...
' 新建注册表关键字...
Success = RegCreateKeyEx(KeyRoot, KeyName, 0, ValueType, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, lpAttr, hKey, 0)
If Success <> ERROR_SUCCESS Then SetKeyValue = False: RegCloseKey hKey: Exit Function
' 设置注册表关键字的值...
If IsMissing(ValueName) = False Then
Select Case ValueType
Case REG_SZ, REG_EXPAND_SZ, REG_MULTI_SZ
Success = RegSetValueEx(hKey, ValueName, 0, ValueType, ByVal CStr(Value), LenB(StrConv(Value, vbFromUnicode)) + 1)
Case REG_DWORD
If CDbl(Value) < = 4294967295# And CDbl(Value) >= 0 Then
Dim sValue As String
sValue = DoubleToHex(Value)
Dim dValue(3) As Byte
dValue(0) = Format("&h" & Mid(sValue, 7, 2))
dValue(1) = Format("&h" & Mid(sValue, 5, 2))
dValue(2) = Format("&h" & Mid(sValue, 3, 2))
dValue(3) = Format("&h" & Mid(sValue, 1, 2))
Success = RegSetValueEx(hKey, ValueName, 0, ValueType, dValue(0), 4)
Else
Success = ERROR_BADKEY
End If
Case REG_BINARY
On Error Resume Next
Success = 1                             ' 假设调用API不成功(成功返回0)
ReDim tmpValue(UBound(Value)) As Byte
For i = 0 To UBound(tmpValue)
tmpValue(i) = Value(i)
Next i
Success = RegSetValueEx(hKey, ValueName, 0, ValueType, tmpValue(0), UBound(Value) + 1)
End Select
End If
If Success <> ERROR_SUCCESS Then SetKeyValue = False: RegCloseKey hKey: Exit Function
' 关闭注册表关键字...
RegCloseKey hKey
SetKeyValue = True                                       ' 返回函数值
End Function
'-------------------------------------------------------------------------------------------------------------
'- 获得已存在的注册表关键字的值...
'- 如果 ValueName="" 则返回 KeyName 项的默认值...
'- 如果指定的注册表关键字不存在, 则返回空串...
'- 参数说明: KeyRoot--根类型, KeyName--子项名称, ValueName--值项名称, ValueType--值项类型
'-------------------------------------------------------------------------------------------------------------
Public Function GetKeyValue(KeyRoot As KeyRoot, KeyName As String, ValueName As String, Optional ValueType As Long) As String
Dim TempValue As String      &nbs\np;                      ' 注册表关键字的临时值
Dim Value As String                                 ' 注册表关键字的值
Dim ValueSize As Long                               ' 注册表关键字的值的实际长度
TempValue = Space(1024)                             ' 存储注册表关键字的临时值的缓冲区
ValueSize = 1024                                    ' 设置注册表关键字的值的默认长度
' 打开一个已存在的注册表关键字...
RegOpenKeyEx KeyRoot, KeyName, 0, KEY_ALL_ACCESS, hKey
' 获得已打开的注册表关键字的值...
RegQueryValueEx hKey, ValueName, 0, ValueType, ByVal TempValue, ValueSize
' 返回注册表关键字的的值...
Select Case ValueType                                                        ' 通过判断关键字的类型, 进行处理
Case REG_SZ, REG_MULTI_SZ, REG_EXPAND_SZ
TempValue = Left$(TempValue, ValueSize - 1)                          ' 去掉TempValue尾部空格
Value = TempValue
Case REG_DWORD
ReDim dValue(3) As Byte
RegQueryValueEx hKey, ValueName, 0, REG_DWORD, dValue(0), ValueSize
For i = 3 To 0 Step -1
Value = Value + String(2 - Len(Hex(dValue(i))), "0") + Hex(dValue(i))   ' 生成长度为8的十六进制字符串
Next i
If CDbl("&H" & Value) < 0 Then                                              ' 将十六进制的 Value 转换为十进制
Value = 2 ^ 32 + CDbl("&H" & Value)
Else
Value = CDbl("&H" & Value)
End If
Case REG_BINARY
If ValueSize > 0 Then
ReDim bValue(ValueSize - 1) As Byte                                     ' 存储 REG_BINARY 值的临时数组
RegQueryValueEx hKey, ValueName, 0, REG_BINARY, bValue(0), ValueSize
For i = 0 To ValueSize - 1
Value = Value + String(2 - Len(Hex(bValue(i))), "0") + Hex(bValue(i)) + " "  ' 将数组转换成字符串
Next i
End If
End Select
' 关闭注册表关键字...
RegCloseKey hKey
GetKeyValue = Trim(Value)                                                    ' 返回函数值
End Function
'-------------------------------------------------------------------------------------------------------------
'- 删除已存在的注册表关键字的值...
'- 如果指定的注册表关键字不存在, 则不做任何操作...
'- 参数说明: KeyRoot--根类型, KeyName--子项名称, ValueName--值项名称
'-------------------------------------------------------------------------------------------------------------
Public Function DeleteKey(KeyRoot As KeyRoot, KeyName As String, Optional ValueName As String) As Boolean
Dim tmpKeyName As String                            ' 注册表关键字的临时子项名称
Dim tmpValueName As String                          ' 注册表关键字的临时子键名称
' 打开一个已存在的注册表关键字...
Success = RegOpenKeyEx(KeyRoot, KeyName, 0, KEY_ALL_ACCESS, hKey)
If Success <> ERROR_SUCCESS Then DeleteKey = False: RegCloseKey hKey: Exit Function
' 删除已打开的注册表关键字...
tmpKeyName = ""
tmpValueName = KeyName
If ValueName = "" Then                                    &\nnbsp;         ' 判断ValueName是否缺省, 如缺省作相应处理
If InStrRev(KeyName, "\\") > 1 Then
tmpValueName = Right(KeyName, InStrRev(KeyName, "\\") + 1)
tmpKeyName = Left(KeyName, InStrRev(KeyName, "\\") - 1)
End If
Success = RegOpenKeyEx(KeyRoot, tmpKeyName, 0, KEY_ALL_ACCESS, hKey)
Success = RegDeleteKey(hKey, tmpValueName)
Else
Success = RegDeleteValue(hKey, ValueName)
End If
If Success <> ERROR_SUCCESS Then DeleteKey = False: RegCloseKey hKey: Exit Function
' 关闭注册表关键字...
RegCloseKey hKey
DeleteKey = True                                    ' 返回函数值
End Function
'-------------------------------------------------------------------------------------------------------------
'- 获得注册表关键字的一些信息...
'- SubKeyName()      注册表关键字的所有子项的名称(注意:最小下标为0)
'- ValueName()       注册表关键字的所有子键的名称(注意:最小下标为0)
'- ValueType()       注册表关键字的所有子键的类型(注意:最小下标为0)
'- CountKey          注册表关键字的子项数量
'- CountValue        注册表关键字的子键数量
'- MaxLenKey         注册表关键字的子项名称的最大长度
'- MaxLenValue       注册表关键字的子键名称的最大长度
'-------------------------------------------------------------------------------------------------------------
Public Function GetKeyInfo(KeyRoot As KeyRoot, KeyName As String, SubKeyName() As String, ValueName() As String, ValueType() As ValueType, Optional CountKey As Long, Optional CountValue As Long, Optional MaxLenKey As Long, Optional MaxLenValue As Long) As Boolean
Dim f As FILETIME
Dim l As Long, s As String
' 打开一个已存在的注册表关键字...
Success = RegOpenKeyEx(KeyRoot, KeyName, 0, KEY_ALL_ACCESS, hKey)
If Success <> ERROR_SUCCESS Then GetKeyInfo = False: RegCloseKey hKey: Exit Function
' 获得一个已打开的注册表关键字的信息...
Success = RegQueryInfoKey(hKey, vbNullString, ByVal 0&, ByVal 0&, CountKey, MaxLenKey, ByVal 0&, CountValue, MaxLenValue, ByVal 0&, ByVal 0&, f)
If Success <> ERROR_SUCCESS Then GetKeyInfo = False: RegCloseKey hKey: Exit Function
If CountKey <> 0 Then
ReDim SubKeyName(CountKey - 1) As String            ' 重新定义数组, 使用数组大小与注册表关键字的子项数量匹配
For i = 0 To CountKey - 1
SubKeyName(i) = Space(255)
l = 255
RegEnumKeyEx hKey, i, ByVal SubKeyName(i), l, 0, vbNullString, ByVal 0&, f
SubKeyName(i) = Left(SubKeyName(i), l)
Next i
' 下面的二重循环对字符串数组进行冒泡排序
For i = 0 To UBound(SubKeyName)
For j = i + 1 To UBound(SubKeyName)
If SubKeyName(i) > SubKeyName(j) Then
s = SubKeyName(i)
SubKeyName(i) = SubKeyName(j)
SubKeyName(j) = s
End If
Next j
Next i
End If
If CountValue <> 0 Then
ReDim ValueName(CountValue - 1) As String           ' 重新定义数组, 使用数组大小与注册表关键字的子键数量匹配
ReDim ValueType(CountValue - 1) As Long             ' 重新定义数组, 使用数组大小与注册表关键字的子键数量匹配
For i = 0 To CountValue - 1
ValueName(i) = Space(255)
l = 255
RegEnumValue hKey, i, ByVal ValueName(i), l, 0, ValueType(i), ByVal 0&, ByVal 0&
ValueName(i) = Left(ValueName(i), l)
Next i
' 下面的二重循环对字符串数组进行冒泡排序
For i = 0 To UBound(ValueName)
For j = i + 1 To UBound(ValueName)
If ValueName(i) > ValueName(j) Then
s = ValueName(i)
ValueName(i) = ValueName(j)
ValueName(j) = s
End If
&\nnbsp;  Next j
Next i
End If
' 关闭注册表关键字...
RegCloseKey hKey
GetKeyInfo = True                                   ' 返回函数值
End Function
'-------------------------------------------------------------------------------------------------------------
'- 导出注册表关键字的值
'- 参数说明: KeyRoot--根类型, KeyName--子项名称, FileName--导出的文件路径及文件名(原始数据库格式)
'-------------------------------------------------------------------------------------------------------------
Public Function SaveKey(KeyRoot As KeyRoot, KeyName As String, FileName As String) As Boolean
On Error Resume Next
Dim lpAttr As SECURITY_ATTRIBUTES                   ' 注册表安全类型
lpAttr.nLength = 50                                 ' 设置安全属性为缺省值...
lpAttr.lpSecurityDescriptor = 0                     ' ...
lpAttr.bInheritHandle = True                        ' ...
If EnablePrivilege(SE_BACKUP_NAME) = False Then
SaveKey = False
Exit Function
End If
Success = RegOpenKeyEx(KeyRoot, KeyName, 0&, KEY_ALL_ACCESS, hKey)
If Success <> 0 Then
SaveKey = False
Success = RegCloseKey(hKey)
Exit Function
End If
Success = RegSaveKey(hKey, FileName, lpAttr)
If Success = 0 Then SaveKey = True Else SaveKey = False
Success = RegCloseKey(hKey)
End Function
'-------------------------------------------------------------------------------------------------------------
'- 导入注册表关键字的值
'- 参数说明: KeyRoot--根类型, KeyName--子项名称, FileName--导入的文件路径及文件名(原始数据库格式)
'-------------------------------------------------------------------------------------------------------------
Public Function RestoreKey(KeyRoot As KeyRoot, KeyName As String, FileName As String) As Boolean
On Error Resume Next
If EnablePrivilege(SE_RESTORE_NAME) = False Then
RestoreKey = False
Exit Function
End If
Success = RegOpenKeyEx(KeyRoot, KeyName, 0&, KEY_ALL_ACCESS, hKey)
If Success <> 0 Then
RestoreKey = False
Success = RegCloseKey(hKey)
Exit Function
End If
Success = RegRestoreKey(hKey, FileName, REG_FORCE_RESTORE)
If Success = 0 Then RestoreKey = True Else RestoreKey = False
Success = RegCloseKey(hKey)
End Function
'-------------------------------------------------------------------------------------------------------------
'- 使注册表允许导入/导出
'-------------------------------------------------------------------------------------------------------------
Private Function EnablePrivilege(seName As String) As Boolean
On Error Resume Next
Dim p_lngRtn As Long
Dim p_lngToken As Long
Dim p_lngBufferLen As Long
Dim p_typLUID As LUID
Dim p_typTokenPriv As TOKEN_PRIVILEGES
Dim p_typPrevTokenPriv As TOKEN_PRIVILEGES
p_lngRtn = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, p_lngToken)
If p_lngRtn = 0 Then
EnablePrivilege = False
Exit Function
End If
If Err.LastDllError <> 0 Then
EnablePrivilege = False
Exit Function
End If
p_lngRtn = LookupPrivilegeValue(0&, seName, p_typLUID)
If p_lngRtn = 0 Then
EnablePrivilege = False
Exit Function
End If
p_typTokenPriv.PrivilegeCount = 1
p_typTokenPriv.Privileges.Attributes = SE_PRIVILEGE_ENABLED
p_typTokenPriv.Privileges.pLuid = p_typLUID
EnablePrivilege = (AdjustTokenPrivileges(p_lngToken, False, p_typTokenPriv, Len(p_typPrevTokenPriv), p_typPrevTokenPriv, p_lngBufferLen) <> 0)
End Function
'-------------------------------------------------------------------------------------------------------------
'- 将 Double 型( 限制在 0--2^32-1 )的数字转换为十六进制并在前面补零
'- 参数说明: Number--要转换的 Double 型数字
'-------------------------------------------------------------------------------------------------------------
Private Function DoubleToHex(ByVal Number As Double) As String
Dim strHex As String
strHex = Space(8)
For i = 1 To 8
Select Case Number - Int(Number / 16) * 16
Case 10
Mid(strHex, 9 - i, 1) = "A"
Case 11
Mid(strHex, 9 - i, 1) = "B"
Case 12
Mid(strHex, 9 - i, 1) = "C"
&nbs\np;        Case 13
Mid(strHex, 9 - i, 1) = "D"
Case 14
Mid(strHex, 9 - i, 1) = "E"
Case 15
Mid(strHex, 9 - i, 1) = "F"
Case Else
Mid(strHex, 9 - i, 1) = CStr(Number - Int(Number / 16) * 16)
End Select
Number = Int(Number / 16)
Next i
DoubleToHex = strHex
End Function

[常用API]一个不含在通用对话框控件中的常用对话框

选择文件夹对话框,在安装程序和软件设置中经常用到,但是神奇的是,如此常用的对话框,却不包含在VB的通用对话框控件中,这也导致了很多人手工设计这个对话框。今天在翻看别人程序的时候,发现他们用到了这个API,于是就写出来分享。
此API在Vista下也测试成功,所以可以正常使用。
API如下:
程序代码 程序代码
Private Declare Function SHBrowseForFolder Lib “shell32.dll” Alias “SHBrowseForFolderA” (lpBrowseInfo As BROWSEINFO) As Long
Private Declare Function SHGetPathFromIDList Lib “shell32.dll” Alias “SHGetPathFromIDListA” (ByVal pidl As Long, ByVal pszPath As String) As Long
相关的测试程序代码,贴在附件中。
下载文件

UTF-8的一些处理

由于一些平台对UTF-8的支持不够,导致程序开发上的一些困难,于是查到一些资料,还是很有意义的。发出来参考一下
支持常见的ANSI、UTF-8、UTF-16LE、UTF-16BE这几种编码文本

Option Explicit
&#39;mTextUTF.bas
&#39;模块:UTF文本文件访问
&#39;作者:zyl910
&#39;版本:1.0
&#39;日期:2006-1-23
&#39;== 说明 ===================================================
&#39;支持Unicode编码的文本文件读写。暂时支持ANSI、UTF-8、UTF-16LE、UTF-16BE这几种编码文本
&#39;== 更新记录 ===============================================
&#39;[V1.0] 2006-1-23
&#39;1.支持最常见的ANSI、UTF-8、UTF-16LE、UTF-16BE这几种编码文本
&#39;## 编译预处理常数 #########################################
&#39;== 全局常数 ===============================================
&#39;IncludeAPILib:引用了API库,此时不需要手动写API声明
&#39;## API ####################################################
#If IncludeAPILib = 0 Then
&#39;== File ===================================================
Private Declare Function Cr&#101;ateFile Lib "kernel32" Alias "Cr&#101;ateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Long) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long
Private Declare Function SetFilePointer Lib "kernel32" (ByVal hFile As Long, ByVal lDistanceToMove As Long, lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Private Const INVALID_HANDLE_VALUE = -1
Private Const GENERIC_READ = &H80000000
Private Const GENERIC_WRITE = &H40000000
Private Const FILE_SHARE_READ = &H1
Private Const FILE_SHARE_WRITE = &H2
Private Const Cr&#101;ate_NEW = 1
Private Const Cr&#101;ate_ALWAYS = 2
Private Const OPEN_EXISTING = 3
Private Const OPEN_ALWAYS = 4
Private Const TRUNCATE_EXISTING = 5
Private Const FILE_ATTRIBUTE_NORMAL = &H80
Private Const FILE_BEGIN = 0
Private Const FILE_CURRENT = 1
Private Const FILE_END = 2
&#39;== Unicode ================================================
Private Declare Function MultiByteToWideChar Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, ByRef lpMultiByteStr As Any, ByVal cchMultiByte As Long, ByRef lpWideCharStr As Any, ByVal cchWideChar As Long) As Long
Private Declare Function WideCharToMultiByte Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, ByRef lpWideCharStr As Any, ByVal cchWideChar As Long, ByRef lpMultiByteStr As Any, ByVal cchMultiByte As Long, ByRef lpDefaultChar As Any, ByVal lpUsedDefaultChar As Long) As Long
Private Const CP_UTF8 As Long = 65001
#End If
&#39;###########################################################
&#39;Unicode编码格式
Public Enum UnicodeEncodeFormat
UEF_ANSI = 0 &#39;ANSI+DBCS
UEF_UTF8 &#39;UTF-8
UEF_UTF16LE &#39;UTF-16LE
UEF_UTF16BE &#39;UTF-16BE
UEF_UTF32LE &#39;UTF-32LE
UEF_UTF32BE &#39;UTF-32BE
UEF_Auto = -1 &#39;自动识别编码
&#39;隐藏项目
[_UEF_Min] = UEF_ANSI
[_UEF_Max] = UEF_UTF32BE
End Enum
&#39;ANSI+DBCS方式的文本所使用的代码页。默认为0,表示使用系统当前代码页。可以利用该参数实现读取其他代码编码的文本,比如想在 简体中文平台下 读取 繁体中文平台生成的txt,就将它设为950
Public UEFCodePage As Long
&#39;判断BOM
&#39;返回值:BOM所占字节
&#39;dwFirst:[in]文件最开始的4个字节
&#39;fmt:[out]返回编码类型
Public Function UEFCheckBOM(ByVal dwFirst As Long, ByRef fmt As UnicodeEncodeFormat) As Long
If dwFirst = &HFEFF& Then
fmt = UEF_UTF32LE
UEFCheckBOM = 4
ElseIf dwFirst = &HFFFE0000 Then
fmt = UEF_UTF32BE
UEFCheckBOM = 4
ElseIf (dwFirst And &HFFFF&) = &HFEFF& Then
fmt = UEF_UTF16LE
UEFCheckBOM = 2
ElseIf (dwFirst And &HFFFF&) = &HFFFE& Then
fmt = UEF_UTF16BE
UEFCheckBOM = 2
ElseIf (dwFirst And &HFFFFFF) = &HBFBBEF Then
fmt = UEF_UTF8
UEFCheckBOM = 3
Else
fmt = UEF_ANSI
UEFCheckBOM = 0
End If
End Function
&#39;生成BOM
&#39;返回值:BOM所占字节
&#39;fmt:[in]编码类型
&#39;dwFirst:[out]文件最开始的4个字节
Public Function UEFMakeBOM(ByVal fmt As UnicodeEncodeFormat, ByRef dwFirst As Long) As Long
Sel&#101;ct Case fmt
Case UEF_UTF8
dwFirst = &HBFBBEF
UEFMakeBOM = 3
Case UEF_UTF16LE
dwFirst = &HFEFF&
UEFMakeBOM = 2
Case UEF_UTF16BE
dwFirst = &HFFFE&
UEFMakeBOM = 2
Case UEF_UTF32LE
dwFirst = &HFEFF&
UEFMakeBOM = 4
Case UEF_UTF32BE
dwFirst = &HFFFE0000
UEFMakeBOM = 4
Case Else
dwFirst = 0
UEFMakeBOM = 0
End Sel&#101;ct
End Function
&#39;判断文本文件的编码类型
&#39;返回值:编码类型。文件无法打开时,返回UEF_Auto
&#39;FileName:文件名
Public Function UEFCheckTextFileFormat(ByVal FileName As String) As UnicodeEncodeFormat
Dim hFile As Long
Dim dwFirst As Long
Dim nNumRead As Long
&#39;打开文件
hFile = Cr&#101;ateFile(FileName, GENERIC_READ, FILE_SHARE_READ o&#114; FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, ByVal 0&)
If INVALID_HANDLE_VALUE = hFile Then &#39;文件无法打开
UEFCheckTextFileFormat = UEF_Auto
Exit Function
End If
&#39;判断BOM
dwFirst = 0
Call ReadFile(hFile, dwFirst, 4, nNumRead, ByVal 0&)
nNumRead = UEFCheckBOM(dwFirst, UEFCheckTextFileFormat)
&#39;Debug.Print nNumRead
&#39;关闭文件
Call CloseHandle(hFile)
End Function
&#39;读取文本文件
&#39;返回值:读取的文本。返回vbNullString表示文件无法打开
&#39;FileName:[in]文件名
&#39;fmt:[in,out]使用何种文本编码格式来读取文本。为UEF_Auto时表示自动判断,且在fmt参数返回文本所用编码格式
Public Function UEFLoadTextFile(ByVal FileName As String, Optional ByRef fmt As UnicodeEncodeFormat = UEF_Auto) As String
Dim hFile As Long
Dim nFileSize As Long
Dim nNumRead As Long
Dim dwFirst As Long
Dim CurFmt As UnicodeEncodeFormat
Dim cbBOM As Long
Dim cbTextData As Long
Dim CurCP As Long
Dim byBuf() As Byte
Dim cchStr As Long
Dim I As Long
Dim byTemp As Byte
&#39;判断fmt范围
If fmt <> UEF_Auto Then
If fmt < &#91;_UEF_Min&#93; o&#114; fmt > [_UEF_Max] Then
GoTo FunEnd
End If
End If
&#39;打开文件
hFile = Cr&#101;ateFile(FileName, GENERIC_READ, FILE_SHARE_READ o&#114; FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, ByVal 0&)
If INVALID_HANDLE_VALUE = hFile Then &#39;文件无法打开
GoTo FunEnd
End If
&#39;判断文件大小
nFileSize = GetFileSize(hFile, nNumRead)
If nNumRead <> 0 Then &#39;超过4GB
GoTo FreeHandle
End If
If nFileSize < 0 Then &#\n39;超过2GB
GoTo FreeHandle
End If
&#39;判断BOM
dwFirst = 0
Call ReadFile(hFile, dwFirst, 4, nNumRead, ByVal 0&)
cbBOM = UEFCheckBOM(dwFirst, CurFmt)
&#39;恢复文件指针
If fmt = UEF_Auto Then &#39;自动判断
fmt = CurFmt
&#39;cbBOM = cbBOM
Else &#39;手动设置编码
If fmt = CurFmt Then &#39;若编码相同,则忽略BOM标记
&#39;cbBOM = cbBOM
Else &#39;编码不同,那么都是数据
cbBOM = 0
End If
End If
Call SetFilePointer(hFile, cbBOM, ByVal 0&, FILE_BEGIN)
cbTextData = nFileSize - cbBOM
&#39;读取数据
UEFLoadTextFile = ""
Sel&#101;ct Case fmt
Case UEF_ANSI, UEF_UTF8
&#39;判断应使用的CodePage
CurCP = IIf(fmt = UEF_UTF8, CP_UTF8, UEFCodePage)
&#39;分配缓冲区
On Error GoTo FreeHandle
ReDim byBuf(0 To cbTextData - 1)
On Error GoTo 0
&#39;读取数据
nNumRead = 0
Call ReadFile(hFile, byBuf(0), cbTextData, nNumRead, ByVal 0&)
&#39;取得Unicode文本长度
cchStr = MultiByteToWideChar(CurCP, 0, byBuf(0), nNumRead, ByVal 0&, ByVal 0&)
If cchStr > 0 Then
&#39;分配字符串空间
On Error GoTo FreeHandle
UEFLoadTextFile = String$(cchStr, 0)
On Error GoTo 0
&#39;取得文本
cchStr = MultiByteToWideChar(CurCP, 0, byBuf(0), nNumRead, ByVal StrPtr(UEFLoadTextFile), cchStr + 1)
End If
Case UEF_UTF16LE
cchStr = (cbTextData + 1) \\ 2
&#39;分配字符串空间
On Error GoTo FreeHandle
UEFLoadTextFile = String$(cchStr, 0)
On Error GoTo 0
&#39;取得文本
nNumRead = 0
Call ReadFile(hFile, ByVal StrPtr(UEFLoadTextFile), cbTextData, nNumRead, ByVal 0&)
&#39;修正文本长度
cchStr = (nNumRead + 1) \\ 2
If cchStr > 0 Then
If Len(UEFLoadTextFile) > cchStr Then
UEFLoadTextFile = Left$(UEFLoadTextFile, cchStr)
End If
Else
UEFLoadTextFile = ""
End If
Case UEF_UTF16BE
&#39;分配缓冲区
On Error GoTo FreeHandle
ReDim byBuf(0 To cbTextData - 1)
On Error GoTo 0
&#39;读取数据
nNumRead = 0
Call ReadFile(hFile, byBuf(0), cbTextData, nNumRead, ByVal 0&)
If nNumRead > 0 Then
&#39;隔两字节翻转相邻字节
For I = 0 To nNumRead - 1 - 1 Step 2 &#39;再-1是为了避免最后多出的那个字节
byTemp = byBuf(I)
byBuf(I) = byBuf(I + 1)
byBuf(I + 1) = byTemp
Next I
&#39;取得文本
UEFLoadTextFile = byBuf &#39;VB允许String中的字符串数据与Byte数组直接转换
End If
Case UEF_UTF32LE
UEFLoadTextFile = vbNullString &#39;暂时不支持
Case UEF_UTF32BE
UEFLoadTextFile = vbNullString &#39;暂时不支持
Case Else
Debug.Assert False
End Sel&#101;ct
FreeHandle:
&#39;关闭文件
Call CloseHandle(hFile)
FunEnd:
End Function
&#39;保存文本文件
&#39;返回值:是否成功
&#39;FileName:[in]文件名
&#39;sText:[in]欲输出的文本
&#39;IsAppend:[in]是否是添加方式
&#39;fmt:[in,out]使用何种文本编码格式来存储文本。当IsAppend=True时允许UEF_Auto自动判断,且在fmt参数返回文本所用编码格式
&#39;DefFmt:[in]当使用添加模式时,若文件不存在且fmt = UEF_Auto时应使用的编码格式
Public Function UEFSaveTextFile(ByVal FileName As String, _
ByRef sText As String, Optional ByVal IsAppend As Boolean = False, _
Optional ByRef fmt As UnicodeEncodeFormat = UEF_Auto, Optional ByVal DefFmt As UnicodeEncodeFormat = UEF_ANSI) As Boolean
Dim hFile As Long
Dim nFileSize As Long
Dim nNumRead As Long
Dim dwFirst As Long
Dim cbBOM As Long
Dim CurCP As Long
Dim byBuf() As Byte
Dim cbBuf As Long
Dim I As Long
Dim byTemp As Byte
&#39;判断fmt范围
If IsAppend And (fmt = UEF_Auto) Then
Else
If fmt < &#91;_UEF_Min&#93; o&#114; fmt > [_UEF_Max] Then
GoTo FunEnd
End If
End If
&#39;打开文件
hFile = Cr&#101;ateFile(FileName, GENERIC_READ o&#114; GENERIC_WRITE, FILE_SHARE_READ o&#114; FILE_SHARE_WRITE, ByVal 0&, IIf(IsAppend, OPEN_ALWAYS, Cr&#101;ate_ALWAYS), FILE_ATTRIBUTE_NORMAL, ByVal 0&)
If INVALID_HANDLE_VALUE = hFile Then &#39;文件无法打开
GoTo FunEnd
End If
&#39;判断文件大小
nFileSize = GetFileSize(hFile, nNumRead)
If nFileSize = 0 And nNumRead = 0 Then &#39;文件大小为0字节
IsAppend = False &#39;此时需要写BOM标志
If fmt = UEF_Auto Then fmt = DefFmt
End If
&#39;判断BOM
If IsAppend And (fmt = UEF_Auto) Then
dwFirst = 0
Call ReadFile(hFile, dwFirst, 4, nNumRead, ByVal 0&)
cbBOM = UEFCheckBOM(dwFirst, fmt)
ElseIf IsAppend = False Then
cbBOM = UEFMakeBOM(fmt, dwFirst)
End If
&#39;文件指针定位
Call SetFilePointer(hFile, 0, ByVal 0&, IIf(IsAppend, FILE_END, FILE_BEGIN))
&#39;写BOM
If IsAppend = False Then
If cbBOM > 0 Then
Call WriteFile(hFile, dwFirst, cbBOM, nNumRead, ByVal 0&)
End If
End If
&#39;写文本数据
If Len(sText) > 0 Then
Sel&#101;ct Case fmt
Case UEF_ANSI, UEF_UTF8
&#39;判断应使用的CodePage
CurCP = IIf(fmt = UEF_UTF8, CP_UTF8, UEFCodePage)
&#39;取得缓冲区大小
cbBuf = WideCharToMultiByte(CurCP, 0, ByVal StrPtr(sText), Len(sText), ByVal 0&, 0, ByVal 0&, ByVal 0&)
If cbBuf > 0 Then
&#39;分配缓冲区
On Error GoTo FreeHandle
ReDim byBuf(0 To cbBuf)
On Error GoTo 0
&#39;转换文本
cbBuf = WideCharToMultiByte(CurCP, 0, ByVal StrPtr(sText), Len(sText), byBuf(0), cbBuf + 1, ByVal 0&, ByVal 0&)
&#39;写文件
Call WriteFile(hFile, byBuf(0), cbBuf, nNumRead, ByVal 0&)
UEFSaveTextFile = True
End If
Case UEF_UTF16LE
&#39;写文件
Call WriteFile(hFile, ByVal StrPtr(sText), LenB(sText), nNumRead, ByVal 0&)
UEFSaveTextFile = True
Case UEF_UTF16BE
&#39;将字符串中的数据复制到byBuf
On Error GoTo FreeHandle
byBuf = sText
On Error GoTo 0
cbBuf = UBound(byBuf) - LBound(byBuf) + 1
&#39;隔两字节翻转相邻字节
For I = 0 To cbBuf - 1 - 1 Step 2 &#39;再-1是为了避免最后多出的那个字节
byTemp = byBuf(I)
byBuf(I) = byBuf(I + 1)
byBuf(I + 1) = byTemp
Next I
&#39;写文件
Call WriteFile(hFile, byBuf(0), cbBuf, nNumRead, ByVal 0&)
UEFSaveTextFile = True
Case UEF_UTF32LE
UEFSaveTextFile = False &#39;暂时不支持
Case UEF_UTF32BE
UEFSaveTextFile = False &#39;暂时不支持
Case Else
Debug.Assert False
End Sel&#101;ct
Else
UEFSaveTextFile = True
End If
FreeHandle:
&#39;关闭文件
Call CloseHandle(hFile)
FunEnd:
End Function