2012年8月15日 星期三

何謂callback function?


簡單的說,如果你使用了某個function,那麼你就是call了一個function。如果系統或是函式是要求你給一個function pointer,這個function pointer指到一個實際的函式(多半這個函式是你自己寫的)。然後它會在適當的時間呼叫此function,則此function就是所謂的 callback function。因為這個function是被callback了。
範例:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define DEFAULT_BLOCK_SIZE (4096)
 
// 定義callback function的prototype。
typedef void (* CALLBACK) (int);
 
// 定義了一個名為ShowPercentage的函式。這就是我們的callback函式。
// 他的prototype必須與前面的CALLBACK宣告一致。
void ShowPercentage(int percentage)
{
    fprintf(stderr, "%dn%nn", percentage);
}
 
// 定義了一個CopyFile的函式,這個函式會將參數source所指定檔案複製到
// target參數所指定的檔案去。而且每複製DEFAULT_BLOCK_SIZE數量的資料
// 就會呼叫一次callback參數所指到function一次。
void CopyFile(const char *source, const char *target, CALLBACK callback)
{
    char buf[DEFAULT_BLOCK_SIZE] ;
    struct stat fs ;
    int fdSrc, fdTrg ;
    int readBytes = 0, totalReadBytes = 0, percentage = 0;
    fdSrc = open(source, O_RDONLY);
    fstat(fdSrc, &fs);
    fdTrg = open(target,O_CREAT|O_TRUNC|O_RDWR);
    // 主要複製資料的迴圈
    while((readBytes=read(fdSrc, buf, DEFAULT_BLOCK_SIZE)) > 0)
{
        write(fdTrg, buf, readBytes);
        totalReadBytes += readBytes ;
        //複製資料後就呼叫callback函式去做顯示百分比的動作。
        callback( (totalReadBytes*100)/fs.st_size);
}
    close(fdTrg);
    close(fdSrc);
}
 
int main(void)
{
    // 這個範例中只是利用callback來顯示目前的進度。
    // 實際上我們可以利用callback來做更多的動作。
    CopyFile("A.TXT", "B.TXT", ShowPercentage);
    return 0 ;
}

2012年8月14日 星期二

GetLastError() 用法


char* lpMsgBuf;
FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER   |   FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        GetLastError(),
        MAKELANGID(LANG_NEUTRAL,   SUBLANG_DEFAULT),   //   Default   language
        (LPTSTR)   &lpMsgBuf,
        0,
        NULL
        );
MessageBox((LPCTSTR)lpMsgBuf,L"Error ",MB_OK|MB_ICONINFORMATION   );

2012年8月10日 星期五

