枚举当前系统中硬盘的方法(1)

枚举的结果是 “\\.\PhysicalDriveX”这样的形式。

需要注意的是:程序必须在 Administrator 权限下运行。即运行时,要求 “Run as Administrator”。同样在VS2008中编译时,由于权限不够,可能无法看到运行结果。本程序使用的是最传统的方法,使用CreateFile尝试不同的 PhysicalDirve+Number如果能够打开就是存在的。

#include "stdafx.h"
#include   <windows.h>   
#include   <stdio.h>   
#include   <conio.h> 

int _tmain(int argc, _TCHAR* argv[])
{
	int    i;
	TCHAR  Device[0x20];
	HANDLE hDevice;

	for (i=0;i<10;i++)

	{
		wsprintf(Device,TEXT("\\\\.\\PhysicalDrive%d"), i);
		
		hDevice = CreateFile(Device, 
						GENERIC_READ,
						FILE_SHARE_READ, 
						NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);

		if (hDevice!=INVALID_HANDLE_VALUE) {printf("%ws \n",Device); }
		CloseHandle( hDevice );   
	}



	_getch(); 
	return 0;
}

 

phylist1

获得当前系统增加/移除USB设备名称的方法 VC (2)

上面一篇文章给出了 Delphi 版本获得当前系统增加/移除USB设备名称的方法。这篇给出一个VC 的版本。编译器为 VS2008 Express,代码如下

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#include <dbt.h>
#include <Shellapi.h>
// This GUID is for all USB 
/* A5DCBF10-6530-11D2-901F-00C04FB951ED */
GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED };
// For informational messages and window titles
PWSTR g_pszAppName;
// Forward declarations
void OutputMessage(HWND hOutWnd, WPARAM wParam, LPARAM lParam);
void ErrorHandler(LPTSTR lpszFunction);
//
// DoRegisterDeviceInterfaceToHwnd
//
BOOL DoRegisterDeviceInterfaceToHwnd( 
    IN GUID InterfaceClassGuid, 
    IN HWND hWnd,
    OUT HDEVNOTIFY *hDeviceNotify 
)
// Routine Description:
//     Registers an HWND for notification of changes in the device interfaces
//     for the specified interface class GUID.
// Parameters:
//     InterfaceClassGuid - The interface class GUID for the device 
//         interfaces.
//     hWnd - Window handle to receive notifications.
//     hDeviceNotify - Receives the device notification handle. On failure, 
//         this value is NULL.
// Return Value:
//     If the function succeeds, the return value is TRUE.
//     If the function fails, the return value is FALSE.
// Note:
//     RegisterDeviceNotification also allows a service handle be used,
//     so a similar wrapper function to this one supporting that scenario
//     could be made from this template.
{
    DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
    ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
    NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    NotificationFilter.dbcc_classguid = InterfaceClassGuid;
    *hDeviceNotify = RegisterDeviceNotification( 
        hWnd,                       // events recipient
        &NotificationFilter,        // type of device
        DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
        );
    if ( NULL == *hDeviceNotify ) 
    {
        ErrorHandler(TEXT("RegisterDeviceNotification"));
        return FALSE;
    }
    return TRUE;
}
//
// MessagePump
//
void MessagePump(
    HWND hWnd
)
// Routine Description:
//     Simple main thread message pump.
//
// Parameters:
//     hWnd - handle to the window whose messages are being dispatched
// Return Value:
//     None.
{
    MSG msg; 
    int retVal;
    // Get all messages for any window that belongs to this thread,
    // without any filtering. Potential optimization could be
    // obtained via use of filter values if desired.
    while( (retVal = GetMessage(&msg, NULL, 0, 0)) != 0 ) 
    { 
        if ( retVal == -1 )
        {
            ErrorHandler(TEXT("GetMessage"));
            break;
        }
        else
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    } 
}
//
// WinProcCallback
//
INT_PTR WINAPI WinProcCallback(
                              HWND hWnd,
                              UINT message,
                              WPARAM wParam,
                              LPARAM lParam
                              )
