How to Create and Use a Custom Cursor in Visual Basic; Win SDK

ID Number: Q76666

1.00

WINDOWS

Summary:

Using a graphics editor, the Microsoft Windows Software Development

Kit (SDK), and the Microsoft C compiler, you can create a dynamic-link

library (DLL) containing mouse cursors that can be used in a Visual

Basic application. By making calls to the Windows API functions

LoadLibrary, LoadCursor, SetClassWord, and GetFocus, you can display a

custom cursor from within a Visual Basic application. Below are the

steps necessary to a create a custom cursor and a Visual Basic

application to use the custom cursor.

This information applies to Microsoft Visual Basic programming system

version 1.0 for Windows.

More Information:

Setting a custom cursor in a Visual Basic application requires a call

to the Windows API function LoadLibrary to load the custom DLL

containing the cursor resource(s). A call to LoadCursor is then

required to load a single cursor contained in the DLL. The return

value of the LoadCursor function is a handle to the custom cursor.

This handle can be passed as an argument to the API function

SetClassWord with the constant GCW_HCURSOR. SetClassWord also requires

a window handle (hWnd) to the object (form or control) for which the

cursor is to be set. The hWnd of a form is available via the hWnd run-

time method. For example, the statement FWnd = Form1.hWnd will return

the hWnd of Form1 to the variable FWnd. The hWnd of a control can be

obtained by first using the SetFocus method on the control to give it

the input focus and then calling the API function GetFocus. GetFocus

returns the hWnd of the object with the current input focus.

A custom cursor always takes the place of the system cursor. The

MousePointer property of a form or control to receive the custom

cursor must be set to zero (system). Any other value for this property

will result in the selected cursor being displayed, not the custom

cursor.

Because the cursor is defined as part of a window class, any change to

the window class will be reflected across any control or form that

uses that class. For example, if the MousePointer property for two

command buttons is zero (system) and a custom cursor is set for one of

the command buttons, both of the command buttons will receive the

custom cursor. To guarantee a custom cursor for each control requires

that the cursor be set by calling SetClassWord in the MouseMove event

procedure of the control.

Some controls, such as command buttons, do not contain a MouseMove

event procedure. A custom mouse pointer for these types of controls

can be set by initiating a timer event. Within the timer event, calls

to the API functions GetCursorPos and WindowFromPoint can be made to

determine if the mouse is over the control or not. If the

WindowFromPoint API call returns the hWnd of the control, then the

mouse is over the control. A call to SetClassWord can then be made to

set the custom cursor for the control.

Below is an example of using the Windows SDK and C Compiler to create

a DLL containing cursor resources. Further below are the steps

necessary to create a Visual Basic program to use the cursor

resources.

If you do not have the Windows SDK but have a pre-compiled DLL

containing cursor resources, skip to the steps below outlining how to

create a Visual Basic application to use the custom cursor resources.

1. Using a graphics editor such as WinSDK Paint, create two cursor

images. Save the images separately as CURS1.CUR and CURS2.CUR,

respectively.

2. Using any text editor, create a C source file containing the

minimum amount of code for a Windows DLL. The source code must

contain the functions LibEntry and WEP (Windows exit procedure).

Below is an example of the C source file.

#include <windows.h>

int _far _pascal LibMain (HANDLE hInstance,

WORD wDataSeg,

WORD cbHeapSize,

LPSTR lpszCmdLine)

{

return(1);

}

int _far _pascal WEP (int nParameter)

{

return(1);

}

3. Save the file created in step 2 above as CURSORS.C.

4. Using any text editor, create a definition file (.DEF) for the

DLL. Enter the following as the body of the .DEF file.

LIBRARY CURSORS

DESCRIPTION 'DLL containing cursor resources'

EXETYPE WINDOWS

STUB 'WINSTUB.EXE'

CODE MOVEABLE DISCARDABLE

DATA MOVEABLE SINGLE

HEAPSIZE 0

EXPORTS

WEP @1 RESIDENTNAME

5. Save the file created in step 4 above as CURSORS.DEF.

6. Using a text editor, create a resource file for the cursors created

in step 1 above. Enter the following as the body of the .RC file.

Cursor1 CURSOR CURS1.CUR

Cursor2 CURSOR CURS2.CUR

7. Save the file created in step 6 above as CURSORS.RC.

8. Compile CURSORS.C from the DOS command line as follows:

CL /AMw /c /Gsw /Os /W2 CURSORS.C

9. Link the program from the DOS command line as follows (enter the

following two lines on a single line):

LINK /NOE /NOD cursors.obj +

LIBENTRY.OBJ,,,MDLLCEW.LIB+LIBW.LIB,CURSORS.DEF;

This will create the file CURSORS.EXE.

10. Add the cursor resources created in step 1 above to the .EXE file

created in step 9 above by invoking the Microsoft Resource

Compiler (RC.EXE) from the DOS command line as follows:

RC CURSORS.RC

11. Rename CURSORS.EXE to CURSORS.DLL from the DOS command line as

follows:

REN CURSORS.EXE CURSORS.DLL

