VC进程控制相关代码 5/29
//获取进程路径
CString GetProcessPath( DWORD idProcess )
{
// 获取进程路径
CString sPath;
// 打开进程句柄
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, idProcess );
if( NULL != hProcess )
{
HMODULE hMod;
DWORD cbNeeded;
// 获取路径
if( EnumProcessModules( hProcess, &hMod, sizeof( hMod ), &cbNeeded ) )
{
DWORD dw = GetModuleFileNameEx( hProcess, hMod, sPath.
GetBuffer( MAX_PATH ), MAX_PATH );
sPath.ReleaseBuffer();
}
CloseHandle( hProcess );
}
return( sPath );
}
//获取进程优先级
CString GetProcessPriority(HANDLE hProcess)
{
char sz1[10] = "NORMAL";
char sz2[10] = "IDLE";
char sz3[10] = "REALTIME";
char sz4[10] = "HIGH";
char sz5[10] = "NULL";
char sz6[15] = "ABOVENORMAL";
char sz7[15] = "BELOWNORMAL";
//进程优先级返回
if(GetPriorityClass(hProcess) == NORMAL_PRIORITY_CLASS)
return sz1;
if(GetPriorityClass(hProcess) == IDLE_PRIORITY_CLASS)
return sz2;
if(GetPriorityClass(hProcess) == REALTIME_PRIORITY_CLASS)
return sz3;
if(GetPriorityClass(hProcess) == HIGH_PRIORITY_CLASS)
return sz4;
if(GetPriorityClass(hProcess) == ABOVE_NORMAL_PRIORITY_CLASS)
return sz6;
if(GetPriorityClass(hProcess) == BELOW_NORMAL_PRIORITY_CLASS)
return sz7;
else
return sz5;
}
//终止进程主函数
void TerminateProcessID(DWORD dwID)
{
HANDLE hProcess = NULL;
//打开进程句柄
hProcess = OpenProcess(PROCESS_TERMINATE,FALSE,dwID);
if(hProcess != NULL)
{
//终止进程
TerminateProcess(hProcess,0);
::CloseHandle(hProcess);
}
}
//获取进程快照
void GetProcessInfo()
{
SHFILEINFO shSmall;
int nIndex;
CString str;
//声明进程信息变量
PROCESSENTRY32 ProcessInfo;
//获取系统中的所有进程信息
HANDLE SnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if(SnapShot != NULL)
{
m_ListCtrl.DeleteAllItems();
//设置ProcessInfo的大小
ProcessInfo.dwSize = sizeof(PROCESSENTRY32);
//返回系统中第一个进程的信息
BOOL Status = Process32First(SnapShot,&ProcessInfo);
//进程计数
int m_nProcess = 0;
while(Status)
{
m_nProcess++;
ZeroMemory(&shSmall,sizeof(shSmall));
//获取进程文件的信息
SHGetFileInfo(ProcessInfo.szExeFile,0,&shSmall,
sizeof(shSmall),SHGFI_ICON|SHGFI_SMALLICON);
//在列表控件中添加映像名称
nIndex = m_ListCtrl.InsertItem(m_nProcess,ProcessInfo.szExeFile);
//在列表控件中添加进程PID
str.Format("%08X",ProcessInfo.th32ProcessID);
m_ListCtrl.SetItemText(nIndex,1,str);
//在列表控件中添加进程的父进程PID
str.Format("%08X",ProcessInfo.th32ParentProcessID);
m_ListCtrl.SetItemText(nIndex,2,str);
//获取进程路径
str = GetProcessPath(ProcessInfo.th32ProcessID);
m_ListCtrl.SetItemText(nIndex,3,str);
//获取下一个进程信息
Status = Process32Next(SnapShot,&ProcessInfo);
}
}
else
MessageBox("获取进程信息失败!");
}
//获取模块快照
void GetProcessModule(DWORD dwID)
{
MODULEENTRY32 me32;
int nIndex;
or="#000000">CString str;
// 在使用这个结构之前,先设置它的大小
me32.dwSize = sizeof(me32);
// 给进程内所有模块拍一个快照
HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwID);
if(hModuleSnap == INVALID_HANDLE_VALUE)
{
//建立快照失败
MessageBox("获取模块信息失败!", "提示", MB_OK|MB_ICONWARNING);
return;
}
// 遍历模块快照,轮流显示每个模块的信息
BOOL bMore = Module32First(hModuleSnap, &me32);
int m_nModule = 0;
while(bMore)
{
m_nModule++;
nIndex = m_listmod.InsertItem(m_nModule, me32.szExePath);//模块路径
str.Format("%u", me32.modBaseSize);//模块大小
m_listmod.SetItemText(nIndex,1,str);
bMore = Module32Next(hModuleSnap, &me32);
}
// 不要忘记清除掉snapshot对象
CloseHandle(hModuleSnap);
}
//
// FindProcess
// 这个函数唯一的参数是你指定的进程名,如:你的目标进程
// 是 "Notepad.exe",返回值是该进程的ID,失败返回0
//
DWORD FindProcess(char *strProcessName)
{
DWORD aProcesses[1024], cbNeeded, cbMNeeded;
HMODULE hMods[1024];
HANDLE hProcess;
char szProcessName[MAX_PATH];
if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) ) return 0;
for(int i=0; i< (int) (cbNeeded / sizeof(DWORD)); i++)
{
//_tprintf(_T("%d "), aProcesses[i]);
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE, aProcesses[i]);
EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbMNeeded);
GetModuleFileNameEx( hProcess, hMods[0], szProcessName,sizeof(szProcessName));
if(strstr(szProcessName, st
rProcessName))
{
//_tprintf(_T("%s;"), szProcessName);
return(aProcesses[i]);
}
//_tprintf(_T(" "));
}
return 0;
}
//
// KillProcess
// 此函数中用上面的 FindProcess 函数获得你的目标进程的ID
// 用WIN API OpenPorcess 获得此进程的句柄,再以TerminateProcess
// 强制结束这个进程
//
VOID KillProcess()
{
// When the all operation fail this function terminate the "winlogon" Process for force exit the system.
HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE,
FindProcess("YourTargetProcess.exe"));
if(hYourTargetProcess == NULL)
{
return;
}
TerminateProcess(hProcess, 0);
CloseHandle(hProcess);
return;
}
进程模块枚举之获取基地址 5/28
typedef BOOL (_stdcall *ENUMPROCESSMODULES)
(HANDLE hProcess,HMODULE* lphModule,DWORD cb,LPDWORD lpcbNeeded);
typedef DWORD (_stdcall *GETMODULEFILENAMEEX)
(HANDLE hProcess,HMODULE hModule,LPTSTR lpFilename,DWORD nSize );
unsigned int FindModlueAddr(DWORD dwProcessId,char *TempSMPFileName)
{
HMODULE hModule = LoadLibrary("psapi.dll");
HMODULE hMods[1024];
DWORD cbNeeded=0;
char szModName[MAX_PATH];
//char TempSMPFileName[256];
unsigned int Ret = 0;
if(hModule == NULL)
return 0;
ENUMPROCESSMODULES pEnumProcessModules =
(ENUMPROCESSMODULES)GetProcAddress(hModule, "EnumProcessModules");
GETMODULEFILENAMEEX pGetModuleFileNameEx =
(GETMODULEFILENAMEEX)GetProcAddress(hModule, "GetModuleFileNameExA");
HANDLE hProcess =
OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ,false,dwProcessId);
if(!hProcess)
{
Ret = 0;
goto FuncRet1;
}
//strcpy(TempSMPFileName,(const char *)GetSMPFileName());
int i;
if(pEnumProcessModules(hProcess,hMods,sizeof(hMods), &cbNeeded))
{
//枚举成功
for ( i = 0; i <= (int)(cbNeeded / sizeof(HMODULE)); i++ )
{
if(pGetModuleFileNameEx( hProcess, hMods[i], szModName,sizeof(szModName)))
{
if(strstr(szModName,TempSMPFileName/*".SMP"/*"NDDCLW.SMP"*/))
{
//MessageBox(0,szModName,"提示",MB_OK);
Ret = (unsigned int)hMods[i];
goto FuncRet;
}
}
}
}
FuncRet:
CloseHandle(hProcess);
FuncRet1:
FreeLibrary(hModule);
return Ret;
}
进程与线程状态监视 5/27
有时候我们希望能够动态监视系统中任意进程/线程的创建与销毁。为了达到此目的我翻阅了 DDK 手册,发现其提供的 PsSetCreateProcessNotifyRoutine(),PsSetCreateThreadNotifyRoutine(),等函数可以实现此功能。这两个函数可以通过向系统注册一个 CALLBALCK 函数来监视进程/线程等操作。函数原形如下:
NTSTATUS
PsSetCreateProcessNotifyRoutine(
IN PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
IN BOOLEAN Remove
);
VOID
(*PCREATE_PROCESS_NOTIFY_ROUTINE) (
IN HANDLE ParentId,
IN HANDLE ProcessId,
IN BOOLEAN Create
);
NTSTATUS
PsSetCreateThreadNotifyRoutine(
IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
);
VOID
(*PCREATE_THREAD_NOTIFY_ROUTINE) (
IN HANDLE ProcessId,
IN HANDLE ThreadId,
IN BOOLEAN Create
);
通过原形可以看出,其 CALLBACK 函数只提供了进程ID/线程ID。并没有提供进程名。那么我们要进一步通过进程ID来获取进程名。这需要用到一个未公开的函数 PsLookupProcessByProcessId()。函数原形如下:
NTSTATUS PsLookupProcessByProcessId(
IN ULONG ulProcId,
OUT PEPROCESS * pEProcess
);
函数输出的 EPROCESS 结构也是未公开的内核进程结构,很多人称其为 KPEB。EPROCESS 结构中的偏移 0x1FC 指向当前进程名的偏移。(这个结构虽然可以在驱动程序中直接使用。但没有公布其结构,网上有不少高手已将其结构给出。有兴趣可以自行搜索,或去 IFS DDK 中获取,这里因为结构太长,就不贴出来了)有了这个结构我们就可以从中得到进程名。NT系统还提供了一个函数可以动态监视进程装载映像。此函数可以得到进程加栽时所调用的 DLL 名称与全路径,还有一些映像信息。为我们获得更详细的进程装载信息提供了更好的帮助。
函数原形如下:
NTSTATUS
PsSetLoadImageNotifyRoutine(
IN PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine
);
VOID
(*PLOAD_IMAGE_NOTIFY_ROUTINE) (
IN PUNICODE_STRING FullImageName,
IN HANDLE ProcessId, // where image is mapped
IN PIMAGE_INFO ImageInfo
);
typedef struct _IMAGE_INFO {
union {
ULONG Properties;
struct {
ULONG ImageAddressingMode : 8; //code addressing mode
ULONG SystemModeImage : 1; //system mode image
ULONG ImageMappedToAllPids : 1; //mapped in all processes
ULONG Reserved : 22;
};
};
PVOID ImageBase;
ULONG ImageSelector;
ULONG ImageSize;
ULONG ImageSectionNumber;
} IMAGE_INFO, *PIMAGE_INFO;
利用以上提供的函数与结构,我们便能实现一个进程/线程监视器。下面这段代码演示了如何实现此功能。
/*****************************************************************
文件名 : WssProcMon.c
描述 : 进程/线程监视器
作者 : sinister
最后修改日期 : 2002-11-02
*****************************************************************/
#include "ntddk.h"
#include "string.h"
#define ProcessNameOffset 0x1fc
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS PsLookupProcessByProcessId(IN ULONG ulProcId, OUT PEPROCESS * pEProcess);
VOID ProcessCreateMon ( IN HANDLE hParentId, IN HANDLE PId,IN BOOLEAN bCreate);
VOID ThreadCreateMon (IN HANDLE PId, IN HANDLE TId, IN BOOLEAN bCreate);
VOID ImageCreateMon (IN PUNICODE_STRING FullImageName, IN HANDLE ProcessId, IN PIMAGE_INFO ImageInfo );
// 驱动入口
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{
UNICODE_STRING nameString, linkString;
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
int i;
//建立设备
RtlInitUnicodeString( &nameString, L"\\Device\\WssProcMon" );
status = IoCreateDevice( DriverObject,
0,
&nameString,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&deviceObject );
if (!NT_SUCCESS( status ))
return status;
RtlInitUnicodeString( &linkString, L"\\DosDevices\\WssProcMon" );
status = IoCreateSymbolicLink (&linkString, &nameString);
if (!NT_SUCCESS( status ))
{
IoDeleteDevice (DriverObject->DeviceObject);
return status;
}
status = PsSetLoadImageNotifyRoutine(ImageCreateMon);
if (!NT_SUCCESS( status ))
{
DbgPrint("PsSetLoadImageNotifyRoutine()\n");
return status;
}
status = PsSetCreateThreadNotifyRoutine(ThreadCreateMon);
if (!NT_SUCCESS( status ))
{
DbgPrint("PsSetCreateThreadNotifyRoutine()\n");
return status;
}
status = PsSetCreateProcessNotifyRoutine(ProcessCreateMon, FALSE);
if (!NT_SUCCESS( status ))
{
DbgPrint("PsSetCreateProcessNotifyRoutine()\n");
return status;
}
for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
DriverObject->MajorFunction[i] = MydrvDispatch;
}
return STATUS_SUCCESS;
}
//处理设备对象操作
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
IoCompleteRequest( Irp, 0 );
return Irp->IoStatus.Status;
}
VOID ProcessCreateMon ( IN HANDLE hParentId, IN HANDLE PId,IN BOOLEAN bCreate )
{
PEPROCESS EProcess;
ULONG ulCurrentProcessId;
LPTSTR lpCurProc;
NTSTATUS status;
status = PsLookupProcessByProcessId( (ULONG)PId, &EProcess);
if (!NT_SUCCESS( status ))
{
DbgPrint("PsLookupProcessByProcessId()\n");
return ;
}
if ( bCreate )
{
lpCurProc = (LPTSTR)EProcess;
lpCurProc = lpCurProc + ProcessNameOffset;
DbgPrint( "CREATE PROCESS = PROCESS NAME: %s , PROCESS PARENTID: %d, PROCESS ID: %d, PROCESS ADDRESS %x:\n",
lpCurProc,
hParentId,
PId,
EProcess );
}
else
{
DbgPrint( "TERMINATED == PROCESS ID: %d\n", PId);
}
}
VOID ThreadCreateMon (IN HANDLE PId, IN HANDLE TId, IN BOOLEAN bCreate)
{
PEPROCESS EProcess;
ULONG ulCurrentProcessId;
LPTSTR lpCurProc;
NTSTATUS status;
status = PsLookupProcessByProcessId( (ULONG)PId, &EProcess);
if (!NT_SUCCESS( status ))
{
DbgPrint("PsLookupProcessByProcessId()\n");
return ;
}
if ( bCreate )
{
lpCurProc = (LPTSTR)EProcess;
lpCurProc = lpCurProc + ProcessNameOffset;
DbgPrint( "CREATE THREAD = PROCESS NAME: %s PROCESS ID: %d, THREAD ID: %d\n", lpCurProc, PId, TId );
}
else
{
DbgPrint( "TERMINATED == THREAD ID: %d\n", TId);
}
}
VOID ImageCreateMon (IN PUNICODE_STRING FullImageName, IN HANDLE ProcessId, IN PIMAGE_INFO ImageInf
o )
{
DbgPrint("FullImageName: %S,Process ID: %d\n",FullImageName->Buffer,ProcessId);
DbgPrint("ImageBase: %x,ImageSize: %d\n",ImageInfo->ImageBase,ImageInfo->ImageSize);
}
VB ChangePID 5/27
Option Explicit
Private Const STATUS_ACCESS_DENIED = &HC0000022
Private Const SECTION_MAP_WRITE = &H2
Private Const SECTION_MAP_READ = &H4
Private Const READ_CONTROL = &H20000
Private Const WRITE_DAC = &H40000
Private Const NO_INHERITANCE = 0
Private Const DACL_SECURITY_INFORMATION = &H4
Private Type UNICODE_STRING
Length As Integer
MaximumLength As Integer
Buffer As Long
End Type
Private Type OBJECT_ATTRIBUTES
Length As Long
RootDirectory As Long
ObjectName As Long
Attributes As Long
SecurityDeor As Long
SecurityQualityOfService As Long
End Type
Private Enum ACCESS_MODE
NOT_USED_ACCESS
GRANT_ACCESS
SET_ACCESS
DENY_ACCESS
REVOKE_ACCESS
SET_AUDIT_SUCCESS
SET_AUDIT_FAILURE
End Enum
Private Enum MULTIPLE_TRUSTEE_OPERATION
NO_MULTIPLE_TRUSTEE
TRUSTEE_IS_IMPERSONATE
End Enum
Private Enum TRUSTEE_FORM
TRUSTEE_IS_SID
TRUSTEE_IS_NAME
End Enum
Private Enum TRUSTEE_TYPE
TRUSTEE_IS_UNKNOWN
TRUSTEE_IS_USER
TRUSTEE_IS_GROUP
End Enum
Private Type TRUSTEE
pMultipleTrustee As Long
MultipleTrusteeOperation As MULTIPLE_TRUSTEE_OPERATION
TrusteeForm As TRUSTEE_FORM
TrusteeType As TRUSTEE_TYPE
ptstrName As String
End Type
Private Type EXPLICIT_ACCESS
grfAccessPermissions As Long
grfAccessMode As ACCESS_MODE
grfInheritance As Long
TRUSTEE As TRUSTEE
End Type
Private Enum SE_OBJECT_TYPE
SE_UNKNOWN_OBJECT_TYPE = 0
SE_FILE_OBJECT
SE_SERVICE
SE_PRINTER
SE_REGISTRY_KEY
SE_LMSHARE
SE_KERNEL_OBJECT
SE_WINDOW_OBJECT
SE_DS_OBJECT
SE_DS_OBJECT_ALL
SE_PROVIDER_DEFINED_OBJECT
SE_WMIGUID_OBJECT
End Enum
Private Declare Function SetSecurityInfo Lib "advapi32.dll" (ByVal Handle As Long, ByVal ObjectType As SE_OBJECT_TYPE, ByVal SecurityInfo As Long, ppsidOwner As Long, ppsidGroup As Long, ppDacl As Any, ppSacl As Any) As Long
Private Declare Function GetSecurityInfo Lib "advapi32.dll" (ByVal Handle As Long, ByVal ObjectType As SE_OBJECT_TYPE, ByVal SecurityInfo As Long, ppsidOwner As Long, ppsidGroup As Long, ppDacl As Any, ppSacl As Any, ppSecurityDeor As Long) As Long
Private Declare Function SetEntriesInAcl Lib "advapi32.dll" Alias "SetEntriesInAclA" (ByVal cCountOfExplicitEntries As Long, pListOfExplicitEntries As EXPLICIT_ACCESS, ByVal OldAcl As Long, NewAcl As Long) As Long
Private Declare Sub RtlInitUnicodeString Lib "NTDLL.DLL" (DestinationString As UNICODE_STRING, ByVal SourceString As Long)
Private Declare Function ZwOpenSection Lib "NTDLL.DLL" (SectionHandle As Long, ByVal DesiredAccess As Long, ObjectAttributes As color="#000000">Any) As Long
Private Declare Function LocalFree Lib "kernel32" (ByVal hMem As Any) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function MapViewOfFile Lib "kernel32" (ByVal hFileMappingObject As Long, ByVal dwDesiredAccess As Long, ByVal dwFileOffsetHigh As Long, ByVal dwFileOffsetLow As Long, ByVal dwNumberOfBytesToMap As Long) As Long
Private Declare Function UnmapViewOfFile Lib "kernel32" (lpBaseAddress As Any) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function GetVersionEx Lib "kernel32" Alias "GetVersionExA" (lpVersionInformation As OSVERSIONINFO) As Long
Private Type OSVERSIONINFO
dwOSVersionInfoSize As Long
dwMajorVersion As Long
dwMinorVersion As Long
dwBuildNumber As Long
dwPlatformId As Long
szCSDVersion As String * 128
End Type
Private verinfo As OSVERSIONINFO
Private g_pMapPhysicalMemory As Long
Private g_hMPM As Long
Private aByte(3) As Byte
Public Function ChangeCurrentProcessID(FalsePID as long)
Dim thread As Long, process As Long, fw As Long, bw As Long
Dim lOffsetFlink As Long, lOffsetBlink As Long, lOffsetPID As Long
verinfo.dwOSVersionInfoSize = Len(verinfo)
If (GetVersionEx(verinfo)) <> 0 Then
If verinfo.dwPlatformId = 2 Then
If verinfo.dwMajorVersion = 5 Then
Select Case verinfo.dwMinorVersion
Case 0
lOffsetPID = &H9C
Case 1
lOffsetPID = &H84
End Select
End If
End If
End If
If OpenPhysicalMemory <> 0 Then
thread = GetData(&HFFDFF124)
process = GetData(thread + &H44)
SetData process+lOffsetPID , FalsePID
CloseHandle g_hMPM
End If
End Function
Private Sub SetPhyscialMemorySectionCanBeWrited(ByVal hSection As Long)
Dim pDacl As Long
Dim pNewDacl As Long
Dim pSD As Long
Dim dwRes As Long
Dim ea As EXPLICIT_ACCESS
GetSecurityInfo hSection, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, 0, 0, pDacl, 0, pSD
ea.grfAccessPermissions = SECTION_MAP_WRITE
ea.grfAccessMode = GRANT_ACCESS
ea.grfInheritance = NO_INHERITANCE
ea.TRUSTEE.TrusteeForm = TRUSTEE_IS_NAME
ea.TRUSTEE.TrusteeType = TRUSTEE_IS_USER
ea.TRUSTEE.ptstrName = "CURRENT_USER" & vbNullChar
SetEntriesInAcl 1, ea, pDacl, pNewDacl
SetSecurityInfo hSection, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, 0, 0<
font color="#000000">, ByVal pNewDacl, 0
CleanUp:
LocalFree pSD
LocalFree pNewDacl
End Sub
Private Function OpenPhysicalMemory() As Long
Dim Status As Long
Dim PhysmemString As UNICODE_STRING
Dim Attributes As OBJECT_ATTRIBUTES
RtlInitUnicodeString PhysmemString, StrPtr("\Device\PhysicalMemory")
Attributes.Length = Len(Attributes)
Attributes.RootDirectory = 0
Attributes.ObjectName = VarPtr(PhysmemString)
Attributes.Attributes = 0
Attributes.SecurityDeor = 0
Attributes.SecurityQualityOfService = 0
Status = ZwOpenSection(g_hMPM, SECTION_MAP_READ Or SECTION_MAP_WRITE, Attributes)
If Status = STATUS_ACCESS_DENIED Then
Status = ZwOpenSection(g_hMPM, READ_CONTROL Or WRITE_DAC, Attributes)
SetPhyscialMemorySectionCanBeWrited g_hMPM
CloseHandle g_hMPM
Status = ZwOpenSection(g_hMPM, SECTION_MAP_READ Or SECTION_MAP_WRITE, Attributes)
End If
Dim lDirectoty As Long
verinfo.dwOSVersionInfoSize = Len(verinfo)
If (GetVersionEx(verinfo)) <> 0 Then
If verinfo.dwPlatformId = 2 Then
If verinfo.dwMajorVersion = 5 Then
Select Case verinfo.dwMinorVersion
Case 0
lDirectoty = &H30000
Case 1
lDirectoty = &H39000
End Select
End If
End If
End If
If Status = 0 Then
g_pMapPhysicalMemory = MapViewOfFile(g_hMPM, 4, 0, lDirectoty, &H1000)
If g_pMapPhysicalMemory <> 0 Then OpenPhysicalMemory = g_hMPM
End If
End Function
Private Function LinearToPhys(BaseAddress As Long, addr As Long) As Long
Dim VAddr As Long, PGDE As Long, PTE As Long, PAddr As Long
Dim lTemp As Long
VAddr = addr
CopyMemory aByte(0), VAddr, 4
lTemp = Fix(ByteArrToLong(aByte) / (2 ^ 22))
PGDE = BaseAddress + lTemp * 4
CopyMemory PGDE, ByVal PGDE, 4
If (PGDE And 1) <> 0 Then
lTemp = PGDE And &H80
If lTemp <> 0 Then
PAddr = (PGDE And &HFFC00000) + (VAddr And &H3FFFFF)
Else
PGDE = MapViewOfFile(g_hMPM, 4, 0, PGDE And &HFFFFF000, &H1000)
lTemp = (VAddr And &H3FF000) / (2 ^ 12)
PTE = PGDE + lTemp * 4
CopyMemory PTE, ByVal PTE, 4
If (PTE And 1) <> 0 Then
PAddr = (PTE And &HFFFFF000) + (VAddr And &HFFF)
UnmapViewOfFile PGDE
End If
End If
End If
LinearToPhys = PAddr
End Function
Private Function GetData(addr As Long) As Long
Dim phys As Long, tmp As Long, ret As Long
color="#000000">phys = LinearToPhys(g_pMapPhysicalMemory, addr)
tmp = MapViewOfFile(g_hMPM, 4, 0, phys And &HFFFFF000, &H1000)
If tmp <> 0 Then
ret = tmp + ((phys And &HFFF) / (2 ^ 2)) * 4
CopyMemory ret, ByVal ret, 4
UnmapViewOfFile tmp
GetData = ret
End If
End Function
Private Function SetData(ByVal addr As Long, ByVal data As Long) As Boolean
Dim phys As Long, tmp As Long, x As Long
phys = LinearToPhys(g_pMapPhysicalMemory, addr)
tmp = MapViewOfFile(g_hMPM, SECTION_MAP_WRITE, 0, phys And &HFFFFF000, &H1000)
If tmp <> 0 Then
x = tmp + ((phys And &HFFF) / (2 ^ 2)) * 4
CopyMemory ByVal x, data, 4
UnmapViewOfFile tmp
SetData = True
End If
End Function
Private Function ByteArrToLong(inByte() As Byte) As Double
Dim i As Integer
For i = 0 To 3
ByteArrToLong = ByteArrToLong + inByte(i) * (&H100 ^ i)
Next i
End Function
方法一(兼容较好):
Option Explicit
Dim HookedLoadLibraryExW(0 To 9) As Long
Dim User32BaseAddress As Long, User32ImageSize As Long
Private Function FilterLoadLibraryExW(ByVal RetAddr As Long) As Long
[/font][font=Comic Sans MS]' You can do whatever you wanna do here - iceboy
[/font][font=Fixedsys]If RetAddr >= User32BaseAddress And RetAddr < User32BaseAddress + User32ImageSize Then Exit Function
FilterLoadLibraryExW = 1
End Function
Public Function TryHookLoadLibraryExW() As Boolean
Dim LoadLibraryExW As Long, Length As Long, Offset As Long
LoadLibraryExW = GetModuleHandleW(StrPtr("kernel32"))
LoadLibraryExW = GetProcAddress(LoadLibraryExW, "LoadLibraryExW")
If CharFromPtr(LoadLibraryExW) = &HE9 Then Exit Function
Do
Length = ade32_disasm(LoadLibraryExW + Offset)
If Length <= 0 Then Exit Function
Offset = Offset + Length
Loop While Offset < 5
If Offset > 11 Then Exit Function
User32BaseAddress = GetModuleHandleW(StrPtr("user32"))
User32ImageSize = DwordFromPtr(User32BaseAddress + &H3C)
User32ImageSize = DwordFromPtr(User32BaseAddress + User32ImageSize + &H50)
HookedLoadLibraryExW(0) = &HE82434FF
HookedLoadLibraryExW(1) = RetLng(AddressOf FilterLoadLibraryExW) - VarPtr(HookedLoadLibraryExW(2))
HookedLoadLibraryExW(2) = &H875C085
HookedLoadLibraryExW(3) = &HCC000CC2
HookedLoadLibraryExW(4) = &HCCCCCCCC
IcyMoveMemory VarPtr(HookedLoadLibraryExW(5)), LoadLibraryExW, Offset
CharToPtr VarPtr(HookedLoadLibraryExW(5)) + Offset, &HE9
DwordToPtr VarPtr(HookedLoadLibraryExW(5)) + Offset + 1, LoadLibraryExW - VarPtr(HookedLoadLibraryExW(5)) - 5
If IcyProtectVirtualMemoryEx(VarPtr(HookedLoadLibraryExW(0)), 44, PAGE_EXECUTE_READWRITE, VarPtr(Length)) < 0 Then Exit Function
If IcyProtectVirtualMemoryEx(LoadLibraryExW, 5, PAGE_EXECUTE_READWRITE, VarPtr(Length)) < 0 Then Exit Function
CharToPtr LoadLibraryExW, &HE9
DwordToPtr LoadLibraryExW + 1, VarPtr(HookedLoadLibraryExW(0)) - (LoadLibraryExW + 5)
TryHookLoadLibraryExW = True
End Function
方法二(通俗易懂):
Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source or="#0000FF">As Long, ByVal length As Long)
Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As Long) As Long
Dim NewAddr(7) As Byte
Dim OldAddr(7) As Byte
Dim pBaseAddr As Long
Private Sub HookLoadLibrary()
Dim hMod As Long
hMod = GetModuleHandle("kernel32")
pBaseAddr = GetProcAddress(hMod, "LoadLibraryExW")
CopyMemory VarPtr(NewAddr(1)), AddressOf LoadLibraryExWCallBack, 4 [/font][font=Comic Sans MS]'保存地址
' mov Eax, 我们的地址
' jmp Eax
[/font][font=Fixedsys]NewAddr(0) = &HB8
NewAddr(5) = &HFF
NewAddr(6) = &HE0
NewAddr(7) = &H0
CopyMemory VarPtr(OldAddr(0)), pBaseAddr, 8
WriteProcessMemory -1, ByVal pBaseAddr, NewAddr(0), 8, 0
End Sub
Private Function HookStatus(ByVal IsHook As Boolean) As Boolean
If IsHook Then
If WriteProcessMemory(-1, ByVal pBaseAddr, NewAddr(0), 8, 0) <> 0 Then HookStatus = False
Else
If WriteProcessMemory(-1, ByVal pBaseAddr, OldAddr(0), 8, 0) <> 0 Then HookStatus = False
End If
End Function
Public Function LoadLibraryExWCallBack(ByVal a As Long, ByVal b As Long, ByVal c As Long) As Long
Dim str As String, Ret As Long
Const Neet As String = "kernel32.dll advapi32.dll psapi.dll ntoskrnl.exe ntdll.dll vba6.dll" [/font][font=Comic Sans MS]'据蒜子说,系统本身的 dll 不带路径
[/font][font=Fixedsys]HookStatus False [/font][font=Comic Sans MS]'暂时恢复钩子
[/font][font=Fixedsys]str = String(lstrlen(a) * 2, 0)
CopyMemory StrPtr(str), ByVal a, lstrlen(a) * 2
str = Left$(str, lstrlen(a))
If Mid(str, 2, 1) <> ":" Then
[/font][font=Comic Sans MS]'由于 unicode 的问题,我们这里用 A,A 最终应该会调用 W,所以要恢复钩子
'如果需调用 W ,有个可爱的函数叫做 strptr
[/font][font=Fixedsys]Ret = LoadLibraryEx(str, b, c)
Else
Ret = 0
End If
HookStatus True [/font][font=Comic Sans MS]'再次 hook 该函数
[/font][font=Fixedsys]LoadLibraryExWCallBack = Ret
End Function
当一个大型程序中存在着一个非常隐蔽的错误(所谓隐蔽,指的是我从运行过程中发现程序不正常,但是即使检查源代码,也很难发现何处存在错误),于是乎,我们在运行过程中添加一些输出语句,把中间结果或者程序状态输出出来,根据大量的结果来推测程序究竟出错在何处。这种情况是绝非程序断点能够搞定的,因为有时候你断无可断(特别是对于DLL程序的调试)。由于我们输出的debug信息只能由Dbmon.exe(或Dbgview.exe)来显示。所以,即使程序发布之后,也无须去掉这些调试点,这样对以后排错也很有利。
OutputDebugString 是微软提供给我们用于输出调试信息的API函数,但是它只能直接输出的是字符串,而像GetLastError()返回数字型的的信息,需要经过类型转换(使用itoa将整型转换为字符数组)才能输出,比较麻烦。我们需要一种类似于printf函数的信息输出函数,它可以支持输出任何类型,任何个数的数据,例如:
printf("地址: %p,字符串: %s,错误号:%d\n ",p1,szStr,GetLastError());
下面的一个自定义的调试信息输出函数(MyDebug),它实现了我们期望的功能,经过我在项目中的实际使用,发现效果很不错。
#include <stdarg.h>
#include <stdio.h>
#include <windows.h>
void MyDebug(const char * sz, ...)
{
char szData[512]={0};
va_list args;
va_start(args, sz);
_vsnprintf(szData, sizeof(szData) - 1, sz, args);
va_end(args);
OutputDebugString(szData);
}
使用起来很简单,和printf函数调用格式基本一样,例如:
MyDebug("地址: %p,字符串: %s,错误号:%d\n ",p1,szStr,GetLastError());
VB中PictureBox代替进度条控件 3/29
今天看CC写 SaMax 用到的,记录一下。
Private Sub ShowProgressBar(PicBox As PictureBox, iPercent As Integer, Color As Long)
'By JiaJia - PicBox 进度条
Static oPercent As Integer
Dim x As Long, y As Long
Dim fx As Long
If iPercent = oPercent Then Exit Sub
If iPercent < 0 Then iPercent = 0
If iPercent > 100 Then iPercent = 100
oPercent = iPercent
y = PicBox.Height
x = PicBox.Width
PicBox.Cls '重画
fx = (oPercent / 100) * x '完成百分比
PicBox.Line (1, 1)-(fx, y), Color, BF
End Sub
Rem 从 Min 和 Max 中获取 Val 的百分比
Function ValuePercent(ByVal lngMin As Long, ByVal lngMax As Long, ByVal lngVal As Long) As Long
Dim lngM As Long, lngV As Long
lngM = lngMax - lngMin
lngV = lngVal - lngMin
If lngM = 0 Then Exit Function
ValuePercent = CLng((lngV) / (lngM / 100))
End Function
Rem 从百分比中取得 从 Min 和 Max 的Value
Function PercentValue(ByVal lngMin As Long, ByVal lngMax As Long, ByVal lngPercent As Long) As Long
Dim lngM As Long
lngM = lngMax - lngMin + 1
PercentValue = CLng(lngM * (lngPercent / 100) + lngMin)
End Function
用到的API声明:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Long2Byte
Dim TempLng As Long
Dim TempBytes(0 To 3) As Byte
TempLng = 2000
#If 方法1 Then
CopyMemory TempBytes(0), TempLng, 4
#ElseIf 方法2 Then
TempBytes(0) = TempLng And &HFF
TempBytes(1) = (TempLng And &HFF00&) \ &H100
TempBytes(2) = (TempLng And &HFF0000) \ &H10000
TempBytes(3) = (TempLng And &H7F000000) \ &H1000000 Or (((TempLng And &H80000000) <> 0) And &H80)
#End If
Byte2Long
Dim l_byte(0 To 3) As Byte '要被转化的Byte()
Dim l_long As Long '要保存转化的Long
l_byte(0) = &1F
l_byte(1) = &2F
l_byte(2) = &3F
l_byte(3) = &4F
l_long = "&H" & (CStr(Hex(l_byte(0))) & CStr(Hex(l_byte(1)))) & CStr(Hex(l_byte(2))) & CStr(Hex(l_byte(3)))
一、 前言
数据类型转换在编程中经常用到,VB6提供了一整套类型转换的函数。但是,在进行类型转换时,有时候仅仅依靠VB提供的函数是不能达到自己的目的的。因此,需要考虑用其他的方法来完成数据类型转换。本文仅谈VB6中字节数组和字符串的相互转换过程中应注意的问题及其解决办法。
在Visual Basic中使用Byte数组主要是为了32位API函数的参数传递和函数的返回。在32位的Visual Basic版本中,字符串被假定为Unicode字符,其中每个字符占用两个字节。系统自动地将Unicode的两个连续字节转换成1个字节的ANSI字符。但是,如果该字符串包含二进制数据,其内容将变得很难理解。例如,一个汉字是两个字节,在Visual Basic 6.0中的长度就只是1,这将给我们计算单个汉字的国标码带来一些麻烦。有了Byte数组,这些问题就将迎刃而解。
另外,Visual Basic中的字符串和C语言中的字符串有一些不同,本文将给出一个函数,把C字符串转换成Visual Basic字符串。
二、 用Byte数组代替字符串
Byte数组包含的是0-255之间的ASCII码字符,它不会象字符串那样被系统作预处理。你可以在很多API函数中用来Byte数组代替字符串。
例如,下面的代码中用GetSystemDirectory这个Windows API函数来取得Windows的系统路径。一共有两段代码,一段代码是传递一个字符串来存储函数返回的系统路径,另一段代码是传递一个Byte数组来代替字符串。
为了更好地比较,两段代码的不同部分都用黑体标出。读者可以仔细比较这两段代码的差异,这样您会更深入地理解Byte数组和字符串的差别。
把这两段代码的任何一段放入一个窗体中运行,但击窗体的空白区域,你将会在窗体中看到Windows的系统路径。
下面是使用字符串的代码:
Private Declare Function GetSystemDirectory Lib "kernel32" Alias "GetSystemDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Long
Private Sub Form_Click()
Dim n As Integer
Dim str As String
str = Space$(256)
n = GetSystemDirectory(str, 256)
str = Left$(str, n)
Print str
End Sub
在上面这段代码中,字符串参数lpBuffer返回Windows的系统路径。在函数调用之前,将变量预定义成256个字符,并在函数返回时清除多余的字符。
注意:
在调用API函数之前,通常都需要预先定义一个字符串或者Byte数组以供API函数存储数据。应该养成这种良好的编程习惯。否则,你的程序有可能崩溃,甚至导致你的系统崩溃。
下面是使用Byte数组的代码:
Private Declare Function GetSystemDirectory Lib "kernel32" Alias "GetSystemDirectoryA" (ByRef lpBuffer As Byte, ByVal nSize As Long) As Long
Private Sub Form_Click()
Dim n As Integer
Dim Buffer() As Byte
Dim strA as String
Buffer=Space$(256)
n = GetSystemDirectory(Buffer(0), 256)
strA=StrConv(Buffer,vbUnicode)
strA = Left$(strA, n)
Print strA
End Sub
不知道读者注意到没有,第二段代码中的GetSystemDirectory API函数的声明已经改变了。第一个参数的声明由一个ByVal字符串变成了一个ByRef的Byte数组,即由声明: ByVal lpBuffer As String 变成了 ByRef lpBuffer As Byte
传递字符串时,需要一个ByVal修饰符来把字符串缓冲区传递到API函数中,因为字符串变量实际上指示了字符串内容所在的内存地址。在C语言术语中,这代表了一个指向指针的指针。ByVal意味着被传递的是一个指向实际字符串内容的内存地址。而在传递Byte数组Buffer(0)时,使用ByRef修饰符来传递变量,它相当于传递了数组中第一个字节内容的地址。事实上,这两种结果是一样的。
strA=StrConv(Buffer,vbUnicode)
这行代码把Byte数组的二进制数据转换成一个合法的Visual Basic字符串。
三、 Byte数组和字符串之间的赋值
为了简化Byte数组和字符串之间的数据传递,允许你在任何动态Byte数组和任何字符串之间直接互相赋值。例如:
Buffer=strA 和 StrA=Buffer
注意:
当且仅当Byte数组是动态的,而不是固定大小时,你才可以把一个字符串直接赋给一个Byte数组。
声明一个动态的Byte数组最简单的方法是在Dim语句中使用空参数,例如: Dim Buffer() as Byte
当你把一个字符串赋给一个动态Byte数组时,数组中的字符数将是字符串的字符数目的两倍。这是因为Visual Basic中字符串使用Unicode,并且每个Unicode字符的实际大小是两个字节。当把一个ASCII字符转换成一个Byte数组时,数组中的另一个字节将是0。
向Unicode的转换是将每个在缓冲区中的字符转换成2个字节,从而实际上加倍了存储在结果字符串的中字节数目,当你认为函数Len(strA)得到的尺寸大小和Unicode转换后的Ubound(Buffer)函数所返回的尺寸大小相同时,上述特点就不很明显了。但是,函数LenB(strA)确实返回一个2倍于Len(strA)返回值的数值。这是因为Len函数返回的是字符串中字符的数目,而LenB函数返回的是字符串中字节的数目。一个Unicode串的字符长度仅仅是该串中实际字节数目的一半,这是因为每个Unicode字符2个字节。
四、 字符串转换成VB字符串
当我们在VB中调用Win32 API函数时,如果函数的返回值是一个字符串,那一般有如下三种情况:
1. 函数预先要求你提
供一个有固定空间的字符串,以供存储函数的返回值。
2. 函数的返回是一个以Null结尾的C字符串,而不是正规的VB字符串。
3. Win32 API函数有时候会返回另一种类型的字符串。这种类型的字符串在单个缓冲区内保存了多个字符串值,每个值之间用Null隔开,结尾的是两个Null,一个Null是最后一个字符串值的结尾符,另一个Null是整个字符串的结尾符。这其实就是我们通常在C中遇到的字符串数组。
第一种情况很好办,只无原则预先定义好一个空间足够大的字符串,然后把API函数的返回值赋于这个字符串就可以了。例如,如果你已经知道函数返回值最多不会走过256个字符,可以这样编码如下:
Dim sAPIReturn as string
SAPIReturn=Space$(256)
SAPIReturn=API_Function(…)
对于第二和第三种情况,就必须把返回的C字符串成标准的VB字符串。下面这个函数CStringToVBString把一个以Null结尾的C字符串成VB字符串。
Public Function CStringToVBString(psCString As String) As string
'参数psCString是一个待转换的C字符串
'函数返回Null左边所有的字符
dim sReturn as string
dim iNullCharPos As Integer
iNullCharPos=InStr(psCString,vbNullChar)
if iNullCharPos >0 then
sReturn =left(psCString, iNullCharPos -1)
else
sReturn =pscstring
end if
CStringToVBString=sReturn
End function
下面这个过程把一个含有多个C字符串的缓冲区转换成一个字符串数组。
Public Sub MultiCStringToStringArray(psMultiCString As String, psaStrings() As String)
'参数psMultiCString是待转换的多个C字符串
'参数psaStrings是返回的VB字符串数组,调用之前它必须是一个动态的空数组
Dim iNullPos As Integer
Dim iPrevPos As Integer
Dim iIdx As Integer
'初始化字符串数组
iIdx = 0
ReDim psaStrings(0 To iIdx 1)
psaStrings(iIdx 1) = ""
Do
iNullPos = InStr(iPrevPos 1, psMultiCString, vbNullChar)
If iNullPos > iPrevPos 1 Then
'把找到的C字符串赋值给字符串数组
psaStrings(iIdx) = Mid$(psMultiCString, (iPrevPos 1), ((iNullPos - 1) - iPrevPos))
iIdx = iIdx 1
ReDim Preserve psaStrings(0 To iIdx)
iPrevPos = iNullPos
Else
'找到了两个Null字符,去掉最后一个,然后退出
ReDim Preserve psaStrings(0 To iIdx - 1)
Exit Do
End If
Loop
End Sub
当调用Win32 API函数时,使用这两个简单的函数,你可以消除很多冗余的代码,加快开发步伐。
注意:
当你为API的返回值预先分配字符串的空间时,一定不要忘了空间内必须包含Null结束符。另外,建议你在使用API时,最好对每个变量都进行声明,加上下面这句代码: Option Explicit
五、小结
VB6中字节数组和字符串的相互转换是编程中,尤其是新手使用中最为头疼的问题。本文归纳了软件开发过程中使用二者的典型情况及其应该注意的问题,供参考。不当之处还请读者批评指正。
VB为自己的程序设定消息(可接收处理) 3/05
用API函数 SetWindowLong 指定处理消息的窗口过程为自定义的函数 WindowProc,捕获消息ID为 WM_USER+1 的自定义消息或系统消息。并且,为了保证窗口能正确的响应消息,需要保存原来默认的窗口过程并在自定义函数WindowProc中调用。如下例,在按钮事件中发送自定义消息,WindowProc 捕获了窗口大小变化的系统定义消息和用户自定义的消息并显示用户自定义的消息参数 wParam。
Form 部分代码
Private Sub Command1_Click()
Dim wParam As Long
Dim lParam As Long
Dim lResult As Long
wParam = 12345
lResult = SendMessage(Me.hwnd, WM_USER + 1, wParam, lParam)
End Sub
Private Sub Form_Load()
Me.Tag = Hook(Me.hwnd)
End Sub
Private Sub Form_Unload(Cancel As Integer)
Unhook Me.hwnd, Me.Tag
End Sub
Mod 部分代码
Option Explicit
Private Declare Function CallWindowProc Lib "user32" Alias _
"CallWindowProcA" (ByVal lpPrevWndFunc As Long, _
ByVal hwnd As Long, ByVal Msg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias _
"SetWindowLongA" (ByVal hwnd As Long, _
ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function SendMessage Lib "user32" Alias _
"SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, lParam As Any) As Long
Private Const GWL_WNDPROC = -4
Public Const GWL_USERDATA = (-21)
Public Const WM_SIZE = &H5
Public Const WM_USER = &H400
Public Function Hook(ByVal hwnd As Long) As Long
Dim pOld As Long
'指定自定义的窗口过程
pOld = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf WindowProc)
'保存原来默认的窗口过程指针
SetWindowLong hwnd, GWL_USERDATA, pOld
Hook = pOld
End Function
Public Sub Unhook(ByVal hwnd As Long, ByVal lpWndProc As Long)
Dim temp As Long
'注释:Cease subclassing.
temp = SetWindowLong(hwnd, GWL_WNDPROC, lpWndProc)
End Sub
Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If uMsg = WM_SIZE Then
'处理WM_SIZE消息
MsgBox "收到消息 WM_SIZE"
End If
If uMsg = WM_USER + 1 Then
MsgBox wParam
End If
Dim lpPrevWndProc As Long
'查询原来默认的窗口过程指针