// Routine Description:
//     Simple Windows callback for handling messages.
//     This is where all the work is done because the example
//     is using a window to process messages. This logic would be handled 
//     differently if registering a service instead of a window.
// Parameters:
//     hWnd - the window handle being registered for events.
//     message - the message being interpreted.
//     wParam and lParam - extended information provided to this
//          callback by the message sender.
//     For more information regarding these parameters and return value,
//     see the documentation for WNDCLASSEX and CreateWindowEx.
{
    LRESULT lRet = 1;
    static HDEVNOTIFY hDeviceNotify;
    static HWND hEditWnd;
    static ULONGLONG msgCount = 0;
	
	switch (message)
    {
    case WM_CREATE:
        //
        // This is the actual registration., In this example, registration 
        // should happen only once, at application startup when the window
        // is created.
        //
        // If you were using a service, you would put this in your main code 
        // path as part of your service initialization.
        //
        if ( ! DoRegisterDeviceInterfaceToHwnd(
                        GUID_DEVINTERFACE_USB_DEVICE, 
                        hWnd,
                        &hDeviceNotify) )
        {
            // Terminate on failure.
            ErrorHandler(TEXT("DoRegisterDeviceInterfaceToHwnd"));
            ExitProcess(1);
        }

        //
        // Make the child window for output.
        //
        hEditWnd = CreateWindow(TEXT("EDIT"),// predefined class 
                                NULL,        // no window title 
                                WS_CHILD | WS_VISIBLE | WS_VSCROLL | 
                                ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, 
                                0, 0, 0, 0,  // set size in WM_SIZE message 
                                hWnd,        // parent window 
                                (HMENU)1,    // edit control ID 
                                (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE), 
                                NULL);       // pointer not needed
        if ( hEditWnd == NULL )
        {
            // Terminate on failure.
            ErrorHandler(TEXT("CreateWindow: Edit Control"));
            ExitProcess(1);
        }
        // Add text to the window. 
        SendMessage(hEditWnd, WM_SETTEXT, 0, 
            (LPARAM)TEXT("Registered for USB device notification...\n"));
        break;
    case WM_SETFOCUS: 
        SetFocus(hEditWnd);
        break;
    case WM_SIZE: 
        // Make the edit control the size of the window's client area. 
        MoveWindow(hEditWnd, 
                   0, 0,                  // starting x- and y-coordinates 
                   LOWORD(lParam),        // width of client area 
                   HIWORD(lParam),        // height of client area 
                   TRUE);                 // repaint window
        break;
    case WM_DEVICECHANGE :
    {
        //
        // This is the actual message from the interface via Windows messaging.
        // This code includes some additional decoding for this particular device type
        // and some common validation checks.
        //
        // Note that not all devices utilize these optional parameters in the same
        // way. Refer to the extended information for your particular device type 
        // specified by your GUID.
        //
        PDEV_BROADCAST_DEVICEINTERFACE b = (PDEV_BROADCAST_DEVICEINTERFACE) lParam;
        TCHAR strBuff[256];
        // Output some messages to the window.
        switch (wParam)
        {
        case DBT_DEVICEARRIVAL:
            msgCount++;
            StringCchPrintf(
                strBuff, 256, 
                TEXT("Message %d: DBT_DEVICEARRIVAL\n"), msgCount);
        OutputMessage(hEditWnd, wParam, (LPARAM)strBuff);
            StringCchPrintf(
                strBuff, 256, 
				TEXT("USB Device information: \n"));
        OutputMessage(hEditWnd, wParam, (LPARAM)strBuff);
		//MultiByteToWideChar(CP_ACP, 0, strBuff,256,b->dbcc_name,sizeof(b->dbcc_name));
        OutputMessage(hEditWnd, wParam, (LPARAM)b->dbcc_name);
            break;
        case DBT_DEVICEREMOVECOMPLETE:
            msgCount++;
            StringCchPrintf(
                strBuff, 256, 
                TEXT("Message %d: DBT_DEVICEREMOVECOMPLETE\n"), msgCount);
            break;
        case DBT_DEVNODES_CHANGED:
            msgCount++;
            StringCchPrintf(
                strBuff, 256, 
                TEXT("Message %d: DBT_DEVNODES_CHANGED\n"), msgCount);
            break;
        default:
            msgCount++;
            StringCchPrintf(
                strBuff, 256, 
                TEXT("Message %d: WM_DEVICECHANGE message received, value %d unhandled.\n"), 
                msgCount, wParam);
            break;
        }
        OutputMessage(hEditWnd, wParam, (LPARAM)strBuff);
    }
            break;
    case WM_CLOSE:
        if ( ! UnregisterDeviceNotification(hDeviceNotify) )
        {
           ErrorHandler(TEXT("UnregisterDeviceNotification")); 
        }
        DestroyWindow(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        // Send all other messages on to the default windows handler.
        lRet = DefWindowProc(hWnd, message, wParam, lParam);
        break;
    }
    return lRet;
}
#define WND_CLASS_NAME TEXT("SampleAppWindowClass")
//
// InitWindowClass
//
BOOL InitWindowClass()
// Routine Description:
//      Simple wrapper to initialize and register a window class.
// Parameters:
//     None
// Return Value:
//     TRUE on success, FALSE on failure.
// Note: 
//     wndClass.lpfnWndProc and wndClass.lpszClassName are the
//     important unique values used with CreateWindowEx and the
//     Windows message pump.
{
    WNDCLASSEX wndClass;
    wndClass.cbSize = sizeof(WNDCLASSEX);
    wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
    wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProcCallback);
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hIcon = LoadIcon(0,IDI_APPLICATION);
    wndClass.hbrBackground = CreateSolidBrush(RGB(192,192,192));
    wndClass.hCursor = LoadCursor(0, IDC_ARROW);
    wndClass.lpszClassName = WND_CLASS_NAME;
    wndClass.lpszMenuName = NULL;
    wndClass.hIconSm = wndClass.hIcon;

    if ( ! RegisterClassEx(&wndClass) )
    {
        ErrorHandler(TEXT("RegisterClassEx"));
        return FALSE;
    }
    return TRUE;
}
//
// main
//
int __stdcall _tWinMain(
                      HINSTANCE hInstanceExe, 
                      HINSTANCE, // should not reference this parameter
                      PTSTR lpstrCmdLine, 
                      int nCmdShow
                      )
{
//
// To enable a console project to compile this code, set
// Project->Properties->Linker->System->Subsystem: Windows.
//
    int nArgC = 0;
    PWSTR* ppArgV = CommandLineToArgvW(lpstrCmdLine, &nArgC);
    g_pszAppName = ppArgV[0];
    if ( ! InitWindowClass() )
    {
        // InitWindowClass displays any errors
        return -1;
    }
    // Main app window
    HWND hWnd = CreateWindowEx(
                    WS_EX_CLIENTEDGE | WS_EX_APPWINDOW,
                    WND_CLASS_NAME,
                    g_pszAppName,
                    WS_OVERLAPPEDWINDOW, // style
                    CW_USEDEFAULT, 0, 
                    640, 480,
                    NULL, NULL, 
                    hInstanceExe, 
                    NULL);
    
    if ( hWnd == NULL )
    {
        ErrorHandler(TEXT("CreateWindowEx: main appwindow hWnd"));
        return -1;
    }
    // Actually draw the window.
    ShowWindow(hWnd, SW_SHOWNORMAL);
    UpdateWindow(hWnd);
    // The message pump loops until the window is destroyed.
    MessagePump(hWnd);
    return 1;
}
//
// OutputMessage
//
void OutputMessage(
    HWND hOutWnd, 
    WPARAM wParam, 
    LPARAM lParam
)
// Routine Description:
//     Support routine.
//     Send text to the output window, scrolling if necessary.
// Parameters:
//     hOutWnd - Handle to the output window.
//     wParam  - Standard windows message code, not used.
//     lParam  - String message to send to the window.
// Return Value:
//     None
// Note:
//     This routine assumes the output window is an edit control
//     with vertical scrolling enabled.
//     This routine has no error checking.
{
    LRESULT   lResult;
    LONG      bufferLen;
    LONG      numLines;
    LONG      firstVis;
  
    // Make writable and turn off redraw.
    lResult = SendMessage(hOutWnd, EM_SETREADONLY, FALSE, 0L);
    lResult = SendMessage(hOutWnd, WM_SETREDRAW, FALSE, 0L);
    // Obtain current text length in the window.
    bufferLen = SendMessage (hOutWnd, WM_GETTEXTLENGTH, 0, 0L);
    numLines = SendMessage (hOutWnd, EM_GETLINECOUNT, 0, 0L);
    firstVis = SendMessage (hOutWnd, EM_GETFIRSTVISIBLELINE, 0, 0L);
    lResult = SendMessage (hOutWnd, EM_SETSEL, bufferLen, bufferLen);
    // Write the new text.
    lResult = SendMessage (hOutWnd, EM_REPLACESEL, 0, lParam);
    // See whether scrolling is necessary.
    if (numLines > (firstVis + 1))
    {
        int        lineLen = 0;
        int        lineCount = 0;
        int        charPos;
        // Find the last nonblank line.
        numLines--;
        while(!lineLen)
        {
            charPos = SendMessage(
                hOutWnd, EM_LINEINDEX, (WPARAM)numLines, 0L);
            lineLen = SendMessage(
                hOutWnd, EM_LINELENGTH, charPos, 0L);
            if(!lineLen)
                numLines--;
        }
        // Prevent negative value finding min.
        lineCount = numLines - firstVis;
        lineCount = (lineCount >= 0) ? lineCount : 0;
        
        // Scroll the window.
        lResult = SendMessage(
            hOutWnd, EM_LINESCROLL, 0, (LPARAM)lineCount);
    }
    // Done, make read-only and allow redraw.
    lResult = SendMessage(hOutWnd, WM_SETREDRAW, TRUE, 0L);
    lResult = SendMessage(hOutWnd, EM_SETREADONLY, TRUE, 0L);
} 
//
// ErrorHandler
//
void ErrorHandler(
   LPTSTR lpszFunction
) 
// Routine Description:
//     Support routine.
//     Retrieve the system error message for the last-error code
//     and pop a modal alert box with usable info.
// Parameters:
//     lpszFunction - String containing the function name where 
//     the error occurred plus any other relevant data you'd 
//     like to appear in the output.
// Return Value:
//     None
// Note:
//     This routine is independent of the other windowing routines
//     in this application and can be used in a regular console
//     application without modification.
{
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError();
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );
    // Display the error message and exit the process.
    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf)
                  + lstrlen((LPCTSTR)lpszFunction)+40)
                  * sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, g_pszAppName, MB_OK);
    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
}

 