.NET(C#):兩個進程防止被終止


我們知道,AppDomain.ProcessExit能監視當前進程的退出,而Process.Exited事件只能監視其他進程的退出。而且如果進程被強制結束AppDomain.ProcessExit不會發生的。綜上事實,我們可以用兩個進程互相監視另一個進程的Process.Exited事件來,然後如果一個進程被結束,另一個進程會重新打開這個程序。

比如示例中兩個程序,mgen_p1,和mgen_p2。一個程序結束後,另一個程序會馬上重新運行被結束的程序。當然如果兩個程序同時被結束,那麼他們會被徹底結束。

image
兩個程序的代碼是類似的,比如其中mgen_p1的代碼:
        static void Main(string[] args)
        {
            Console.WriteLine("mgen_p1正在運行");
            var pros = Process.GetProcessesByName("mgen_p2");
            Process mgen_p2;

            //判斷另一個程序是否在運行
            if (pros.Length == 0)
                run_mgen_p2();
            else
            {
                //設置進程的Exited退出事件
                mgen_p2 = pros[0];
                mgen_p2.EnableRaisingEvents = true;
                mgen_p2.Exited += new EventHandler(process_Exited);
            }


            System.Threading.Thread.Sleep(-1);
        }

        //重新啟動另一個程序
        static void run_mgen_p2()
        {
            var process = new Process();
            process.StartInfo.FileName = "mgen_p2";
            process.EnableRaisingEvents = true;
            process.Exited += new EventHandler(process_Exited);

            process.Start();
        }

        //進程退出事件
        static void process_Exited(object sender, EventArgs e)
        {
            run_mgen_p2();
        }
可以下載源代碼或者示例程序
(此為微軟SkyDrive存檔,請用瀏覽器直接下載,用某些下載工具可能無法下載)
環境:Visual C# 2010 Express

Creating Named Shared Memory


To share data, multiple processes can use memory-mapped files that the system paging file stores.

First Process

The first process creates the file mapping object by calling the CreateFileMapping function with INVALID_HANDLE_VALUE and a name for the object. By using the PAGE_READWRITE flag, the process has read/write permission to the memory through any file views that are created.
Then the process uses the file mapping object handle that CreateFileMapping returns in a call to MapViewOfFile to create a view of the file in the process address space. The MapViewOfFile function returns a pointer to the file view, pBuf. The process then uses the CopyMemoryfunction to write a string to the view that can be accessed by other processes.
Prefixing the file mapping object names with "Global\" allows processes to communicate with each other even if they are in different terminal server sessions. This requires that the first process must have the SeCreateGlobalPrivilege privilege.
When the process no longer needs access to the file mapping object, it should call the CloseHandle function. When all handles are closed, the system can free the section of the paging file that the object uses.
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>

#define BUF_SIZE 256
TCHAR szName[]=TEXT("Global\\MyFileMappingObject");
TCHAR szMsg[]=TEXT("Message from first process.");

int _tmain()
{
   HANDLE hMapFile;
   LPCTSTR pBuf;

   hMapFile = CreateFileMapping(
                 INVALID_HANDLE_VALUE,    // use paging file
                 NULL,                    // default security
                 PAGE_READWRITE,          // read/write access
                 0,                       // maximum object size (high-order DWORD)
                 BUF_SIZE,                // maximum object size (low-order DWORD)
                 szName);                 // name of mapping object

   if (hMapFile == NULL)
   {
      _tprintf(TEXT("Could not create file mapping object (%d).\n"),
             GetLastError());
      return 1;
   }
   pBuf = (LPTSTR) MapViewOfFile(hMapFile,   // handle to map object
                        FILE_MAP_ALL_ACCESS, // read/write permission
                        0,
                        0,
                        BUF_SIZE);

   if (pBuf == NULL)
   {
      _tprintf(TEXT("Could not map view of file (%d).\n"),
             GetLastError());

       CloseHandle(hMapFile);

      return 1;
   }


   CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
    _getch();

   UnmapViewOfFile(pBuf);

   CloseHandle(hMapFile);

   return 0;
}


Second Process

A second process can access the string written to the shared memory by the first process by calling the OpenFileMapping function specifying the same name for the mapping object as the first process. Then it can use the MapViewOfFile function to obtain a pointer to the file view, pBuf. The process can display this string as it would any other string. In this example, the message box displayed contains the message "Message from first process" that was written by the first process.
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#pragma comment(lib, "user32.lib")

#define BUF_SIZE 256
TCHAR szName[]=TEXT("Global\\MyFileMappingObject");

int _tmain()
{
   HANDLE hMapFile;
   LPCTSTR pBuf;

   hMapFile = OpenFileMapping(
                   FILE_MAP_ALL_ACCESS,   // read/write access
                   FALSE,                 // do not inherit the name
                   szName);               // name of mapping object

   if (hMapFile == NULL)
   {
      _tprintf(TEXT("Could not open file mapping object (%d).\n"),
             GetLastError());
      return 1;
   }

   pBuf = (LPTSTR) MapViewOfFile(hMapFile, // handle to map object
               FILE_MAP_ALL_ACCESS,  // read/write permission
               0,
               0,
               BUF_SIZE);

   if (pBuf == NULL)
   {
      _tprintf(TEXT("Could not map view of file (%d).\n"),
             GetLastError());

      CloseHandle(hMapFile);

      return 1;
   }

   MessageBox(NULL, pBuf, TEXT("Process2"), MB_OK);

   UnmapViewOfFile(pBuf);

   CloseHandle(hMapFile);

   return 0;
}



Using Shared Memory in a Dynamic-Link Library


The following example demonstrates how the DLL entry-point function can use a file-mapping object to set up memory that can be shared by processes that load the DLL. The shared DLL memory persists only as long as the DLL is loaded. Applications can use the SetSharedMem and GetSharedMem functions to access the shared memory.

DLL that Implements the Shared Memory

The example uses file mapping to map a block of named shared memory into the virtual address space of each process that loads the DLL. To do this, the entry-point function must:
  1. Call the CreateFileMapping function to get a handle to a file-mapping object. The first process that loads the DLL creates the file-mapping object. Subsequent processes open a handle to the existing object. For more information, see Creating a File-Mapping Object.
  2. Call the MapViewOfFile function to map a view into the virtual address space. This enables the process to access the shared memory. For more information, see Creating a File View.
Note that while you can specify default security attributes by passing in a NULL value for the lpAttributes parameter of CreateFileMapping, you may choose to use a SECURITY_ATTRIBUTES structure to provide additional security.
// The DLL code

#include <windows.h> 
#include <memory.h> 
 
#define SHMEMSIZE 4096 
 
static LPVOID lpvMem = NULL;      // pointer to shared memory
static HANDLE hMapObject = NULL;  // handle to file mapping

// The DLL entry-point function sets up shared memory using a 
// named file-mapping object. 
 
BOOL WINAPI DllMain(HINSTANCE hinstDLL,  // DLL module handle
    DWORD fdwReason,              // reason called 
    LPVOID lpvReserved)           // reserved 
{ 
    BOOL fInit, fIgnore; 
 
    switch (fdwReason) 
    { 
        // DLL load due to process initialization or LoadLibrary
 
          case DLL_PROCESS_ATTACH: 
 
            // Create a named file mapping object
 
            hMapObject = CreateFileMapping( 
                INVALID_HANDLE_VALUE,   // use paging file
                NULL,                   // default security attributes
                PAGE_READWRITE,         // read/write access
                0,                      // size: high 32-bits
                SHMEMSIZE,              // size: low 32-bits
                TEXT("dllmemfilemap")); // name of map object
            if (hMapObject == NULL) 
                return FALSE; 
 
            // The first process to attach initializes memory
 
            fInit = (GetLastError() != ERROR_ALREADY_EXISTS); 
 
            // Get a pointer to the file-mapped shared memory
 
            lpvMem = MapViewOfFile( 
                hMapObject,     // object to map view of
                FILE_MAP_WRITE, // read/write access
                0,              // high offset:  map from
                0,              // low offset:   beginning
                0);             // default: map entire file
            if (lpvMem == NULL) 
                return FALSE; 
 
            // Initialize memory if this is the first process
 
            if (fInit) 
                memset(lpvMem, '\0', SHMEMSIZE); 
 
            break; 
 
        // The attached process creates a new thread
 
        case DLL_THREAD_ATTACH: 
            break; 
 
        // The thread of the attached process terminates
 
        case DLL_THREAD_DETACH: 
            break; 
 
        // DLL unload due to process termination or FreeLibrary
 
        case DLL_PROCESS_DETACH: 
 
            // Unmap shared memory from the process's address space
 
            fIgnore = UnmapViewOfFile(lpvMem); 
 
            // Close the process's handle to the file-mapping object
 
            fIgnore = CloseHandle(hMapObject); 
 
            break; 
 
        default: 
          break; 
     } 
 
    return TRUE; 
    UNREFERENCED_PARAMETER(hinstDLL); 
    UNREFERENCED_PARAMETER(lpvReserved); 
} 

// The export mechanism used here is the __declspec(export)
// method supported by Microsoft Visual Studio, but any
// other export method supported by your development
// environment may be substituted.

#ifdef __cplusplus    // If used by C++ code, 
extern "C" {          // we need to export the C interface
#endif
 
// SetSharedMem sets the contents of the shared memory 
 
__declspec(dllexport) VOID __cdecl SetSharedMem(LPWSTR lpszBuf) 
{ 
    LPWSTR lpszTmp; 
    DWORD dwCount=1;
 
    // Get the address of the shared memory block
 
    lpszTmp = (LPWSTR) lpvMem; 
 
    // Copy the null-terminated string into shared memory
 
    while (*lpszBuf && dwCount<SHMEMSIZE) 
    {
        *lpszTmp++ = *lpszBuf++; 
        dwCount++;
    }
    *lpszTmp = '\0'; 
} 
 
// GetSharedMem gets the contents of the shared memory
 
__declspec(dllexport) VOID __cdecl GetSharedMem(LPWSTR lpszBuf, DWORD cchSize) 
{ 
    LPWSTR lpszTmp; 
 
    // Get the address of the shared memory block
 
    lpszTmp = (LPWSTR) lpvMem; 
 
    // Copy from shared memory into the caller's buffer
 
    while (*lpszTmp && --cchSize) 
        *lpszBuf++ = *lpszTmp++; 
    *lpszBuf = '\0'; 
}
#ifdef __cplusplus
}
#endif


Shared memory can be mapped to a different address in each process. For this reason, each process has its own instance of lpvMem, which is declared as a global variable so that it is available to all DLL functions. The example assumes that the DLL global data is not shared, so each process that loads the DLL has its own instance of lpvMem.
Note that the shared memory is released when the last handle to the file-mapping object is closed. To create persistent shared memory, you would need to ensure that some process always has an open handle to the file-mapping object.

Processes that Use the Shared Memory

The following processes use the shared memory provided by the DLL defined above. The first process calls SetSharedMem to write a string while the second process calls GetSharedMem to retrieve this string.
This process uses the SetSharedMem function implemented by the DLL to write the string "This is a test string" to the shared memory. It also starts a child process that will read the string from the shared memory.
// Parent process

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

extern "C" VOID __cdecl SetSharedMem(LPWSTR lpszBuf);

HANDLE CreateChildProcess(LPTSTR szCmdline) 
{ 
   PROCESS_INFORMATION piProcInfo; 
   STARTUPINFO siStartInfo;
   BOOL bFuncRetn = FALSE; 
 
// Set up members of the PROCESS_INFORMATION structure. 
 
   ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
 
// Set up members of the STARTUPINFO structure. 
 
   ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
   siStartInfo.cb = sizeof(STARTUPINFO); 
 
// Create the child process. 
    
   bFuncRetn = CreateProcess(NULL, 
      szCmdline,     // command line 
      NULL,          // process security attributes 
      NULL,          // primary thread security attributes 
      TRUE,          // handles are inherited 
      0,             // creation flags 
      NULL,          // use parent's environment 
      NULL,          // use parent's current directory 
      &siStartInfo,  // STARTUPINFO pointer 
      &piProcInfo);  // receives PROCESS_INFORMATION 
   
   if (bFuncRetn == 0) 
   {
      printf("CreateProcess failed (%)\n", GetLastError());
      return INVALID_HANDLE_VALUE;
   }
   else 
   {
      CloseHandle(piProcInfo.hThread);
      return piProcInfo.hProcess;
   }
}

int _tmain(int argc, TCHAR *argv[])
{
   HANDLE hProcess;

   if (argc == 1) 
   {
      printf("Please specify an input file");
      ExitProcess(0);
   }

   // Call the DLL function
   printf("\nProcess is writing to shared memory...\n\n");
   SetSharedMem(L"This is a test string");

   // Start the child process that will read the memory
   hProcess = CreateChildProcess(argv[1]);

   // Ensure this process is around until the child process terminates
   if (INVALID_HANDLE_VALUE != hProcess) 
   {
      WaitForSingleObject(hProcess, INFINITE);
      CloseHandle(hProcess);
   }
   return 0;
}



This process uses the GetSharedMem function implemented by the DLL to read a string from the shared memory. It is started by the parent process above.
// Child process

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

extern "C" VOID __cdecl GetSharedMem(LPWSTR lpszBuf, DWORD cchSize);

int _tmain( void )
{
    WCHAR cBuf[MAX_PATH];

    GetSharedMem(cBuf, MAX_PATH);
 
    printf("Child process read from shared memory: %S\n", cBuf);
    
    return 0;
}



2012年8月9日 星期四

C# 鍵盤掛鉤(keyboard hook)範例


這是一個以 C# 撰寫的 Windows Forms 範例程式,示範如何設置鍵盤掛鉤,以攔截特定的按鍵。
除了示範鍵盤掛鉤的設置與解除,同時也包含兩個取得鍵盤狀態的類別:KeyboardInfo 與 KeyStateInfo。這兩個類別取自文章Obtaining Key State info in .NET,它們等於是傳統 WinAPI 的 GetKeyState 函式的實作,但使用起來方便許多。我針對 ALT 鍵無法正確判斷的 bug 作了修正。以下是範例程式的完整原始碼:
    1 using System;
    2 using System.ComponentModel;
    3 using System.Windows.Forms;
    4 using System.Diagnostics;
    5 using System.Runtime.InteropServices;
    6
    7 namespace KeyboardHook
    8 {
    9     public partial class Form1 : Form
   10     {
   11         public Form1()
   12         {
   13             InitializeComponent();
   14         }
   15
   16         const int WH_KEYBOARD = 2;
   17
   18         public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
   19
   20         private static int m_HookHandle = 0;    // Hook handle
   21         private HookProc m_KbdHookProc;            // 鍵盤掛鉤函式指標
   22
   23         // 設置掛鉤.
   24         [DllImport("user32.dll", CharSet = CharSet.Auto,
   25         CallingConvention = CallingConvention.StdCall)]
   26         public static extern int SetWindowsHookEx(int idHook, HookProc lpfn,
   27         IntPtr hInstance, int threadId);
   28
   29         // 將之前設置的掛鉤移除。記得在應用程式結束前呼叫此函式.
   30         [DllImport("user32.dll", CharSet = CharSet.Auto,
   31         CallingConvention = CallingConvention.StdCall)]
   32         public static extern bool UnhookWindowsHookEx(int idHook);
   33
   34         // 呼叫下一個掛鉤處理常式(若不這麼做,會令其他掛鉤處理常式失效).
   35         [DllImport("user32.dll", CharSet = CharSet.Auto,
   36         CallingConvention = CallingConvention.StdCall)]
   37         public static extern int CallNextHookEx(int idHook, int nCode,
   38         IntPtr wParam, IntPtr lParam);
   39
   40         [DllImport("kernel32.dll")]
   41         static extern int GetCurrentThreadId();
   42
   43         private void button1_Click(object sender, EventArgs e)
   44         {
   45             if (m_HookHandle == 0)
   46             {
   47                 m_KbdHookProc = new HookProc(Form1.KeyboardHookProc);
   48
   49                 m_HookHandle = SetWindowsHookEx(WH_KEYBOARD, m_KbdHookProc, IntPtr.Zero, GetCurrentThreadId());
   50
   51                 if (m_HookHandle == 0)
   52                 {
   53                     MessageBox.Show("呼叫 SetWindowsHookEx 失敗!");
   54                     return;
   55                 }
   56                 button1.Text = "解除鍵盤掛鉤";
   57             }
   58             else
   59             {
   60                 bool ret = UnhookWindowsHookEx(m_HookHandle);
   61                 if (ret == false)
   62                 {
   63                     MessageBox.Show("呼叫 UnhookWindowsHookEx 失敗!");
   64                     return;
   65                 }
   66                 m_HookHandle = 0;
   67                 button1.Text = "設置鍵盤掛鉤";
   68             }
   69         }
   70
   71         public static int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
   72         {
   73             // 當按鍵按下及鬆開時都會觸發此函式,這裡只處理鍵盤按下的情形。
   74             bool isPressed = (lParam.ToInt32() & 0x80000000) == 0;  
   75
   76             if (nCode < 0 || !isPressed)
   77             {
   78                 return CallNextHookEx(m_HookHandle, nCode, wParam, lParam);
   79             }
   80
   81             // 取得欲攔截之按鍵狀態
   82             KeyStateInfo ctrlKey = KeyboardInfo.GetKeyState(Keys.ControlKey);
   83             KeyStateInfo altKey = KeyboardInfo.GetKeyState(Keys.Alt);
   84             KeyStateInfo shiftKey = KeyboardInfo.GetKeyState(Keys.ShiftKey);
   85             KeyStateInfo f8Key = KeyboardInfo.GetKeyState(Keys.F8);
   86
   87             if (ctrlKey.IsPressed)
   88             {
   89                 System.Diagnostics.Debug.WriteLine("Ctrl Pressed!");
   90             }
   91             if (altKey.IsPressed)
   92             {
   93                 System.Diagnostics.Debug.WriteLine("Alt Pressed!");
   94             }
   95             if (shiftKey.IsPressed)
   96             {
   97                 System.Diagnostics.Debug.WriteLine("Shift Pressed!");
   98             }
   99             if (f8Key.IsPressed)
  100             {
  101                 System.Diagnostics.Debug.WriteLine("F8 Pressed!");
  102             }
  103
  104             return CallNextHookEx(m_HookHandle, nCode, wParam, lParam);
  105         }
  106     }
  107
  108     public class KeyboardInfo
  109     {
  110         private KeyboardInfo() { }
  111
  112         [DllImport("user32")]
  113         private static extern short GetKeyState(int vKey);
  114
  115         public static KeyStateInfo GetKeyState(Keys key)
  116         {
  117             int vkey = (int)key;
  118
  119             if (key == Keys.Alt)
  120             {
  121                 vkey = 0x12;    // VK_ALT
  122             }
  123
  124             short keyState = GetKeyState(vkey);
  125             int low = Low(keyState);
  126             int high = High(keyState);
  127             bool toggled = (low == 1);
  128             bool pressed = (high == 1);
  129
  130             return new KeyStateInfo(key, pressed, toggled);
  131         }
  132
  133         private static int High(int keyState)
  134         {
  135             if (keyState > 0)
  136             {
  137                 return keyState >> 0x10;
  138             }
  139             else
  140             {
  141                 return (keyState >> 0x10) & 0x1;
  142             }
  143
  144         }
  145
  146         private static int Low(int keyState)
  147         {
  148             return keyState & 0xffff;
  149         }
  150     }
  151
  152
  153     public struct KeyStateInfo
  154     {
  155         Keys m_Key;
  156         bool m_IsPressed;
  157         bool m_IsToggled;
  158
  159         public KeyStateInfo(Keys key, bool ispressed, bool istoggled)
  160         {
  161             m_Key = key;
  162             m_IsPressed = ispressed;
  163             m_IsToggled = istoggled;
  164         }
  165
  166         public static KeyStateInfo Default
  167         {
  168             get
  169             {
  170                 return new KeyStateInfo(Keys.None, falsefalse);
  171             }
  172         }
  173
  174         public Keys Key
  175         {
  176             get { return m_Key; }
  177         }
  178
  179         public bool IsPressed
  180         {
  181             get { return m_IsPressed; }
  182         }
  183
  184         public bool IsToggled
  185         {
  186             get { return m_IsToggled; }
  187         }
  188     }
  189 }
NOTE:
  • 此範例的鍵盤掛鉤攔截四個按鍵:Ctrl、Alt、Shift、和 F8。執行時,可在 Visual Studio 的 Output 視窗觀察輸出的除錯訊息。
  • 此範例的鍵盤掛夠只有當此應用程式為作用中視窗時才有作用。
  • 在第 49 行呼叫 SetWindowHookEx 以設置鍵盤掛鉤時,最後一個傳入參數也可以用 AppDomain.GetCurrentThreadId(),可是此方法在 .NET 2.0 已標示為「已過時」(deprecated) ,且建議改用 Thread.ManagedThreadId 屬性。但問題是,ManagedThreadId 傳回的執行緒 ID 並不是底層的 Win32 執行緒 ID,在這裡並不適用。因此,為了取得正確的 win32 執行緒 ID,且避免 Visual Studio 編譯時發出警告,在此範例中是利用 P/Invoke 的方式直接呼叫 WinAPI GetCurrentThreadId 來取得執行緒 ID。
  • 在鍵盤掛鉤程序中(KeyboardHookProc),如果要"吃掉"攔到的按鍵,可直接傳回 1,且不要呼叫 CallNextHookEx
  • 執行此範例時,如果想要將鍵盤掛鉤的處理抽離出來,成為一個獨立的類別,可以參考這篇文章:在C#中使用鉤子

C# hook


View Code
 /*  
     lanuage c#
     date:2012/3/10
     builder:charlie
     mouse hook
 */
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Data;
 using System.Drawing;
 using System.Linq;
 using System.Text;
 using System.Windows.Forms;
 using System.Runtime.InteropServices;
 using System.Diagnostics;
 using System.Reflection;
 using System.Threading;

 namespace myhook
 {
     public partial class Form1 : Form
     {
         #region const

         private int mousehook = 0;
         private const int WH_LBUTTONDOWN = 0x201;
         private const int WM_RBUTTONDOWN = 0x204;
         private const int WH_MOUSE_LL = 14;
         private const int WM_MOUSEWHEEL = 0x020A;

         #endregion

         #region delegate

         /// <summary>
 /// Hookproc is a pointer to application-defined or library-defined callback function
 /// </summary>
 /// <param name="nCode"></param>
 /// if nCode is greater than zero, than hook procees.
 /// else it will pass a messge to the CallNextHookEx function, and return the value it returns.
 /// <param name="wParam"></param>
 /// specifies whether the message was sent by the current thread.
 /// <param name="lParam"></param>
 /// Pointer to a CWPSTRUCT structure that contains details about the message
 /// <returns></returns>
         public delegate int HookProc(int nCode, int wParam, IntPtr lParam);
         private HookProc MouseHookProcedure;

         #endregion

         #region api
         //find window
         [DllImport("user32.dll", EntryPoint = "FindWindow")]
         public static extern int FindWindow(
             string lpClassName,
             string lpWindowName
         );
         //get window
         [DllImport("user32.dll", EntryPoint = "GetWindowRect")]
         public static extern int GetWindowRect(
             int hwnd,
             ref Rectangle lpRect
         );
         //install hook
         [DllImport("user32.dll")]
         public static extern int SetWindowsHookEx(
             int idHook,
             HookProc lpfn,
             IntPtr hInstance,
             int threadId
         );
         //uninstall hook
         [DllImport("user32.dll", EntryPoint = "UnhookWindowsHookEx")]
         public static extern bool UnhookWindowsHookEx(
             int hHook
         );
         //next hook
         [DllImport("user32.dll")]
         public static extern int CallNextHookEx(
             int idHook,
             int nCode,
             int wParam,
             IntPtr lParam
         );
         //hook id
         [DllImport("kernel32.dll")]
         public static extern int GetCurrentThreadId();
         //handle
         [DllImport("kernel32.dll")]
         public static extern IntPtr GetModuleHandle(string name);
         //mouse structure
         [StructLayout(LayoutKind.Sequential)]
         public struct MOUSEHOOKSTRUCT
         {
             public Point pt;
             public int hwnd;
             public int wHitTestCode;
             public int dwExtraInfo;
         }
         #endregion

         #region events

         public Form1()
         {
           
             InitializeComponent();
         }

         private void StartmouseHook()
         {
             mousehook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure, GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);
         }
         private void StopmouseHook()
         {
             bool stop = true;
             stop = UnhookWindowsHookEx(mousehook);
         }
         private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
         {
             if (nCode >= 0)
             {
                 MOUSEHOOKSTRUCT mouse = (MOUSEHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MOUSEHOOKSTRUCT));
                 label1.Text = "x=" + mouse.pt.X+""+"y="+mouse.pt.Y;
                 if (wParam == WM_RBUTTONDOWN || wParam == WH_MOUSE_LL || wParam == WH_LBUTTONDOWN)
                 {
                     return 1;
                 }
             }
             return CallNextHookEx(mousehook, nCode, wParam, lParam);
         }
     
         private void Form1_Load(object sender, EventArgs e)
         {
             MouseHookProcedure = new HookProc(MouseHookProc);
             this.StartmouseHook();
         }

         private void button_start_Click(object sender, EventArgs e)
         {
             if (textbox_1.Text == "robinho04")
             {
                 this.StopmouseHook();
                 this.Close();
             }
         }

         private void Form1_FormClosing(object sender, FormClosingEventArgs e)
         {
         }
       
         #endregion
     }
 }