Navigation Bar

02 September 2014

[ MFC ] - Get File Properties using CFileStatus and CFile::GetStatus

1. Introduction


We can get important file properties by making a call to the CFile’s static function GetFileStatus(). This function accepts CFileStatus object as a reference parameter and fills this object with the required information. In this article, I will show how do we use this object to retrieve some important file attributes.



2. About the example


The below screenshot shows the screen shot of example:




When the browse button is clicked, a file selection dialog is displayed. Once the file is picked from the file selection dialog, the file properties displayed in the dialog as described in the screen shot.



3. Creating the Application


Create a dialog based MFC application and name it as FileProp by accepting all the default settings in wizard pages. Once the application is created the class view looks like below:



The below-shown screen shot will help you in designing the application dialog. The dialog marked with number 1 is text boxes and all three check boxes marked as 3 are placed in the group box marked as number 2. Rename of the OK button as close and add a browse button marked as 4. That is all; the dialog is ready with the UI elements.





After the UI design, each control should be assigned with control variable and this association is shown in the below table:

UI Element
Control Variable Name
File Name Edit Box
m_ctrl_edit_filename
Created On Edit Box
m_ctrl_edit_filecreatedon
Modified On Edit Box
m_ctrl_edit_filemodifiedon
File Size Edit Box
m_ctrl_edit_filesize
Read Only Check Box
m_ctrl_chk_readonly
Hidden Check Box
m_ctrl_chk_hidden
System Check Box
m_ctrl_chk_system

To know how to add a control variable watch the video shown in the below link:


4.  Coding – Pick File


To select a file in the system, MFC’s common file dialog is used. An Instance of CfileDialog is created and then the dialog is displayed using the DoModal call. The displayed dialog is shown below:



The file name selected in this dialog is retrieved through CfileDialog’s member function GetPathName(). Even though the name states path name, we actually get filename with fully qualified path. The code for this is shown below:

//Sample 3.0: Get File Properties
void CFilePropDlg::OnBnClickedButtonBrowse()
{
                //Sample 3.1: Get Name of the File
                CFileDialog dlg ( TRUE, NULL, NULL, NULL, "All Files|*.*||" ) ;
                if ( dlg.DoModal( ) == IDOK )
                {
                                m_filename = dlg.GetPathName() ;       
                                m_ctrl_edit_filename.SetWindowText(m_filename);
                }



5. Coding – File Created and Modified time


We have the file name from the previous section of code. Now we will retrieve the properties of this selected file. As already specified in the introduction, CFileStatus object is passed to the GetStatus function of the CFile and this call fills the file properties in the CFileStatus structure. In the below code, m_ctime, m_mtime members are used to get the file created time as well as file modified time.

These members (m_ctime, m_mtime) are CTime instances and using the Format function of the CTime, the time (i.e.) created time and modified times are formatted in more readable format. Then this formatted time is displayed in the application example. Below is the code:

                //Sample 3.2: Get the File Status Object
                CFileStatus status;
                CFile::GetStatus(m_filename, status);

                //Sample 3.3: Get File Creation, Modification
                CString time_formatted;
                CTime time;
                time = status.m_ctime;
                time_formatted = time.Format("%d-%b-%Y   %H:%M:%S");
                m_ctrl_edit_filecreatedon.SetWindowText(time_formatted);
                time = status.m_mtime;
                time_formatted = time.Format("%d-%b-%Y   %H:%M:%S");
                m_ctrl_edit_filemodifiedon.SetWindowText( time_formatted);


6.  Coding – File Attributes and Size


The size of the file is retrieved through the member m_size. This member is formatted in a CString object then displayed on the dialog. Below is code for it:

//Sample 3.4: Get File Size
CString file_size;
file_size.Format("%ld", status.m_size );
m_ctrl_edit_filesize.SetWindowText(file_size);

The file attributes are collected by the Bitwise OR operation when we make a call to the GetStatus function. To make a check for the particular attributes we should do Bitwise AND operation. The hexadecimal decimal constant for each attributes are given below: 

enum Attribute {
   normal =    0x00,
   readOnly =  0x01,
   hidden =    0x02,
   system =    0x04,
   volume =    0x08,
   directory = 0x10,
   archive =   0x20
   };

OK. In the CFileStatus these attribute values are pushed into the member m_attribute and the constant values listed above are tested using the Bitwise AND operation. In the below code we do such a test and place/remove the check mark on the corresponding the checkboxes.

//Sample 3.5: Get the File Attributes
if ( (status.m_attribute & 0x01) == 0x01 )
                m_ctrl_chk_readonly.SetCheck(BST_CHECKED);
else
                m_ctrl_chk_readonly.SetCheck(BST_UNCHECKED);

if ( (status.m_attribute & 0x02) == 0x02)
                m_ctrl_chk_hidden.SetCheck(BST_CHECKED);
else
                m_ctrl_chk_hidden.SetCheck(BST_UNCHECKED);

if ( (status.m_attribute & 0x04) == 0x04)
                m_ctrl_chk_system.SetCheck(BST_CHECKED);
else

                m_ctrl_chk_system.SetCheck(BST_UNCHECKED);

The below video Explains and shows how the sample application works.

Watch How Sample works. Video

Source Code : Download

18 March 2014

[ MFC ] - Using SetWindowsHookEx & WH_KEYBOARD to Set Keyboard Hook Filter Function

1. Introduction


A Windows Hook intercepts specific type of windows hardware event before it reaches its destination say target window. The intercepted hardware events are passed to a function and that function can modify the event and even it can discard it. Such a function is called as Filter function. There are different types of windows hook available. Below is the most used one (Taken from MSDN):

WH_KEYBOARD:
Installs a hook procedure that monitors keystroke messages.

WH_MOUSE:
Installs a hook procedure that intercepts the mouse hardware events.

WH_GETMESSAGE:
Installs a hook procedure that monitors messages posted to a message queue.

A hook should be attached to a Filter Function. For Example, when we attach the WH_MOUSE hook to a filter function, all the mouse hardware messages first go to the filter function before it reaches the target window. In this article, we are going to use the Keyboard hook. Note that the filter function can also be called as hook procedure as it is attached to a hook.

When more than one filter function attached to a windows hook, then this forms a Hook Chain. For example, let us say you have two filter functions for a WH_KEYBOARD, then this forms a hook chain. When a keyboard event triggers, it reaches to all the filter functions in the keyboard hook chain. Have a look at the below picture:





Hope you gone through the above picture. Let us say user pressed a numeric key ‘7’ to display that in the notepad application. The keyboard hook monitors the hardware event of pressing the keyboard key 7. So before this hardware event reaches the notepad application, the keyboard hook (WH_KEYBOARD) will direct that to the attached filter functions. The filter function(s) after processing the message sends that to the actual target window or application.

From the above picture, you can see four filter functions attached to the keyboard hook. Let us say the filter function D is added last. Now, the lastly added filter function receives the tapped keyboard event first and after processing it sends that to Filter function ‘C’. This way the event reaches the notepad application at last once the filter function ‘A’ processed the message. Ok, let us go with creating the example application.



2. About the Example


The example is a single document interface application. Under the file menu, two menu items called “Hook CAPS lock”, “Unhook CAPS Lock” are added. When the “Hook Caps Lock” menu items are clicked, the Keyboard hook is attached to the application and hooks the keyboard event. The “UnHook CAPS Lock“ event removes installed hook from the application.

Under view menu, two menu items called First Dialog and Second Dialog are added. The menu items bring two dialogs with text boxes in it. When a hook is installed, you can type only the capital letters in the dialogs. After performing the UnHook operation, you will not have upper case restriction in the dialogs. Let us jump to the explanation-based walk-through.





3. Create the Example


1) Create a MFC Single document Interface Project called WinHooks. While creating the project remove the support for Document/View architecture and Unicode libraries. Accept the defaults for all other settings. This is shown in the below video:

Video 1:  Creating the Example


2) Once the project is created, add two dialog templates to it. In each dialog add some text boxes. For each dialog add the class using the class wizard that can be accessed through the right-click context menu of the dialog template. Adding the dialog through resource and attaching a class to it is shown in the below video:

Video 2: Adding Dialog Templates



3) After adding the dialog templates to the project through the dialog editor, we need to add two menu items and handler functions. From these handler functions, we will launch our dialogs. Below video shows adding two menu items:

Video 3: Adding Menu Items


Like how we added the menu items for launching the dialogs, the same way add two menu items under the file menu to engage and disengage the hook for tapping the keyboard events. The display names of the menu items are shown in the previous picture:

4. Launching the Dialogs

1) In the MainFrm.cpp file, provide the #include pre-processor directive to include the dialog classes. Below are the two statements:

//Sample 01: Header files to access the dialogs
#include "Dialog1.h"
#include "Dialog2.h"

2) Next, in the menu items handler (Created in video 3) add the code that brings the dialog in front of the user. Note that we are launching the dialogs as Modal dialogs. Below is the code that displays the dialogs:

void CMainFrame::OnViewFirstdlg()
{
            //Sample 02: Open the First Dialog
            CDialog1 dlg;
            dlg.DoModal();
}
void CMainFrame::OnViewSeconddlg()
{
            //Sample 03: Open Second Dialog
            CDialog2 dlg;
            dlg.DoModal();
}

5. Hook Filter Function

I hope by this time you are aware of what is filter function. In this function we will add a filter function and this filter function will be attached to the windows hook that we are going to create in the coming section.

1) Add the Win32 handle for the Windows Hook as a global variable. Note that we are going to add a global filter function sooner and for convenience we are adding the win32 handle at a global level. Below is the Declaration:

//Sample 04: Win32 Handle to Keyboard Hook
HHOOK hKBHook;

2) The signature of the keyboard hook procedure is given below:

//Sample 05: A Global Filter function that will be hooked to the
//                                  Key board event
LRESULT CALLBACK KeyboardProc ( int code, WPARAM w, LPARAM l )

In the keyboard hook procedure, the code indicates how should a procedure respond. That is when we receive the negative code; the keyboard procedure will call the next hook filter function, which is in the hook chain. In our example, we are going the test “WPARAM” to know what key is pressed. The “LAPARAM” is useful to know repeat count of a button; say, for Example, a keyboard button Up Arrow pressed down for some 4 or 5 seconds. 

3) As already told the first parameter received is tested to see whether the filter function can process the message or not. When the code is lesser than zero we should not do anything the make a call to the CallNextHookEx to call the next filter function in the hook chain. Below is code, which does that:

//Sample 5.1: As Per MSDN, Do not intercept the message when
//                                    code is negative
if ( code < 0 )
            return CallNextHookEx ( hKBHook, code, w, l ) ;

4) Next using the WPARAM w, we test to see the CAPS Lock key is pressed. Have a look at the below depiction:


Note that each slot in the above picture denotes the unsigned char and hence each slot consumes 8 bits. The constant VK_CAPITAL denotes 20 and using the GetKeyBoardState function call we get all the key status in an array of 256 unsigned char slot. At the char location 20, we get the CAPS lock key status. When lower order bit is 1 then the CAPS Lock status is ON. So our filter function gets the keyboard status and always keeps the lower order bit of VK_CAPITAL as one as shown above. The net effect is, when the message reaches the application message queue, the CAPS is always ON. Below is the code which is taken from the filter function:

//Samle 5.2: Always keep the CAPS lock state to 1.
unsigned char state [256] ;                                                         
if ( w == VK_CAPITAL )
{
            GetKeyboardState(state);
            state [VK_CAPITAL] = 1 ;
            SetKeyboardState ( state ) ;
}

5) Once we are done dealing with the CAPS LOCK Toggle state, make a call to the next filter function as shown below:

//Sample 5.3: Call the Next Hook in Chain
return CallNextHookEx ( hKBHook, code, w,1 ) ;

The entire keyboard hook procedure (or) filter function is listed below:

//Sample 05: A Global Filter function that will be hooked to the
//                                  Key board event
LRESULT CALLBACK KeyboardProc ( int code, WPARAM w, LPARAM l )
{
            //Sample 5.1: As Per MSDN, Do not intercept the message when
            //                                    code is negative
            if ( code < 0 )
                        return CallNextHookEx ( hKBHook, code, w, l ) ;
            //Samle 5.2: Always keep the CAPS lock state to 1.
            unsigned char state [256] ;                                                         
            if ( w == VK_CAPITAL )
            {
                        GetKeyboardState(state);
                        state [VK_CAPITAL] = 1 ;
                        SetKeyboardState ( state ) ;
            }
            //Sample 5.3: Call the Next Hook in Chain
            return CallNextHookEx ( hKBHook, code, w,1 ) ;
}

6. Install the Hook

1) Remember, the filter function is ready in the previous section and now it is time to set the hook specifying the filter function. Before that we will set the initial caps lock state as ON. Below is the code called from the application’s InitInstance that you are already familiar in the previous section:

//Sample 06: Set the Keyboard state
unsigned char state [256] ;                           
GetKeyboardState(state);
if (state[VK_CAPITAL] == 0 )
       state [VK_CAPITAL] = 1 ;
SetKeyboardState ( state ) ;

2) The SetWindowsHookEx Win32 function installs the windows hook and attaches that to the specific filter function routine. Have a look at the below picture:


The first parameter passed to this function specifying the hook category. In our example, we are going to hook the keyboard events and hence the first parameter passed to this function is WH_KEYBOARD. The second parameter passed to this function specifies the filter function, which will receive all the intercepted keyboard messages. The third parameter specifies the module handle. Say for example, if the hook filter function is in the DLL module, this parameter specifies that dll module name. In our case, this parameter is null as same exe module is implementing the filter function. The final parameter species the thread handle linked to the hook procedure.
Below is the message map handler for the menu items, which will hook and unhook keyboard messages:

void CMainFrame::OnFileSetcapslockhook()
{
       //Sample 07: Set the Key Board Hook
       hKBHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());
}
void CMainFrame::OnFileUnhookcapslock()
{
       //Sample 08: Unset the Key Board Hook
       UnhookWindowsHookEx(hKBHook);
}

7. Running the Application

After setting the hook, launch the dialog. Once the dialogs are launched, you can observe that the dialog accepts only the Capital letters. You can see this in the below video:
Video 4: Running the Application
Source Code: Download