Below are the steps necessary to create a Visual Basic application

that uses the cursor resources created in the steps above.

Important

---------

When running the Visual Basic program created by following the steps

below, it is important to terminate the application from the system

menu, NOT the Run End option from the file menu. When Run End is chosen

from the file menu, the unload event procedure is not executed.

Therefore, the system cursor is not restored and the custom cursor

will remain present at design time. Also, avoid terminating the

program from the Program Manager (PROGMAN.EXE) task list. The unload

event procedure is also not called when a program is terminated from

the task list.

1. Run Visual Basic from Windows or choose New Project (ALT+F, N)

from the File menu if Visual Basic is already running.

2. Put a picture control (Picture1) on Form1.

3. Put a command button (Command1) on Form1.

4. Put a timer control (Timer1) on Form1.

5. Enter the following code in the Global Module

Type PointType

x As Integer

y As Integer

End Type

6. Enter the following code in the General Declaration section of

Form1.

DefInt A-Z

Declare Function LoadLibrary Lib "kernel" (ByVal LibName$)

Declare Function LoadCursor Lib "user" (ByVal hInstance, ByVal

CursorName$)

Declare Function SetClassWord Lib "user" (ByVal hWnd, ByVal

nIndex, ByVal NewVal)

Declare Function DestroyCursor Lib "user" (ByVal Handle)

Declare Function GetFocus Lib "user" ()

Declare Function PutFocus Lib "user" Alias "SetFocus" (ByVal hWnd)

Declare Sub GetCursorPos Lib "user" (p As PointType)

Declare Function WindowFromPoint Lib "user" (ByVal y, ByVal x)

Const GCW_HCURSOR = (-12)

Dim SysCursHandle

Dim Curs1Handle

Dim Curs2Handle

Dim Pic1hWnd

Dim Command1hWnd

Dim p As PointType

7. Enter the following code in the Form_Load event procedure of

Form1.

Sub Form_Load ()

Form1.Show

DLLInstance = LoadLibrary("CURSORS.DLL")

Curs1Handle = LoadCursor(DLLInstance, "Cursor1")

Curs2Handle = LoadCursor(DLLInstance, "Cursor2")

SysCursHandle=SetClassWord(Form1.hWnd,GCW_HCURSOR,Curs2Handle)

'Get the current control with the input focus.

CurrHwnd = GetFocus()

'Get the Window handle of Picture1.

Picture1.SetFocus

Pic1hWnd = GetFocus()

'Get the Window handle of Command1.

Command1.SetFocus

Command1hWnd = GetFocus()

'Restore the focus to the control with the input focus

r = PutFocus(CurrHwnd)

timer1.interval = 1 'One millisecond

timer1.enabled = -1

End Sub

8. Enter the following code in the Form_Unload event procedure of

Form1.

Sub Form_Unload (Cancel As Integer)

'Restore the custom cursors to the system cursor:

LastCursor =SetClassWord(Form1.hWnd, GCW_HCURSOR, SysCursHandle)

LastCursor = SetClassWord(Pic1hWnd, GCW_HCURSOR, SysCursHandle)

LastCursor=SetClassWord(Command1hWnd, GCW_HCURSOR,SysCursHandle)

'Delete the cursor resources from memory:

Success = DestroyCursor(Curs1Handle)

Success = DestroyCursor(Curs2Handle)

End Sub

9. Enter the following code in the Timer1_Timer event procedure of

Timer1.

Sub Timer1_Timer ()

'Get the current (absolute) cursor position

Call GetCursorPos(p)

'Find out which control the midpoint of the cursor is over.

'The cursor is 32 x 32 pixels square. Change the class word

'of the control to the appropriate cursor.

Select Case WindowFromPoint(p.y + 16, p.x + 16)

Case Form1.hWnd

LastCursor = SetClassWord(Form1.hWnd, GCW_HCURSOR,

Curs2Handle)

LastCursor = SetClassWord(Command1hWnd, GCW_HCURSOR,

Curs2Handle)

LastCursor = SetClassWord(Pic1hWnd, GCW_HCURSOR,

Curs2Handle)

Case Command1hWnd

LastCursor = SetClassWord(Form1.hWnd, GCW_HCURSOR,

Curs1Handle)

LastCursor = SetClassWord(Command1hWnd, GCW_HCURSOR,

Curs1Handle)

Case Pic1hWnd

LastCursor = SetClassWord(Form1.hWnd, GCW_HCURSOR,

Curs1Handle)

LastCursor = SetClassWord(Pic1hWnd%, GCW_HCURSOR,

Curs1Handle)

End Select

End Sub

Run the program. The form should receive the "Cursor2" cursor and the

controls Command1 and Picture1 should receive the "Cursor1" cursor as

the mouse cursor is moved about the form.

References:

"Programming Windows: the Microsoft Guide to Writing Applications for

Windows 3," Charles Petzold. Microsoft Press, 1990.

"Microsoft Windows Software Development Kit: Reference Volume 1,"

version 3.0.

WINSDK.HLP file shipped with Microsoft Windows 3.0 SDK.

Additional reference words: 1.00