上面的代码改编自 MSDN :http://msdn.microsoft.com/en-us/library/windows/desktop/aa363432(v=vs.85).aspx

运行结果

usbevent

此外,本文还参考了如下文章

http://blog.csdn.net/daoxwj/article/details/8819784
http://www.codeproject.com/Articles/14500/Detecting-Hardware-Insertion-and-or-Removal

下载
vcusb

获得当前系统增加/移除USB设备名称的方法 Delphi (1)

根据 http://delphi.cjcsoft.net/viewthread.php?tid=48860 文章内容,将类似 \\?\USB#Vid_4146&Pid_d2b5#0005050400044#{a5dcbf10-6530-11d2-901f-00c04fb951ed} 转化为更易读的类型,例如

USB Inserted
Device Type = USB Mass Storage Device
Driver Name = Disk drive
Friendly Name = I0MEGA UMni1GB*IOM2J4 USB Device

之前《关于监视插入U盘的问题(1)》中给出了接收 WM_DEVICECHANGE 消息的方法。从资料中可以看出,直接处理这个消息能够获得当前系统发生变化的盘符,但是无法处理未产生盘符变化(未分区或者其他USB设备)。对于这样的问题,可以使用RegisterDeviceNotification来向系统注册一个回调函数,并且设置好只通知USB设备变化,通过这样的方法能够取得发生变化的USB设备的信息,之后再通过查询注册表对照出更易读的信息。

