VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "cANI" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit '============================================================================================================= ' ' cANI Class Module ' ----------------- ' ' Created By : Kevin Wilson ' http://www.TheVBZone.com ( The VB Zone ) ' http://www.TheVBZone.net ( The VB Zone .net ) ' ' Last Update : November 30, 2000 ' ' VB Versions : 5.0 / 6.0 ' ' Requires : A Standard VB Timer Control ' ' Description : This class module was written to make displaying animations and animated cursors easy and ' efficient. This module gives you the ability to play, pause, and stop animations on any ' object that has a handle (hWnd) and a Device Context (DC)... like PictureBoxes, Forms, User ' Controls, Property Pages, etc. ' ' Example Use : ' ------------- ' ' Private WithEvents ANI As cANI ' ' Private Sub cmdPause_Click() ' ANI.ANI_Pause ' End Sub ' ' Private Sub cmdStart_Click() ' ANI.ANI_Start ' End Sub ' ' Private Sub cmdStop_Click() ' ANI.ANI_Stop ' End Sub ' ' Private Sub Form_Load() ' Set ANI = New cANI ' Set ANI.TimerToUse = Timer1 ' Set ANI.DisplayObject = Me ' ANI.BackColor = vbBlack ' ANI.DisplayHeight = 100 ' ANI.DisplayWidth = 100 ' ANI.DisplayTop = 0 ' ANI.DisplayLeft = 0 ' ANI.FilePath = "C:\TEST.ANI" ' ANI.Interval = 200 ' ANI.Stretch = True ' End Sub ' '============================================================================================================= ' ' LEGAL: ' ' You are free to use this code as long as you keep the above heading information intact and unchanged. Credit ' given where credit is due. Also, it is not required, but it would be appreciated if you would mention ' somewhere in your compiled program that that your program makes use of code written and distributed by ' Kevin Wilson (www.TheVBZone.com). Feel free to link to this code via your web site or articles. ' ' You may NOT take this code and pass it off as your own. You may NOT distribute this code on your own server ' or web site. You may NOT take code created by Kevin Wilson (www.TheVBZone.com) and use it to create products, ' utilities, or applications that directly compete with products, utilities, and applications created by Kevin ' Wilson, TheVBZone.com, or Wilson Media. You may NOT take this code and sell it for profit without first ' obtaining the written consent of the author Kevin Wilson. ' ' These conditions are subject to change at the discretion of the owner Kevin Wilson at any time without ' warning or notice. Copyright© by Kevin Wilson. All rights reserved. ' '============================================================================================================= ' Constants - General Private Const MAX_PATH = 260 ' Constants - FormatMessage.dwFlags Private Const FORMAT_MESSAGE_ALLOCATE_BUFFER = &H100 ' Specifies that the lpBuffer parameter is a pointer to a PVOID pointer, and that the nSize parameter specifies the minimum number of TCHARs to allocate for an output message buffer. The function allocates a buffer large enough to hold the formatted message, and places a pointer to the allocated buffer at the address specified by lpBuffer. The caller should use the LocalFree function to free the buffer when it is no longer needed. Private Const FORMAT_MESSAGE_IGNORE_INSERTS = &H200 ' Specifies that insert sequences in the message definition are to be ignored and passed through to the output buffer unchanged. This flag is useful for fetching a message for later formatting. If this flag is set, the Arguments parameter is ignored. Private Const FORMAT_MESSAGE_FROM_STRING = &H400 ' Specifies that lpSource is a pointer to a null-terminated message definition. The message definition may contain insert sequences, just as the message text in a message table resource may. Cannot be used with FORMAT_MESSAGE_FROM_HMODULE or FORMAT_MESSAGE_FROM_SYSTEM. Private Const FORMAT_MESSAGE_FROM_HMODULE = &H800 ' Specifies that lpSource is a module handle containing the message-table resource(s) to search. If this lpSource handle is NULL, the current process's application image file will be searched. Cannot be used with FORMAT_MESSAGE_FROM_STRING. Private Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000 ' Specifies that the function should search the system message-table resource(s) for the requested message. If this flag is specified with FORMAT_MESSAGE_FROM_HMODULE, the function searches the system message table if the message is not found in the module specified by lpSource. Cannot be used with FORMAT_MESSAGE_FROM_STRING. If this flag is specified, an application can pass the result of the GetLastError function to retrieve the message text for a system-defined error. Private Const FORMAT_MESSAGE_ARGUMENT_ARRAY = &H2000 ' Specifies that the Arguments parameter is not a va_list structure, but instead is just a pointer to an array of values that represent the arguments. ' Constants - DrawIconEx.diFlags Private Const DI_MASK = &H1 ' Draws the icon or cursor using the mask. Private Const DI_IMAGE = &H2 ' Draws the icon or cursor using the image. Private Const DI_NORMAL = &H3 ' Combination of DI_IMAGE and DI_MASK. Private Const DI_COMPAT = &H4 ' Draws the icon or cursor using the system default image rather than the user-specified image. Private Const DI_DEFAULTSIZE = &H8 ' Draws the icon or cursor using the width and height specified by the system metric values for cursors or icons, if the OutputWidth and OutputHeight parameters are set to zero. If this flag is not specified and OutputWidth and OutputHeight are set to zero, the function uses the actual resource size. ' Win32 API Declarations Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hDC As Long) As Long Private Declare Function DeleteDC Lib "gdi32" (ByVal hDC As Long) As Long Private Declare Function DestroyCursor Lib "USER32" (ByVal hCursor As Long) As Long Private Declare Function DrawIconEx Lib "USER32" (ByVal hDC As Long, ByVal xLeft As Long, ByVal yTop As Long, ByVal hIcon As Long, ByVal OutputWidth As Long, ByVal OutputHeight As Long, ByVal iStepIfAniCur As Long, ByVal hbrFlickerFreeDraw As Long, ByVal diFlags As Long) As Long Private Declare Function FormatMessage Lib "KERNEL32" Alias "FormatMessageA" (ByVal dwFlags As Long, lpSource As Any, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, ByVal lpBuffer As String, ByVal nSize As Long, Arguments As Long) As Long Private Declare Function LoadCursorFromFile Lib "USER32" Alias "LoadCursorFromFileA" (ByVal lpFileName As String) As Long Private Declare Function OleTranslateColor Lib "olepro32.dll" (ByVal OLE_COLOR As Long, ByVal hPalette As Long, lpColorRef As Long) As Long 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ' CLASS PROPERTY VARIABLES 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Private WithEvents a_Timer As Timer Attribute a_Timer.VB_VarHelpID = -1 Private a_BackColor As Long Private a_CurrentFrame As Long Private a_DisplayHeight As Long Private a_DisplayLeft As Long Private a_DisplayOBJ As Object Private a_DisplayTop As Long Private a_DisplayWidth As Long Private a_FilePath As String Private a_FrameCount As Long Private a_Interval As Long Private a_Paused As Boolean Private a_Playing As Boolean Private a_Stretch As Boolean 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ' CLASS EVENT DECLARATIONS 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Public Event DisplayFrame(ByVal FrameIndex As Long) 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ' CLASS EVENTS 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Private Sub Class_Initialize() ' Set default property values a_BackColor = -1 a_Interval = 100 End Sub Private Sub Class_Terminate() ' Clean up memory used Set a_Timer = Nothing Set a_DisplayOBJ = Nothing End Sub 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ' OTHER EVENTS 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ' This event is fired every time the specified timer control reports a "Timer" event Private Sub a_Timer_Timer() ' Check if the ANI is stopped or paused If a_Paused = True Then Exit Sub If a_Playing = False Then a_Timer.Enabled = False: Exit Sub ' Increment the current frame a_CurrentFrame = a_CurrentFrame + 1 If a_CurrentFrame > a_FrameCount Then a_CurrentFrame = 1 ' Draw the next frame to the specified DC DeleteDisplay If DrawFrame(a_DisplayOBJ.hDC, a_FilePath, a_CurrentFrame, a_DisplayLeft, a_DisplayTop) = False Then ANI_Stop ' Fire the event that reports the frame being drawn DoEvents RaiseEvent DisplayFrame(a_CurrentFrame) End Sub 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ' CLASS PROPERTIES 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ' OPTIONAL - If this property is specified, it is the color used as the background color ' of the animated cursur. If it is omited, the BackColor property of the ' specified object is used instead. Public Property Get BackColor() As Long BackColor = a_BackColor End Property Public Property Let BackColor(ByVal NewValue As Long) Dim TempColor As Long ' Make sure the color is in a valid Windows® color format (OLE colors don't always work) TempColor = TranslateColor(NewValue) If TempColor = -1 Then Err.Raise -1, App.EXEName & ".cANI.BackColor", "Specified color is invalid" Else a_BackColor = TempColor End If End Property ' OPTIONAL - This property specifies the maximum drawing area height of the animation. ' If this is not specified, the height of the display object is used instead. Public Property Get DisplayHeight() As Long DisplayHeight = a_DisplayHeight End Property Public Property Let DisplayHeight(ByVal NewValue As Long) a_DisplayHeight = NewValue End Property ' OPTIONAL - This property specifies the X position of the upper right corner ' of the animation to be displayed on the specified display object. 0 is the default. Public Property Get DisplayLeft() As Long DisplayLeft = a_DisplayLeft End Property Public Property Let DisplayLeft(ByVal NewValue As Long) a_DisplayLeft = NewValue End Property ' REQUIRED - This property specifies what the animated image should be displayed on. ' ----------------------------------------------------------------------------------- ' IMPORTANT : The object specified here must have the following properties / methods: ' hDC, hWnd, Picture, Image, Height, Width, AutoRedraw, CLS ' ----------------------------------------------------------------------------------- ' NOTE : Objects that could be used are PictureBox, Form, User Control, Property Page, etc. ' ----------------------------------------------------------------------------------- Public Property Get DisplayObject() As Object Set DisplayObject = a_DisplayOBJ End Property Public Property Set DisplayObject(ByVal NewValue As Object) Set a_DisplayOBJ = NewValue ' Set the needed properties for the property a_DisplayOBJ.AutoRedraw = True a_DisplayOBJ.ScaleMode = vbPixels End Property ' OPTIONAL - This property specifies the Y position of the upper right corner ' of the animation to be displayed on the specified display object. 0 is the default. Public Property Get DisplayTop() As Long DisplayTop = a_DisplayTop End Property Public Property Let DisplayTop(ByVal NewValue As Long) a_DisplayTop = NewValue End Property ' OPTIONAL - This property specifies the maximum drawing area width of the animation. ' If this is not specified, the width of the display object is used instead. Public Property Get DisplayWidth() As Long DisplayWidth = a_DisplayWidth End Property Public Property Let DisplayWidth(ByVal NewValue As Long) a_DisplayWidth = NewValue End Property ' REQUIRED - This property specifies what .ANI file should be displayed Public Property Get FilePath() As String FilePath = a_FilePath End Property Public Property Let FilePath(ByVal NewValue As String) ' Make sure the value is a valid file If UCase(Trim(Right(NewValue, 4))) <> ".ANI" Then Err.Raise -1, App.EXEName & ".cANI.FilePath", "Specified file not a valid ANI file" Exit Property ElseIf Dir(NewValue) = "" Then Err.Raise -1, App.EXEName & ".cANI.FilePath", "Specified file not found" Exit Property End If ' Set the new file path a_FilePath = NewValue ' Load the ANI file and get the frames information from it If CountFrames(a_FilePath) = False Then Err.Raise -1, App.EXEName & ".cANI.FilePath", "Failed to correctly load the ANI file" End If End Property ' READ ONLY - This property tells you how many frames are contained within the specified ' .ANI file. Public Property Get FrameCount() As Long FrameCount = a_FrameCount End Property ' OPTIONAL - This property specifies how fast the frames should be displayed in milliseconds. ' The default value is 100 milliseconds. Public Property Get Interval() As Long Interval = a_Interval End Property Public Property Let Interval(ByVal NewValue As Long) a_Interval = NewValue If Not a_Timer Is Nothing Then a_Timer.Interval = NewValue End If End Property ' OPTIONAL - If this property is specified, the animated images displayed will be ' stretched to the size of the a_DisplayHeight / a_DisplayWidth properties. If the ' a_DisplayHeight & a_DisplayWidth properties are not specified, the images are not ' stretched. Public Property Get Stretch() As Boolean Stretch = a_Stretch End Property Public Property Let Stretch(ByVal NewValue As Boolean) a_Stretch = NewValue End Property ' REQUIRED - This property specifies what standard VB timer control to use to tell ' when to display the frame images. Public Property Get TimerToUse() As Timer Set TimerToUse = a_Timer End Property Public Property Set TimerToUse(ByVal NewValue As Timer) Set a_Timer = NewValue a_Timer.Enabled = False a_Timer.Interval = a_Interval End Property 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ' CLASS METHODS 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ' Pauses a currently running animation Public Function ANI_Pause() As Boolean On Error Resume Next a_Paused = Not a_Paused ANI_Pause = True End Function ' Starts displaying the animated image on the specified display object Public Function ANI_Start() As Boolean ' Make sure needed parameters have been set If (a_DisplayOBJ Is Nothing) Or (a_Interval = 0) Or (a_FilePath = "") Or (a_FrameCount = 0) Then Err.Raise -1, App.EXEName & ".cANI.ANI_Start", "Parameter(s) missing or invalid" Exit Function ElseIf a_Timer Is Nothing Then Err.Raise -1, App.EXEName & ".cANI.ANI_Start", "Parameter(s) missing or invalid" Exit Function ElseIf Dir(a_FilePath) = "" Then Err.Raise -1, App.EXEName & ".cANI.ANI_Start", "Parameter(s) missing or invalid" Exit Function End If ' Set the ANI information a_CurrentFrame = 0 a_Paused = False a_Playing = True ' Set the timer to the specified parameters a_Timer.Interval = a_Interval a_Timer.Enabled = True ANI_Start = True End Function ' Stops animating the image on the display object Public Function ANI_Stop() As Boolean On Error Resume Next a_Timer.Enabled = False a_CurrentFrame = 0 a_Paused = False a_Playing = False ANI_Stop = True End Function ' Clears the display object of any images Public Function ANI_Clear() As Boolean ANI_Clear = DeleteDisplay End Function 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ' FUNCTIONS ONLY USED WITHIN THIS CLASS MODULE 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ' Loads the specified file Private Function DrawFrame(ByVal OutputDC As Long, ByVal FilePath As String, ByVal FrameNumber As Long, Optional ByVal X As Long, Optional ByVal Y As Long) As Boolean Dim lngError As Long Dim hCursor As Long Dim ReturnValue As Long ' Make sure that the parameters passed are valid If FilePath = "" Then Err.Raise -1, App.EXEName & ".cANI.DrawFrame", "No ANI file specified to load" Exit Function ElseIf Dir(FilePath) = "" Then Err.Raise -1, App.EXEName & ".cANI.DrawFrame", "Specified ANI file not found" Exit Function ElseIf OutputDC = 0 Then Err.Raise -1, App.EXEName & ".cANI.DrawFrame", "OutputDC parameter invalid" Exit Function ElseIf (FrameNumber < 1) Or (FrameNumber > 1024) Then Err.Raise -1, App.EXEName & ".cANI.DrawFrame", "FrameNumber parameter invalid" Exit Function ElseIf FrameNumber > a_FrameCount Then Err.Raise -1, App.EXEName & ".cANI.DrawFrame", "FrameNumber '" & CStr(FrameNumber) & "' does not exist" Exit Function End If ' Get a handle to the specified file hCursor = LoadCursorFromFile(FilePath) lngError = Err.LastDllError If lngError <> 0 Then Err.Raise lngError, App.EXEName & ".cANI.DrawFrame", GetErrorMsg(lngError) Exit Function ElseIf hCursor = 0 Then Err.Raise -1, App.EXEName & ".cANI.DrawFrame", "Failed to successfully load the specified file." Exit Function End If ' Draw the specified frame on the specified hDC If a_Stretch = True And a_DisplayHeight <> 0 And a_DisplayWidth <> 0 Then ReturnValue = DrawIconEx(OutputDC, X, Y, hCursor, a_DisplayWidth, a_DisplayHeight, FrameNumber - 1, 0, DI_NORMAL) Else ReturnValue = DrawIconEx(OutputDC, X, Y, hCursor, 0, 0, FrameNumber - 1, 0, DI_NORMAL) End If If ReturnValue <> 0 Then Set a_DisplayOBJ.Picture = a_DisplayOBJ.image DrawFrame = True Else Err.Raise lngError, App.EXEName & ".cANI.DrawFrame", GetErrorMsg(lngError) End If ' Clean up memory DestroyCursor hCursor End Function ' Loads the specified file Private Function CountFrames(ByVal FilePath As String) As Boolean Dim lngError As Long Dim hCursor As Long Dim MyCounter As Long Dim TempDC As Long ' Make sure that the parameters passed are valid If FilePath = "" Then Err.Raise -1, App.EXEName & ".cANI.DrawFrame", "No ANI file specified to load" Exit Function ElseIf Dir(FilePath) = "" Then Err.Raise -1, App.EXEName & ".cANI.DrawFrame", "Specified ANI file not found" Exit Function End If ' Get a handle to the specified file hCursor = LoadCursorFromFile(FilePath) lngError = Err.LastDllError If lngError <> 0 Then Err.Raise lngError, App.EXEName & ".cANI.CountFrames", GetErrorMsg(lngError) Exit Function ElseIf hCursor = 0 Then Err.Raise -1, App.EXEName & ".cANI.CountFrames", "Failed to successfully load the specified file." Exit Function End If ' Create a temporary DC to draw onto If a_DisplayOBJ Is Nothing Then TempDC = CreateCompatibleDC(0) Else TempDC = CreateCompatibleDC(a_DisplayOBJ.hDC) End If If TempDC = 0 Then Err.Raise -1, App.EXEName & ".cANI.CountFrames", "Failed to create required DC." Exit Function End If ' Go through the specified file and get all the frames a_FrameCount = 0 For MyCounter = 0 To 1024 If DrawIconEx(TempDC, 0, 0, hCursor, 0, 0, MyCounter, 0, DI_NORMAL) = 0 Then Exit For Else a_FrameCount = a_FrameCount + 1 End If Next ' Finished without errors, free memory DestroyCursor hCursor DeleteDC TempDC CountFrames = True End Function Private Function DeleteDisplay() As Boolean Dim TheColor As Long ' Get the background color to use If a_BackColor = -1 Then TheColor = a_DisplayOBJ.BackColor Else TheColor = a_BackColor End If ' Draw a square where the picture is being displayed the same color as the background ' so it effectively erases the last frame and readies the display for the next one If a_DisplayHeight = 0 Or a_DisplayWidth = 0 Then a_DisplayOBJ.Line (a_DisplayLeft, a_DisplayTop)-(a_DisplayLeft + a_DisplayOBJ.Width, a_DisplayTop + a_DisplayOBJ.Height), TheColor, BF Else a_DisplayOBJ.Line (a_DisplayLeft, a_DisplayTop)-(a_DisplayLeft + a_DisplayWidth, a_DisplayTop + a_DisplayHeight), TheColor, BF End If DeleteDisplay = True End Function ' Convert an OLE or a Windows color into a Windows color Private Function TranslateColor(ByVal oClr As OLE_COLOR, Optional ByVal hPal As Long) As Long If OleTranslateColor(oClr, hPal, TranslateColor) <> 0 Then TranslateColor = -1 End If End Function ' Gets the Windows error description based on the windows error number Private Function GetErrorMsg(ByVal ErrorNumber As Long) As String Dim strMessage As String Dim lngFlags As Long ' Set the proper flags lngFlags = FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS ' Return the error message associated with the specified error number strMessage = String(MAX_PATH, Chr(0)) FormatMessage lngFlags, 0, ErrorNumber, 0&, strMessage, MAX_PATH, ByVal 0 GetErrorMsg = Left(strMessage, InStr(strMessage, Chr(0)) - 1) If Right(GetErrorMsg, 2) = vbCrLf Then GetErrorMsg = Left(GetErrorMsg, Len(GetErrorMsg) - 2) End If End Function