文章还指出这样的转换是通过读取注册表来完成的。

我编写了如下例子,实践证明这个程序工作很好。除了普通的U盘,我还地尝试了一个 USB2UART 的 Cable,可以看出工作正常。

测试环境为 Win7+Delphi10,不需要管理员权限运行程序即可。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,MahUSB;

type
  TForm1 = class(TForm)
    procedure FormShow(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
     { Private declarations }
     FUsb : TUsbClass;
     procedure UsbIN(ASender : TObject; const ADevType,ADriverName,
                     AFriendlyName : string);
     procedure UsbOUT(ASender : TObject; const ADevType,ADriverName,
                     AFriendlyName : string);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   FreeAndNil(FUsb);
end;

procedure TForm1.FormShow(Sender: TObject);
begin
   FUsb := TUsbClass.Create;
   FUsb.OnUsbInsertion := UsbIN;
   FUsb.OnUsbRemoval := UsbOUT;
end;

procedure TForm1.UsbIN(ASender : TObject; const ADevType,ADriverName,
                       AFriendlyName : string);
begin
  showmessage('USB Inserted - Device Type = ' + ADevType + #13#10 +
              'Driver Name = ' + ADriverName + #13+#10 +
              'Friendly Name = ' + AFriendlyName);
end;

procedure TForm1.UsbOUT(ASender : TObject; const ADevType,ADriverName,
                         AFriendlyName : string);
begin
  showmessage('USB Removed - Device Type = ' + ADevType + #13#10 +
               'Driver Name = ' + ADriverName + #13+#10 +
               'Friendly Name = ' + AFriendlyName);
end;

end.

============================================================================================

unit MahUSB;
interface
uses Windows, Messages, SysUtils, Classes, Registry, Masks;

type
  { Event Types }
  TOnUsbChangeEvent = procedure(AObject : TObject;
                                const ADevType,ADriverName,
                                      AFriendlyName : string) of object;

  { USB Class }
  TUsbClass = class(TObject)
  private
    FHandle : HWND;
    FOnUsbRemoval,
    FOnUsbInsertion : TOnUsbChangeEvent;
    procedure GetUsbInfo(const ADeviceString : string;
                         out ADevType,ADriverDesc,
                            AFriendlyName : string);
    procedure WinMethod(var AMessage : TMessage);
    procedure RegisterUsbHandler;
    procedure WMDeviceChange(var AMessage : TMessage);
  public
    constructor Create;
    destructor Destroy; override;
    property OnUsbInsertion : TOnUsbChangeEvent read FOnUsbInsertion
                                           write FOnUsbInsertion;
    property OnUsbRemoval : TOnUsbChangeEvent read FOnUsbRemoval
                                         write FOnUsbRemoval;
  end;

// -----------------------------------------------------------------------------
implementation

type
  // Win API Definitions
  PDevBroadcastDeviceInterface  = ^DEV_BROADCAST_DEVICEINTERFACE;
  DEV_BROADCAST_DEVICEINTERFACE = record
    dbcc_size : DWORD;
    dbcc_devicetype : DWORD;
    dbcc_reserved : DWORD;
    dbcc_classguid : TGUID;
    dbcc_name : char;
  end;

const
  // Miscellaneous
  GUID_DEVINTF_USB_DEVICE : TGUID = '{A5DCBF10-6530-11D2-901F-00C04FB951ED}';
  USB_INTERFACE                = $00000005; // Device interface class
  USB_INSERTION                = $8000;     // System detected a new device
  USB_REMOVAL                  = $8004;     // Device is gone

  // Registry Keys
  USBKEY  = 'SYSTEM\CurrentControlSet\Enum\USB\%s\%s';
  USBSTORKEY = 'SYSTEM\CurrentControlSet\Enum\USBSTOR';
  SUBKEY1  = USBSTORKEY + '\%s';
  SUBKEY2  = SUBKEY1 + '\%s';

constructor TUsbClass.Create;
begin
  inherited Create;
  FHandle := AllocateHWnd(WinMethod);
  RegisterUsbHandler;
end;

destructor TUsbClass.Destroy;
begin
  DeallocateHWnd(FHandle);
  inherited Destroy;
end;

procedure TUsbClass.GetUsbInfo(const ADeviceString : string;
                               out ADevType,ADriverDesc,
                                   AFriendlyName : string);
var sWork,sKey1,sKey2 : string;
    oKeys,oSubKeys : TStringList;
    oReg : TRegistry;
    i,ii : integer;
    bFound : boolean;
begin
  ADevType := '';
  ADriverDesc := '';
  AFriendlyName := '';

  if ADeviceString<>'' then begin
    bFound := false;
    oReg := TRegistry.Create;
    oReg.RootKey := HKEY_LOCAL_MACHINE;

    // Extract the portions of the string we need for registry. eg.
    // \\?\USB#Vid_4146&Pid_d2b5#0005050400044#{a5dcbf10- ..... -54334fb951ed}
    // We need sKey1='Vid_4146&Pid_d2b5' and sKey2='0005050400044'
    sWork := copy(ADeviceString,pos('#',ADeviceString) + 1,1026);
    sKey1 := copy(sWork,1,pos('#',sWork) - 1);
    sWork := copy(sWork,pos('#',sWork) + 1,1026);
    sKey2 := copy(sWork,1,pos('#',sWork) - 1);

    // Get the Device type description from \USB key
    if oReg.OpenKeyReadOnly(Format(USBKEY,[skey1,sKey2])) then begin
      ADevType := oReg.ReadString('DeviceDesc');
      oReg.CloseKey;
      oKeys := TStringList.Create;
      oSubKeys := TStringList.Create;

      // Get list of keys in \USBSTOR and enumerate each key
      // for a key that matches our sKey2='0005050400044'
      // NOTE : The entry we are looking for normally has '&0'
      // appended to it eg. '0005050400044&0'
      if oReg.OpenKeyReadOnly(USBSTORKEY) then begin
        oReg.GetKeyNames(oKeys);
        oReg.CloseKey;

        // Iterate through list to find our sKey2
        for i := 0 to oKeys.Count - 1 do begin
          if oReg.OpenKeyReadOnly(Format(SUBKEY1,[oKeys[i]])) then begin
            oReg.GetKeyNames(oSubKeys);
            oReg.CloseKey;

            for ii := 0 to oSubKeys.Count - 1 do begin
              if MatchesMask(oSubKeys[ii],sKey2 + '*') then begin
                // Got a match?, get the actual desc and friendly name
                if oReg.OpenKeyReadOnly(Format(SUBKEY2,[oKeys[i],
                                        oSubKeys[ii]])) then begin
                  ADriverDesc := oReg.ReadString('DeviceDesc');
                  AFriendlyName := oReg.ReadString('FriendlyName');
                  oReg.CloseKey;
                end;

                bFound := true;
              end;
            end;
          end;

          if bFound then break;
        end;
      end;

      FreeAndNil(oKeys);
      FreeAndNil(oSubKeys);
    end;

    FreeAndNil(oReg);
  end;
end;

procedure TUsbClass.WMDeviceChange(var AMessage : TMessage);
var iDevType : integer;
    sDevString,sDevType,
    sDriverName,sFriendlyName : string;
    pData : PDevBroadcastDeviceInterface;
begin
  if (AMessage.wParam = USB_INSERTION) or
     (AMessage.wParam = USB_REMOVAL) then begin
    pData := PDevBroadcastDeviceInterface(AMessage.LParam);
    iDevType := pData^.dbcc_devicetype;

    // Is it a USB Interface Device ?
    if iDevType = USB_INTERFACE then begin
      sDevString := PChar(@pData^.dbcc_name);
      GetUsbInfo(sDevString,sDevType,sDriverName,sFriendlyName);

      // Trigger Events if assigned
      if (AMessage.wParam = USB_INSERTION) and
        Assigned(FOnUsbInsertion) then
          FOnUsbInsertion(self,sDevType,sDriverName,sFriendlyName);
      if (AMessage.wParam = USB_REMOVAL) and
        Assigned(FOnUsbRemoval) then
          FOnUsbRemoval(self,sDevType,sDriverName,sFriendlyName);
    end;
  end;
end;

procedure TUsbClass.WinMethod(var AMessage : TMessage);
begin
  if (AMessage.Msg = WM_DEVICECHANGE) then
    WMDeviceChange(AMessage)
  else
    AMessage.Result := DefWindowProc(FHandle,AMessage.Msg,
                                     AMessage.wParam,AMessage.lParam);
end;

procedure TUsbClass.RegisterUsbHandler;
var rDbi : DEV_BROADCAST_DEVICEINTERFACE;
    iSize : integer;
begin
  iSize := SizeOf(DEV_BROADCAST_DEVICEINTERFACE);
  ZeroMemory(@rDbi,iSize);
  rDbi.dbcc_size := iSize;
  rDbi.dbcc_devicetype := USB_INTERFACE;
  rDbi.dbcc_reserved := 0;
  rDbi.dbcc_classguid  := GUID_DEVINTF_USB_DEVICE;
  rDbi.dbcc_name := #0;
  RegisterDeviceNotification(FHandle,@rDbi,DEVICE_NOTIFY_WINDOW_HANDLE);
end;

end.

detecusb

DetectUSB1

在Console中使用Timer (2)

之前的一篇文章展示了如何在Console中使用Timer,他使用的是CallBack方式进行回调的。这里演示直接处理 WM_TIMER 的方法,代码如下

#include “stdafx.h”
#include
#include
#include

int count =0;

VOID ShowTimer()
{
count++;
printf(“WM_TIMER in work thread count=%d\n”,count);
}

DWORD CALLBACK Thread(PVOID pvoid)
{
MSG msg;

UINT timerid=SetTimer(NULL,111,3000,NULL);
BOOL bRet;

PeekMessage(&msg,NULL,WM_USER,WM_USER,PM_NOREMOVE);
while((bRet = GetMessage(&msg,NULL,0,0))!= 0)
{
if(bRet==-1)
{
// handle the error and possibly exit
}
else
{
if (msg.message == WM_TIMER) { ShowTimer(); }
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
KillTimer(NULL,timerid);
printf(“thread end here\n”);
return 0;
}

int main()
{
DWORD dwThreadId;
printf(“Use timer in workthread of console application\n”);
HANDLE hThread = CreateThread(NULL,0,Thread,0,0,&dwThreadId);
_getch();
return 0;
}

运行结果和之前的相同。

代码和EXE在这里下载

contimer2

另外,还有如下的一个例子,来自 http://read.pudn.com/downloads121/sourcecode/windows/console/512780/1ms/1ms.cpp__.htm

//************************************************************************************
//本程序使用了两种实现定时的方法,都是基于API的,而不是MFC
//
// 1. 是使用线程定时休眠的方法,启动线程ThreadProc,在线程中输出,然后Sleep 1000ms
//
// 2. 使用定时器方法,设置一个1000ms的定时器(SetTimer),然后捕捉该消息,然后调用回调函数
// TimerProc,在该函数中输出DEF,注意,SetTimer可以直接写回调函数地址,不必捕捉消息。
// 3. 本例子已经很详细了。
// 4. 祝你好运。
//
//************************************************************************************

#include “stdafx.h”
#include

DWORD WINAPI ThreadProc( LPVOID lpParameter )//方法1
{
static DWORD tick=0;
DWORD tmp = GetTickCount();
while(1)
{
tick = GetTickCount()-tmp;//获得系统从启动开始到现在的ms数
tmp = GetTickCount();
printf(“THREAD_PROC: abc %d, tickcount: %d\n”,tick,tmp);//打印abc和上次打印到这次打印之间的毫秒间隔。
Sleep(1000);//每隔1000ms打印abc
}
}
VOID TimerProc()//方法2
{
static DWORD tick=0;
static DWORD tmp = GetTickCount();

tick = GetTickCount()-tmp;
tmp = GetTickCount();
printf(” TimerProc________________def %d\n”,tick);

}

int main()
{
DWORD dwthread;
::CreateThread(NULL,0,ThreadProc,(LPVOID)0,1024,&dwthread);//生成一个线程,在该线程里每1000ms输出一个”abc”,然后SLEEP
SetTimer(0,0,1000,0);//设置一个定时器,定时器的回调函数为0,仅产生定时器消息,在main函数里捕捉该消息
MSG msg;
bool flag;

while(1)//该循环捕捉定时器消息,并且防止main函数退出
{
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
while( (flag = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (flag == -1)
{
// handle the error and possibly exit
}
else if(msg.message==WM_TIMER)
{
TimerProc();//如果是定时器消息,调用timerproc

}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}

}

关于监视插入U盘的问题(1)

本文是参考 http://bbs.csdn.net/topics/390293365?page=1#post-393016986 完成的。

修改了其中的Demo,使得程序能够在 VS2008 Express 下编译成功。

修改后的代码如下,删除了读取识别出来盘符上指定文件的代码

//编程识别u盘的插入,通过处理WM_DEVICECHANGE的消息,来达到识别的效果
//vc下编译通过,能够识别
#include “stdafx.h”
#include
#include
#include
#include

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
TCHAR FirstDriveFromMask (ULONG unitmask);

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPreInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//windows窗口类的成员
TCHAR szClassName[] = TEXT(“MainWClass”);
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = ::LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor = ::LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szClassName;
wndclass.hIconSm = NULL;

::RegisterClassEx(&wndclass);

HWND hwnd = ::CreateWindowEx(
0,
szClassName,
TEXT(“mc”),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
UINT Cret = GetLastError();
//printf(“%d\n”,GetLastError());
if (hwnd == NULL)
{
::MessageBox(NULL,TEXT(“Create Window error!”),TEXT(“error”),MB_OK);
return -1;
}
::ShowWindow(hwnd,nCmdShow);
::UpdateWindow(hwnd);
MSG msg;
while (::GetMessage(&msg,NULL,0,0))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}

return msg.wParam;
}
//回调函数处理消息
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:/*初始化*/
MessageBox(hwnd, TEXT(“go!”), TEXT(“405 Studio”), MB_OK);
break;
case WM_DEVICECHANGE:
if(wParam == DBT_DEVICEARRIVAL) //设备激活
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
TCHAR szMsg[80],Disk;
Disk = FirstDriveFromMask(lpdbv ->dbcv_unitmask);
wsprintf (szMsg,TEXT(“Drive %c: Media has arrived./n”),Disk);
MessageBox (hwnd, szMsg, TEXT(“WM_DEVICECHANGE”), MB_OK);
//Sleep(1000);
::PostQuitMessage(0);

}
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
return 0;
}
//提取u盘的盘符
TCHAR FirstDriveFromMask (ULONG unitmask)
{
char i;
for (i = 0; i < 26; ++i) { if (unitmask & 0x1) break; unitmask = unitmask >> 1;
}
return (i + ‘A’);
}

首先运行本程序,然后插入U盘,可以看出识别出来插入U盘的盘符,另外弹出来的是Windows自动播放的对话框。运行结果如下:

wusb

源程序和编译后的EXE可以从下面下载

watchusb

在Console中使用Timer (1)

本文内容来自 http://biancheng.dnbcw.info/60/60901.html 是在一个控制台窗口中创建一个线程,然后在线程中处理Timer的消息

整理后的代码如下,VC2008 Express编译通过

#include “stdafx.h”
#include
#include
#include

int count =0;

VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)
{
count++;
printf(“WM_TIMER in work thread count=%d\n”,count);
}

DWORD CALLBACK Thread(PVOID pvoid)
{
MSG msg;

UINT timerid=SetTimer(NULL,111,3000,TimerProc);
BOOL bRet;

PeekMessage(&msg,NULL,WM_USER,WM_USER,PM_NOREMOVE);
while((bRet = GetMessage(&msg,NULL,0,0))!= 0)
{
if(bRet==-1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
KillTimer(NULL,timerid);
printf(“thread end here\n”);
return 0;
}

int main()
{
DWORD dwThreadId;
printf(“Use timer in workthread of console application\n”);
HANDLE hThread = CreateThread(NULL,0,Thread,0,0,&dwThreadId);
_getch();
return 0;
}

运行结果

contimer

下载 ConTimer

旧事重提:理解状态机

最近在看编译原理方面的东西,状态机是很重要的部分。之前遇到过一个问题,用这个问题让我深刻的理解了状态机。当年写了这篇文章,但是看起来这次更新忘记上传了。现在补上这篇文章。具体的问题是这样的:

“我不是计算机专业毕业的,也没有研究过离散数学(听说状态机属于离散数学)。前几天刚好有机会听别人讲这方面的问题,于是针对这个题目研究了一下。记录与此。
一.问题
请画出车库门控制器的 FSM(finite state machine)。车库门的功能如下:
1. 开启车库门
2. 关闭车库门
3. 车库门进行中(正在开启或关闭中)停止
4. 当车库门是处于第 3 点的状态时,往(停止前)反方向运行
PS:以上这些功能只须使用一个控制键”

解决方法和分析的过程在PDF 中能找到。

statem

一个备份 Outlook 的VBA

下面这段程序能够实现将你选中的邮件以MSG格式保存到一个目录中,并且生成一个包含邮件内容的TXT文件。举例来说:你在Outlook中选中200封邮件,然后指定 c:\m\ 作为存储目录。运行之后你就会发现,m目录中出现 0 1 2 3….这样的目录,其中是以MSG格式保存的每封邮件,并且每个目录下还有其中邮件TXT格式的内容。程序是以20MB为限,当一个目录中的邮件大于20MB时自动创建另外的目录。之所以选择20MB是因为通常的Mail系统所支持的附件的上限是这个数值。因此,你可以将生成的TXT作为正文,压缩之后的目录作为附件发送到你的邮件服务器上。比较推荐的是 QQ Mail,容量足够大,速度也不慢。当然如果你不放心,或者说觉得它的速度慢,还可以选择一个对你来说速度快的免费邮件服务器,然后在QQ Mail中设定自动收取这个邮箱的邮件。因为 QQ Mail的容量是一直增长的,所以不必担心邮箱爆掉。另外,QQ Mail提供了一个全文搜索的功能,使用户可以很方便的搜索到文件的内容—-这也是为什么要按照TXT保存邮件正文一次。

Function ReplaceCharsForFileName(sName As String) As String
sName = Replace(sName, “/”, ” “, 1, -1)
sName = Replace(sName, “\”, ” “, 1, -1)
sName = Replace(sName, “:”, ” “, 1, -1)
sName = Replace(sName, “?”, ” “, 1, -1)
sName = Replace(sName, “<“, ” “, 1, -1) sName = Replace(sName, “>”, ” “, 1, -1)
sName = Replace(sName, “|”, ” “, 1, -1)
sName = Replace(sName, “*”, ” “, 1, -1)
‘chr(34)='”‘
sName = Replace(sName, Chr(34), ” “, 1, -1)
ReplaceCharsForFileName = sName
End Function

Sub SaveChooseFile()

Dim objItem As Object
Dim strPath As String
Dim strFilePath As String
Dim iSize As Long
Dim iNum As Integer
Dim objFSO As Object

‘exit if you do not choose any files
If ActiveExplorer.Selection.Count = 0 Then Exit Sub

‘the path string should be ended by “\”
strPath = InputBox(“please enter a path :”, ” Enter path”)

Set fso = CreateObject(“Scripting.FileSystemObject”)
Set objFSO = CreateObject(“Scripting.FileSystemObject”)

‘File size counter
iSize = 0

‘Directory counter
iNum = 0

For Each objItem In ActiveExplorer.Selection

‘Create file and dir
If iSize = 0 Then
strFilePath = strPath & Str(iNum)
If Not (objFSO.FolderExists(strFilePath)) Then
objFSO.CreateFolder (strFilePath)
End If
Set ts = fso.CreateTextFile(strFilePath & “\” & Str(iNum) & “Body.txt”, ForAppending, True)
End If

iSize = iSize + objItem.Size

‘write all the chosen file to a file
ts.Write (“==============================================================” & vbCrLf)
ts.Write (objItem.ReceivedTime & vbCrLf)
ts.Write (objItem.SenderName & vbCrLf)
ts.Write (objItem.Subject & vbCrLf)

‘Meeting requirement doesn’t have ‘to’ field
If (objItem.Class <> olMeetingRequest) _
And (objItem.Class <> olMeetingCancellation) _
And (objItem.Class <> olMeetingResponseNegative) _
And (objItem.Class <> olMeetingResponsePositive) _
Then ts.Write (objItem.To & vbCrLf)
ts.Write (objItem.Body & vbCrLf)
ts.Write (“==============================================================”)

‘save the mail as MSG format
objItem.SaveAs strFilePath & “\” & ReplaceCharsForFileName(objItem.Subject) & “.MSG”, OlSaveAsType.olMSGUnicode

‘save the contents to another text if the size is larger than 20MB
If iSize >= 20971520 Then
iSize = 0
iNum = iNum + 1
ts.Close
End If

Next

MsgBox “Email text extraction completed!”, vbOKOnly + vbInformation, “DONE!”

ts.Close
Set objItem = Nothing

End Sub

额外的问题:有时候在保存邮件的过程中会出现如下错误:

当出现错误的时候,可以切换到Debug下面,通过查看邮件的标题得知出现问题的邮件是那封,删除这个邮件即可。这并不是上面程序本身导致的,对于导致问题的邮件,你使用Outlook 直接保存为MSG文件也会出现同样的问题,目前我只在一些会议邀请的邮件上遇到过这样的问题,如果你没有这类邮件应该不会遇到这个问题。

如何判断当前程序是直接运行还是从CMD窗口中运行(下)

继续前一篇提到的问题。仍然是根据天杀的建议更专业一点的方法是通过判断当前进程的父进程来得知是如何运行的。比如,双击运行父进程就是Explore.exe,命令行运行,父进程就是 cmd.exe 。

代码如下:

program Project2;

{$APPTYPE CONSOLE}

uses
SysUtils,
windows,
Tlhelp32,
PsApi;

//检查自己的进程的父进程
procedure CheckParentProc;
var //检查自己的进程的父进程
Pn: TProcesseNtry32;
sHandle:THandle;
H,hMod:Hwnd;
Found:Boolean;
Buffer:array[0..1023]of Char;
cbNeeded:DWORD;
begin
//得到所有进程的列表快照
sHandle:= CreateToolhelp32Snapshot(TH32CS_SNAPALL,0);
Found:= Process32First(sHandle,Pn);//查找进程
while Found do //遍历所有进程
begin
//比较枚举出来的ID是否是当前的ID
if Pn.th32ProcessID = GetCurrentProcessId then
begin
//父进程的句柄
H:= OpenProcess(PROCESS_ALL_ACCESS,True,Pn.th32ParentProcessID);

if EnumProcessModules( H,@hMod,sizeof(hMod),cbNeeded) then
begin
ZeroMemory(@Buffer,MAX_PATH+1);
GetModuleFileNameEx(H, hMod,Buffer,MAX_PATH+1); //枚举进程文件所在路径
//只是简单把它打印出来而已
writeln(strpas(Buffer));
end;
CloseHandle(H);
end;
Found:= Process32Next(sHandle,Pn);//查找下一个
end;
CloseHandle(sHandle);
end;

begin
CheckParentProc;
readln;
end.

下图是这个程序分别在cmd窗口,直接双击和Delphi IDE环境中的运行结果。

完整代码下载

cmd2

=============================================================

2022年01月24日补充 VC 下的实现方法

#include "stdafx.h"

#include <Windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <Psapi.h>

int getProcessName(DWORD pid, LPWSTR fname, DWORD sz)
{
	HANDLE h = NULL;
	int e = 0;
	h = OpenProcess
	(
		PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
		FALSE,
		pid
	);
	if (h)
	{
		if (GetModuleFileNameEx(h, NULL, fname, sz) == 0)
			e = GetLastError();
		CloseHandle(h);
	}
	else
	{
		e = GetLastError();
	}
	return (e);
}

DWORD getParentPID(DWORD pid)
{
	HANDLE h = NULL;
	PROCESSENTRY32 pe = { 0 };
	DWORD ppid = 0;
	pe.dwSize = sizeof(PROCESSENTRY32);
	h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (Process32First(h, &pe))
	{
		do
		{
			if (pe.th32ProcessID == pid)
			{
				ppid = pe.th32ParentProcessID;
				break;
			}
		} while (Process32Next(h, &pe));
	}
	CloseHandle(h);
	return (ppid);
}

boolean IsRuninCMD() {
	DWORD pid, ppid;
	int e;
	wchar_t fname[256];
	pid = GetCurrentProcessId();
	ppid = getParentPID(pid);
	e = getProcessName(ppid, fname, MAX_PATH);
	wprintf(L"PPID=%d\nErr=%d\nEXE={%s}\n", ppid, e, fname);
	if ((wcsstr(fname, L"cmd.exe") != NULL) ||
		(wcsstr(fname, L"powershell.exe") != NULL)) {
		return true;
	} else return false;
}
void main(int argc, char* argv[])
{
	if (IsRuninCMD()) {
		printf("in cmd\n");
	}
	else {
		printf("NOT in cmd\n");
		getchar();
	}
}