elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: ¿Eres nuevo? ¿Tienes dudas acerca del funcionamiento de la comunidad? Lee las Reglas Generales


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación General
| | |-+  .NET (C#, VB.NET, ASP) (Moderador: kub0x)
| | | |-+  Timer en vez de Sleep.
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: Timer en vez de Sleep.  (Leído 3,645 veces)
Meta


Desconectado Desconectado

Mensajes: 3.501



Ver Perfil WWW
Timer en vez de Sleep.
« en: 9 Febrero 2019, 11:10 am »

Hola:

Quiero hacer un parpadeo de un texto en modo consola. Por ahora solo me sale con Sleep, pero no me gusta este. Aún así dejo un ejemplo de lo que quiero pero está hecho en Sleep.



Código
  1. using System;
  2. using System.Threading; // No olvidar.
  3.  
  4. namespace Parpadeo_texto_consola_01_cs
  5. {
  6.    class Program
  7.    {
  8.        static void Main(string[] args)
  9.        {
  10.            // Título de la ventana.
  11.            Console.Title = "Blink";
  12.  
  13.            // Tamaño ventana consola.
  14.            // X anchura.
  15.            Console.WindowWidth = 16;
  16.  
  17.            // Y altura.
  18.            Console.WindowHeight = 2;
  19.  
  20.            // Oculto el cursor.
  21.            Console.CursorVisible = false;
  22.  
  23.            // Como estamos en un ejemplo, da igual en este caso
  24.            // poner un bucle infinito. Quiero que se muestre el
  25.            // resultado.
  26.            while (true)
  27.            {
  28.                // Posición de la pantalla.
  29.                Console.SetCursorPosition(0, 0);
  30.  
  31.                // Mostrar texto en pantalla.
  32.                Console.Write("Hola mundo");
  33.  
  34.                // Retardo de 0.5 segundos. 1000 ml (mili segundos)
  35.                // es igual a 1 segundo.
  36.                Thread.Sleep(500);
  37.  
  38.                // Posición de la pantalla.
  39.                Console.SetCursorPosition(0, 0);
  40.  
  41.                // Mostrar espaciones en blanco para borrar texto anterior.
  42.                Console.Write("          ");
  43.  
  44.                // Retardo 0.3 seg.
  45.                Thread.Sleep(300);
  46.            }
  47.        }
  48.    }
  49. }

Quiero hacer lo mismo, pero con el Timer en modo consola, lo que me cuesta hacerlo. Ya qu el timer pude interrumpir el programa cuando está dentro del temporizador pero con el Sleep, hasta que no acabe el tiempo, el programa se queda como en esclavo, sobre todo en tiempos muy largos.

¿Hay alguna forma de hacerlo en modo consola?

Si se puede hacer el parpadeo en una función y lo llamo cuando quiera, mejor que mejor.

Saludos.


En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.878



Ver Perfil
Re: Timer en vez de Sleep.
« Respuesta #1 en: 9 Febrero 2019, 16:02 pm »

Si se puede hacer el parpadeo en una función y lo llamo cuando quiera, mejor que mejor.

Mucho pedir en tus posts, pero poco agradecer cuando te ofrecen ayuda...

Lo que pides se puede hacer, pero puede resultarte algo complejo por la necesidad de usar funciones nativas de Windows para obtener la información de los caracteres del búfer de salida de la consola, incluyendo el color de texto; pues si vamos a hacer esto, hagámoslo de la forma más sofisticada posible cubriendo detalles como la preservación del color de texto, ¿no?.
Si prefieres algo más básico (léase: imperfecto) usando un timer y conociendo con antelación el texto exacto y la posición de este, también se puede hacer, tienes muchos ejemplos en Internet sobre como utilizar un timer en una aplicación de consola... y ya sabes asignar la posición del cursor y escribir texto en el búfer, por lo que sinceramente no veo necesario más explicaciones, busca y encontrarás.

En fin, esto de aquí abajo lo hice hace un rato, se trata de una función de uso genérico o reutilizable, es decir, se puede llamar cuando se desee y para parpadear cualquier rango de "celdas" en una fila específica...



En esa imagen se puede apreciar dos parpadeos activos distintos, el primero en la posición 0,0 con un intervalo de 500 ms, y el segundo en la posición 0,1 con un intervalo de 100 ms. Ambos parpadeos se detienen tras 5 segundos haciendo uso de un objeto de tipo CancellationTokenSource que es devuelto al llamar a esta función. Por cierto, y como también se puede apreciar, los colores actuales se preservan al parpadear.

Todo lo necesario lo tienes explicado aquí:

...tan solo es necesario realizar algunas modificaciones a ese código para adaptarlo a la función reutilizable que pides y así conseguir hacer lo mismo que te he mostrado en ese gif. Pero como ya dije, siempre puedes optar por hacer algo más básico y sencillo con el uso de un timer. Es cosa tuya.

Un saludo.


« Última modificación: 9 Febrero 2019, 16:04 pm por Eleкtro (aliviado) » En línea



Meta


Desconectado Desconectado

Mensajes: 3.501



Ver Perfil WWW
Re: Timer en vez de Sleep.
« Respuesta #2 en: 9 Febrero 2019, 17:12 pm »

Muchas gracias mi muy distinguido amigo. ;)

Código
  1. using System;
  2.  
  3. namespace Parpadeo_texto_consola_01_cs
  4. {
  5.    class Program
  6.    {
  7.        static void Main(string[] args)
  8.        {
  9.            // Título de la ventana.
  10.            Console.Title = "Blink";
  11.  
  12.            // Tamaño ventana consola.
  13.            // X anchura.
  14.            Console.WindowWidth = 16;
  15.  
  16.            // Y altura.
  17.            Console.WindowHeight = 2;
  18.  
  19.            // Oculto el cursor.
  20.            Console.CursorVisible = false;
  21.  
  22.            // Como estamos en un ejemplo, da igual en este caso
  23.            // poner un bucle infinito. Quiero que se muestre el
  24.            // resultado.
  25.  
  26.            int t = 0;
  27.            System.Timers.Timer blink = new System.Timers.Timer(100);
  28.            blink.Enabled = true;
  29.            bool mostrar = true;
  30.            blink.Elapsed += (a, b) =>
  31.            {
  32.                switch (t++)
  33.                {
  34.                    case 0:
  35.                        mostrar = true;
  36.                        break;
  37.                    case 5:
  38.                        mostrar = false;
  39.                        break;
  40.                    case 6:
  41.                        t = -1;
  42.                        break;
  43.                }
  44.  
  45.                // Posición de la pantalla.
  46.                Console.SetCursorPosition(0, 0);
  47.  
  48.                // Mostrar texto en pantalla.
  49.                Console.Write(mostrar ? "Hola mundo" : "          ");
  50.            };
  51.            Console.ReadKey();
  52.        }
  53.    }
  54. }
  55.  

No me gusta el código de arriba, mejor el que dices. Sigo con ello y me alegro el ejemplo del gif que pusiste. ;)
En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.878



Ver Perfil
Re: Timer en vez de Sleep.
« Respuesta #3 en: 20 Febrero 2019, 00:08 am »

No me gusta el código de arriba, mejor el que dices.

Iba a tirar el código pero antes de tirarlo lo comparto aquí. El código está sin optimizar lo suficiente, incompleto (solo faltaría por implementar la preservación del color de fondo, el cual se especifica en los flags de estructura CHAR_INFO) y sin documentar...

Por cierto, lo hice en VB.NET, así que necesitarás usar cualquier conversor de código a C#.

Código
  1. Friend NotInheritable Class NativeMethods
  2.  
  3.    <StructLayout(LayoutKind.Sequential)>
  4.    Friend Structure CHAR_INFO
  5.        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)>
  6.        Public charData() As Byte
  7.        Public attributes As Short
  8.    End Structure
  9.  
  10.    <StructLayout(LayoutKind.Sequential)>
  11.    Friend Structure COORD
  12.        Public X As Short
  13.        Public Y As Short
  14.    End Structure
  15.  
  16.    <StructLayout(LayoutKind.Sequential)>
  17.    Friend Structure SMALL_RECT
  18.        Public Left As Short
  19.        Public Top As Short
  20.        Public Right As Short
  21.        Public Bottom As Short
  22.    End Structure
  23.  
  24.    <StructLayout(LayoutKind.Sequential)>
  25.    Friend Structure CONSOLE_SCREEN_BUFFER_INFO
  26.        Public dwSize As COORD
  27.        Public dwCursorPosition As COORD
  28.        Public wAttributes As Short
  29.        Public srWindow As SMALL_RECT
  30.        Public dwMaximumWindowSize As COORD
  31.    End Structure
  32.  
  33.    <DllImport("kernel32.dll", SetLastError:=True)>
  34.    Friend Shared Function ReadConsoleOutput(ByVal consoleOutput As IntPtr,
  35.                                             ByVal buffer As IntPtr,
  36.                                             ByVal bufferSize As COORD,
  37.                                             ByVal bufferCoord As COORD,
  38.                                             ByRef refReadRegion As SMALL_RECT) As Boolean
  39.    End Function
  40.  
  41.    <DllImport("kernel32.dll", SetLastError:=True)>
  42.    Friend Shared Function GetStdHandle(ByVal stdHandle As Integer) As IntPtr
  43.    End Function
  44.  
  45. End Class

Código
  1. Public Shared Function ConsoleBlink(ByVal position As Point, ByVal length As Integer, ByVal interval As TimeSpan) As CancellationTokenSource
  2.  
  3.    Dim cts As New CancellationTokenSource()
  4.  
  5.    Const STD_OUTPUT_HANDLE As Integer = -11
  6.  
  7.    Dim t As New Task(
  8.        Sub()
  9.  
  10.            Dim x As Short = CShort(position.X)
  11.            Dim y As Short = CShort(position.Y)
  12.            Dim width As Short = CShort(length)
  13.            Dim height As Short = 1S
  14.            Dim buffer As IntPtr = Marshal.AllocHGlobal(width * height * Marshal.SizeOf(GetType(CHAR_INFO)))
  15.  
  16.            Try
  17.                Dim bufferCoord As New COORD()
  18.                Dim bufferSize As New COORD With {
  19.                    .X = width,
  20.                    .Y = height
  21.                }
  22.  
  23.                Dim rc As New SMALL_RECT With {
  24.                    .Left = x,
  25.                    .Top = y,
  26.                    .Right = (x + width - 1S),
  27.                    .Bottom = (y + height - 1S)
  28.                }
  29.  
  30.                Dim stdOutHandle As IntPtr = NativeMethods.GetStdHandle(STD_OUTPUT_HANDLE)
  31.                If Not NativeMethods.ReadConsoleOutput(stdOutHandle, buffer, bufferSize, bufferCoord, rc) Then
  32.                    ' Not enough storage is available to process this command' may be raised for buffer size > 64K (see ReadConsoleOutput doc.)
  33.                    Throw New Win32Exception(Marshal.GetLastWin32Error())
  34.                End If
  35.  
  36.                Dim charInfoList As New List(Of CHAR_INFO)
  37.                Dim ptr As IntPtr = buffer
  38.                For h As Integer = 0 To (height - 1)
  39.                    For w As Integer = 0 To (width - 1)
  40.                        Dim ci As CHAR_INFO = DirectCast(Marshal.PtrToStructure(ptr, GetType(CHAR_INFO)), CHAR_INFO)
  41.                        charInfoList.Add(ci)
  42.                        ptr += Marshal.SizeOf(GetType(CHAR_INFO))
  43.                    Next w
  44.                Next h
  45.  
  46.                Do Until cts.Token.IsCancellationRequested
  47.                    Dim oldCursorVisible As Boolean = Console.CursorVisible
  48.                    Dim oldPos As New Point(Console.CursorLeft, Console.CursorTop)
  49.                    Dim oldBackColor As ConsoleColor = Console.BackgroundColor
  50.                    Dim oldForeColor As ConsoleColor = Console.ForegroundColor
  51.  
  52.                    Console.CursorVisible = False
  53.                    Console.SetCursorPosition(position.X, position.Y)
  54.                    Console.Write(New String(" "c, length))
  55.                    Console.CursorVisible = oldCursorVisible
  56.                    Console.SetCursorPosition(oldPos.X, oldPos.Y)
  57.                    Thread.Sleep(interval)
  58.                    Console.CursorVisible = False
  59.  
  60.                    For i As Integer = 0 To (charInfoList.Count - 1)
  61.                        Dim ci As CHAR_INFO = charInfoList(i)
  62.                        Dim chars As Char() = Console.OutputEncoding.GetChars(ci.charData)
  63.                        Dim color As ConsoleColor = CType(ci.attributes, ConsoleColor)
  64.  
  65.                        Console.SetCursorPosition(i, position.Y)
  66.                        Console.ForegroundColor = color
  67.                        Console.Write(chars)
  68.                    Next
  69.  
  70.                    Console.SetCursorPosition(oldPos.X, oldPos.Y)
  71.                    Console.CursorVisible = oldCursorVisible
  72.                    Console.ForegroundColor = oldForeColor
  73.                    Thread.Sleep(interval)
  74.                Loop
  75.  
  76.            Finally
  77.                Marshal.FreeHGlobal(buffer)
  78.  
  79.            End Try
  80.  
  81.        End Sub, cts.Token)
  82.  
  83.    t.Start()
  84.    Return cts
  85.  
  86. End Function

Modo de empleo:
Código
  1. Public Module Module1
  2.  
  3.    Public Sub Main()
  4.  
  5.        Dim tt As New Timers.Timer
  6.  
  7.        Console.WindowWidth = 20
  8.        Console.WindowHeight = 5
  9.        Console.CursorVisible = False
  10.        Console.ReadKey()
  11.  
  12.        Dim blinkStr1 As String = "Test"
  13.        Dim blinkPos1 As New Point(0, 0)
  14.        Dim blinkLen1 As Integer = blinkStr1.Length
  15.        Dim blinkTime1 As TimeSpan = TimeSpan.FromMilliseconds(500)
  16.        Dim blinkCt1 As CancellationTokenSource
  17.  
  18.        Dim blinkStr2 As String = "Test"
  19.        Dim blinkPos2 As New Point(0, 1)
  20.        Dim blinkLen2 As Integer = blinkStr2.Length
  21.        Dim blinkTime2 As TimeSpan = TimeSpan.FromMilliseconds(100)
  22.        Dim blinkCt2 As CancellationTokenSource
  23.  
  24.        ' Manually write the blink string at the first console row.
  25.        Console.SetCursorPosition(blinkPos1.X, blinkPos1.Y)
  26.        Console.ForegroundColor = ConsoleColor.Yellow
  27.        Console.Write(blinkStr1(0))
  28.        Console.ForegroundColor = ConsoleColor.Cyan
  29.        Console.Write(blinkStr1(1))
  30.        Console.ForegroundColor = ConsoleColor.Green
  31.        Console.Write(blinkStr1(2))
  32.        Console.ForegroundColor = ConsoleColor.Red
  33.        Console.Write(blinkStr1(3))
  34.        Console.ForegroundColor = ConsoleColor.White
  35.  
  36.        ' Manually write the blink string at the second console row.
  37.        Console.SetCursorPosition(blinkPos2.X, blinkPos2.Y)
  38.        Console.ForegroundColor = ConsoleColor.Green
  39.        Console.Write(blinkStr2)
  40.  
  41.        ' Start blinking the text and return the task cancellation token sources.
  42.        blinkCt1 = ConsoleBlink(blinkPos1, blinkLen1, blinkTime1)
  43.        blinkCt2 = ConsoleBlink(blinkPos2, blinkLen2, blinkTime2)
  44.  
  45.        ' Write another string.
  46.        Console.ForegroundColor = ConsoleColor.White
  47.        Console.SetCursorPosition(0, 2)
  48.        Console.Write("Hello World!")
  49.  
  50.        ' Wait 5 seconds and cancel text blinking.
  51.        Thread.Sleep(TimeSpan.FromSeconds(5))
  52.        Dim t As New Task(
  53.            Sub()
  54.                blinkCt1.Cancel()
  55.                blinkCt2.Cancel()
  56.            End Sub)
  57.        t.Start()
  58.  
  59.        Console.Read()
  60.  
  61.    End Sub
  62.  
  63. End Module
« Última modificación: 20 Febrero 2019, 00:18 am por Eleкtro (aliviado) » En línea



Meta


Desconectado Desconectado

Mensajes: 3.501



Ver Perfil WWW
Re: Timer en vez de Sleep.
« Respuesta #4 en: 20 Febrero 2019, 00:15 am »

Muchas gracias.
En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.878



Ver Perfil
Re: Timer en vez de Sleep.
« Respuesta #5 en: 20 Febrero 2019, 20:28 pm »

Bueno, al final he decidido mejorarlo para extender la funcionalidad y crear un par de funciones reutilizables, aparte de que ahora el algoritmo preserva tanto el color de texto como el color de fondo, y he corregido un error que había en el primer código con respecto a la posición donde se escriben los caracteres...

Código
  1. Public NotInheritable Class Native
  2.  
  3.    Private Sub New()
  4.    End Sub
  5.  
  6.    <DllImport("kernel32.dll", SetLastError:=True)>
  7.    Public Shared Function GetStdHandle(ByVal std As ConsoleStd
  8.    ) As IntPtr
  9.    End Function
  10.  
  11.    <DllImport("kernel32.dll", SetLastError:=True)>
  12.    Public Shared Function ReadConsoleOutput(ByVal consoleOutput As IntPtr,
  13.                                             ByVal buffer As IntPtr,
  14.                                             ByVal bufferSize As ConsoleCoordinate,
  15.                                             ByVal bufferCoord As ConsoleCoordinate,
  16.                                             ByRef refReadRegion As NativeRectangleSmall
  17.    ) As <MarshalAs(UnmanagedType.Bool)> Boolean
  18.    End Function
  19.  
  20.    <StructLayout(LayoutKind.Sequential)>
  21.    Public Structure CharInfo ' CHAR_INFO
  22.        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)>
  23.        Public CharData As Byte()
  24.        <MarshalAs(UnmanagedType.I2)>
  25.        Public Attributes As CharInfoAttributes
  26.    End Structure
  27.  
  28.    <StructLayout(LayoutKind.Sequential)>
  29.    Public Structure ConsoleCoordinate ' COORD
  30.        Public X As Short
  31.        Public Y As Short
  32.    End Structure
  33.  
  34.    <StructLayout(LayoutKind.Sequential)>
  35.    Public Structure NativeRectangleSmall ' SMALL_RECT
  36.        Public Left As Short
  37.        Public Top As Short
  38.        Public Right As Short
  39.        Public Bottom As Short
  40.    End Structure
  41.  
  42.    Public Enum ConsoleStd As Integer
  43.        StandardInput = -10
  44.        StandardOutput = -11
  45.        StandardError = -12
  46.    End Enum
  47.  
  48.    <Flags>
  49.    Public Enum CharInfoAttributes As Short
  50.        None = &H0S ' Black Color
  51.        BlackColor = CharInfoAttributes.None
  52.        ForeColorBlue = &H1S
  53.        ForeColorGreen = &H2S
  54.        ForeColorRed = &H4S
  55.        ForeColorIntensity = &H8S
  56.        BackColorBlue = &H10S
  57.        BackColorGreen = &H20S
  58.        BackColorRed = &H40S
  59.        BackColorIntensity = &H80S
  60.        LeadingByte = &H100S
  61.        TrailingByte = &H200S
  62.        GridHorizontal = &H400S
  63.        GridVerticalLeft = &H800S
  64.        GridVerticalRight = &H1000S
  65.        ReverseVideo = &H4000S
  66.        Underscore = &H8000S
  67.        ForeColorMask = CharInfoAttributes.ForeColorBlue Or
  68.                        CharInfoAttributes.ForeColorGreen Or
  69.                        CharInfoAttributes.ForeColorRed Or
  70.                        CharInfoAttributes.ForeColorIntensity
  71.        BackColorMask = CharInfoAttributes.BackColorBlue Or
  72.                        CharInfoAttributes.BackColorGreen Or
  73.                        CharInfoAttributes.BackColorRed Or
  74.                        CharInfoAttributes.BackColorIntensity
  75.        ColorMask = CharInfoAttributes.ForeColorMask Or
  76.                    CharInfoAttributes.BackColorMask
  77.    End Enum
  78.  
  79. End Class

Código
  1. ''' ----------------------------------------------------------------------------------------------------
  2. ''' <summary>
  3. ''' Blinks the specified text for the specified amount of times on the current attached console window.
  4. ''' </summary>
  5. ''' ----------------------------------------------------------------------------------------------------
  6. ''' <example> This is a code example.
  7. ''' <code>
  8. ''' Dim pos As New Point(10, 2)
  9. ''' Dim str As String = "Hello World!"
  10. '''
  11. ''' Console.SetCursorPosition(pos.X, pos.Y)
  12. ''' Console.Write(str)
  13. '''
  14. ''' ' Start blinking the text written.
  15. ''' Dim len As Integer = str.Length
  16. ''' Dim interval As Integer = 500
  17. ''' Dim count As Integer = 10
  18. ''' ConsoleTextBlink(pos, len, interval, count)
  19. '''
  20. ''' ' Terminate program.
  21. ''' Console.ReadKey(intercept:=False)
  22. ''' </code>
  23. ''' </example>
  24. ''' ----------------------------------------------------------------------------------------------------
  25. ''' <param name="position">
  26. ''' A <see cref="System.Drawing.Point"/> that indicates the start position of the text.
  27. ''' <para></para>
  28. ''' <see cref="System.Drawing.Point.X"/> specifies the column, <see cref="System.Drawing.Point.Y"/> the row.
  29. ''' </param>
  30. '''
  31. ''' <param name="length">
  32. ''' The length of the text (or cells) to blink.
  33. ''' </param>
  34. '''
  35. ''' <param name="interval">
  36. ''' The blink interval, in milliseconds.
  37. ''' </param>
  38. '''
  39. ''' <param name="count">
  40. ''' The amount of times to blink the text.
  41. ''' </param>
  42. ''' ----------------------------------------------------------------------------------------------------
  43. ''' <returns>
  44. ''' A <see cref="CancellationTokenSource"/> object which you can use it to stop the blink at any time.
  45. ''' </returns>
  46. ''' ----------------------------------------------------------------------------------------------------
  47. <DebuggerStepThrough>
  48. Public Function ConsoleTextBlink(ByVal position As Point, ByVal length As Integer, ByVal interval As Integer, count As Integer) As CancellationTokenSource
  49.    Return InternalConsoleTextBlink(position, length, TimeSpan.FromMilliseconds(interval), count)
  50. End Function
  51.  
  52. ''' ----------------------------------------------------------------------------------------------------
  53. ''' <summary>
  54. ''' Blinks the specified text for the specified amount of times on the current attached console window.
  55. ''' </summary>
  56. ''' ----------------------------------------------------------------------------------------------------
  57. ''' <example> This is a code example.
  58. ''' <code>
  59. ''' Dim pos As New Point(10, 2)
  60. ''' Dim str As String = "Hello World!"
  61. '''
  62. ''' Console.SetCursorPosition(pos.X, pos.Y)
  63. ''' Console.Write(str)
  64. '''
  65. ''' ' Start blinking the text written.
  66. ''' Dim len As Integer = str.Length
  67. ''' Dim interval As TimeSpan = TimeSpan.FromMilliseconds(500)
  68. ''' Dim count As Integer = 10
  69. ''' ConsoleTextBlink(pos, len, interval, count)
  70. '''
  71. ''' ' Terminate program.
  72. ''' Console.ReadKey(intercept:=False)
  73. ''' </code>
  74. ''' </example>
  75. ''' ----------------------------------------------------------------------------------------------------
  76. ''' <param name="position">
  77. ''' A <see cref="System.Drawing.Point"/> that indicates the start position of the text.
  78. ''' <para></para>
  79. ''' <see cref="System.Drawing.Point.X"/> specifies the column, <see cref="System.Drawing.Point.Y"/> the row.
  80. ''' </param>
  81. '''
  82. ''' <param name="length">
  83. ''' The length of the text (or cells) to blink.
  84. ''' </param>
  85. '''
  86. ''' <param name="interval">
  87. ''' The blink interval.
  88. ''' </param>
  89. '''
  90. ''' <param name="count">
  91. ''' The amount of times to blink the text.
  92. ''' </param>
  93. ''' ----------------------------------------------------------------------------------------------------
  94. ''' <returns>
  95. ''' A <see cref="CancellationTokenSource"/> object which you can use it to stop the blink at any time.
  96. ''' </returns>
  97. ''' ----------------------------------------------------------------------------------------------------
  98. <DebuggerStepThrough>
  99. Public Function ConsoleTextBlink(ByVal position As Point, ByVal length As Integer, ByVal interval As TimeSpan, count As Integer) As CancellationTokenSource
  100.    Return InternalConsoleTextBlink(position, length, interval, count)
  101. End Function
  102.  
  103. ''' ----------------------------------------------------------------------------------------------------
  104. ''' <summary>
  105. ''' Blinks the specified text for indefinitely time on the current attached console window.
  106. ''' </summary>
  107. ''' ----------------------------------------------------------------------------------------------------
  108. ''' <example> This is a code example.
  109. ''' <code>
  110. ''' Dim pos As New Point(10, 2)
  111. ''' Dim str As String = "Hello World!"
  112. '''
  113. ''' Console.SetCursorPosition(pos.X, pos.Y)
  114. ''' Console.Write(str)
  115. '''
  116. ''' ' Start blinking the text written.
  117. ''' Dim len As Integer = str.Length
  118. ''' Dim interval As Integer = 500
  119. ''' Dim blinkCt As CancellationTokenSource = ConsoleTextBlink(pos, len, interval)
  120. '''
  121. ''' ' Stop blinking after 5 seconds elapsed.
  122. ''' blinkCt.CancelAfter(5000)
  123. '''
  124. ''' ' Terminate program.
  125. ''' Console.ReadKey(intercept:=False)
  126. ''' </code>
  127. ''' </example>
  128. ''' ----------------------------------------------------------------------------------------------------
  129. ''' <param name="position">
  130. ''' A <see cref="System.Drawing.Point"/> that indicates the start position of the text.
  131. ''' <para></para>
  132. ''' <see cref="System.Drawing.Point.X"/> specifies the column, <see cref="System.Drawing.Point.Y"/> the row.
  133. ''' </param>
  134. '''
  135. ''' <param name="length">
  136. ''' The length of the text (or cells) to blink.
  137. ''' </param>
  138. '''
  139. ''' <param name="interval">
  140. ''' The blink interval, in milliseconds.
  141. ''' </param>
  142. ''' ----------------------------------------------------------------------------------------------------
  143. ''' <returns>
  144. ''' A <see cref="CancellationTokenSource"/> object which you can use it to stop the blink at any time.
  145. ''' </returns>
  146. ''' ----------------------------------------------------------------------------------------------------
  147. <DebuggerStepThrough>
  148. Public Function ConsoleTextBlink(ByVal position As Point, ByVal length As Integer, ByVal interval As Integer) As CancellationTokenSource
  149.    Return InternalConsoleTextBlink(position, length, TimeSpan.FromMilliseconds(interval), Integer.MaxValue)
  150. End Function
  151.  
  152. ''' ----------------------------------------------------------------------------------------------------
  153. ''' <summary>
  154. ''' Blinks the specified text for indefinitely time on the current attached console window.
  155. ''' </summary>
  156. ''' ----------------------------------------------------------------------------------------------------
  157. ''' <example> This is a code example.
  158. ''' <code>
  159. ''' Dim pos As New Point(10, 2)
  160. ''' Dim str As String = "Hello World!"
  161. '''
  162. ''' Console.SetCursorPosition(pos.X, pos.Y)
  163. ''' Console.Write(str)
  164. '''
  165. ''' ' Start blinking the text written.
  166. ''' Dim len As Integer = str.Length
  167. ''' Dim interval As TimeSpan = TimeSpan.FromMilliseconds(500)
  168. ''' Dim blinkCt As CancellationTokenSource = ConsoleTextBlink(pos, len, interval)
  169. ''' blinkCt.CancelAfter()
  170. '''
  171. ''' ' Stop blinking after 5 seconds elapsed.
  172. ''' blinkCt.CancelAfter(5000)
  173. '''
  174. ''' ' Terminate program.
  175. ''' Console.ReadKey(intercept:=False)
  176. ''' </code>
  177. ''' </example>
  178. ''' ----------------------------------------------------------------------------------------------------
  179. ''' <param name="position">
  180. ''' A <see cref="System.Drawing.Point"/> that indicates the start position of the text.
  181. ''' <para></para>
  182. ''' <see cref="System.Drawing.Point.X"/> specifies the column, <see cref="System.Drawing.Point.Y"/> the row.
  183. ''' </param>
  184. '''
  185. ''' <param name="length">
  186. ''' The length of the text (or cells) to blink.
  187. ''' </param>
  188. '''
  189. ''' <param name="interval">
  190. ''' The blink interval.
  191. ''' </param>
  192. ''' ----------------------------------------------------------------------------------------------------
  193. ''' <returns>
  194. ''' A <see cref="CancellationTokenSource"/> object which you can use it to stop the blink at any time.
  195. ''' </returns>
  196. ''' ----------------------------------------------------------------------------------------------------
  197. <DebuggerStepThrough>
  198. Public Function ConsoleTextBlink(ByVal position As Point, ByVal length As Integer, ByVal interval As TimeSpan) As CancellationTokenSource
  199.    Return InternalConsoleTextBlink(position, length, interval, Integer.MaxValue)
  200. End Function
  201.  
  202. ''' ----------------------------------------------------------------------------------------------------
  203. ''' <summary>
  204. ''' Blinks the specified text for the specified amount of times on the current attached console window.
  205. ''' </summary>
  206. ''' ----------------------------------------------------------------------------------------------------
  207. ''' <param name="position">
  208. ''' A <see cref="System.Drawing.Point"/> that indicates the start position of the text.
  209. ''' <para></para>
  210. ''' <see cref="System.Drawing.Point.X"/> specifies the column, <see cref="System.Drawing.Point.Y"/> the row.
  211. ''' </param>
  212. '''
  213. ''' <param name="length">
  214. ''' The length of the text (or cells) to blink.
  215. ''' </param>
  216. '''
  217. ''' <param name="interval">
  218. ''' The blink interval.
  219. ''' </param>
  220. '''
  221. ''' <param name="count">
  222. ''' The amount of times to blink the text.
  223. ''' </param>
  224. ''' ----------------------------------------------------------------------------------------------------
  225. ''' <returns>
  226. ''' A <see cref="CancellationTokenSource"/> object which you can use it to stop the blink at any time.
  227. ''' </returns>
  228. ''' ----------------------------------------------------------------------------------------------------
  229. <DebuggerStepThrough>
  230. Private Function InternalConsoleTextBlink(ByVal position As Point, ByVal length As Integer, ByVal interval As TimeSpan, ByVal count As Integer) As CancellationTokenSource
  231.  
  232.    If (count <= 0) Then
  233.        Throw New ArgumentException(paramName:=NameOf(count), message:="Value greater than 0 is required.")
  234.    End If
  235.  
  236.    If (interval.TotalMilliseconds <= 0) Then
  237.        Throw New ArgumentException(paramName:=NameOf(interval), message:="Value greater than 0 is required.")
  238.    End If
  239.  
  240.    Dim cts As New CancellationTokenSource()
  241.  
  242.    Dim t As New Task(
  243.        Sub()
  244.            Dim x As Short = CShort(position.X)
  245.            Dim y As Short = CShort(position.Y)
  246.            Dim width As Short = CShort(length)
  247.            Dim height As Short = 1S
  248.            Dim buffer As IntPtr = Marshal.AllocHGlobal(width * height * Marshal.SizeOf(GetType(Native.CharInfo)))
  249.            Dim blinkCount As Integer
  250.  
  251.            Try
  252.                Dim bufferCoord As New Native.ConsoleCoordinate()
  253.                Dim bufferSize As New Native.ConsoleCoordinate With {
  254.                    .X = width,
  255.                    .Y = height
  256.                }
  257.  
  258.                Dim rc As New Native.NativeRectangleSmall With {
  259.                    .Left = x,
  260.                    .Top = y,
  261.                    .Right = (x + width - 1S),
  262.                    .Bottom = (y + height - 1S)
  263.                }
  264.  
  265.                Dim stdOutHandle As IntPtr = Native.GetStdHandle(Native.ConsoleStd.StandardOutput)
  266.                If Not Native.ReadConsoleOutput(stdOutHandle, buffer, bufferSize, bufferCoord, rc) Then
  267.                    ' Not enough storage is available to process this command' may be raised for buffer size > 64K (see ReadConsoleOutput doc.)
  268.                    Throw New Win32Exception(Marshal.GetLastWin32Error())
  269.                End If
  270.  
  271.                Dim charInfoList As New List(Of Native.CharInfo)
  272.                Dim ptr As IntPtr = buffer
  273.                For heightIndex As Integer = 0 To (height - 1)
  274.                    For widthIndex As Integer = 0 To (width - 1)
  275.                        Dim ci As Native.CharInfo = DirectCast(Marshal.PtrToStructure(ptr, GetType(Native.CharInfo)), Native.CharInfo)
  276.                        charInfoList.Add(ci)
  277.                        ptr += Marshal.SizeOf(GetType(Native.CharInfo))
  278.                    Next widthIndex
  279.                Next heightIndex
  280.  
  281.                Do Until cts.Token.IsCancellationRequested
  282.                    Dim oldCursorVisible As Boolean = Console.CursorVisible
  283.                    Dim oldPos As New Point(Console.CursorLeft, Console.CursorTop)
  284.                    Dim oldBackColor As ConsoleColor = Console.BackgroundColor
  285.                    Dim oldForeColor As ConsoleColor = Console.ForegroundColor
  286.  
  287.                    Console.CursorVisible = False
  288.                    Console.SetCursorPosition(position.X, position.Y)
  289.                    Console.Write(New String(" "c, length))
  290.                    Console.CursorVisible = oldCursorVisible
  291.                    Console.SetCursorPosition(oldPos.X, oldPos.Y)
  292.                    Thread.Sleep(interval)
  293.                    Console.CursorVisible = False
  294.  
  295.                    For i As Integer = 0 To (charInfoList.Count - 1)
  296.                        Dim ci As Native.CharInfo = charInfoList(i)
  297.                        Dim chars As Char() = (From c As Char In Console.OutputEncoding.GetChars(ci.CharData)
  298.                                               Where (c <> Nothing)).ToArray()
  299.  
  300.                        Dim foreColor As ConsoleColor
  301.                        If ((ci.Attributes And Native.CharInfoAttributes.ForeColorMask) <> 0) Then
  302.                            foreColor = CType((CInt(ci.Attributes)) And Not Native.CharInfoAttributes.BackColorMask, ConsoleColor)
  303.                        End If
  304.  
  305.                        Dim backColor As ConsoleColor
  306.                        If ((ci.Attributes And Native.CharInfoAttributes.BackColorMask) <> 0) Then
  307.                            ' Turn background colors into foreground colors.
  308.                            ' https://referencesource.microsoft.com/#mscorlib/system/console.cs,7a88edaade340cdb
  309.                            backColor = CType((CInt(ci.Attributes)) >> 4, ConsoleColor)
  310.                        End If
  311.  
  312.                        Console.SetCursorPosition((position.X + i), position.Y)
  313.                        Console.ForegroundColor = foreColor
  314.                        Console.BackgroundColor = backColor
  315.                        Console.Write(chars)
  316.                    Next i
  317.  
  318.                    Console.SetCursorPosition(oldPos.X, oldPos.Y)
  319.                    Console.CursorVisible = oldCursorVisible
  320.                    Console.ForegroundColor = oldForeColor
  321.                    Console.BackgroundColor = oldBackColor
  322.  
  323.                    If Interlocked.Increment(blinkCount) = count Then
  324.                        If (cts.Token.CanBeCanceled) Then
  325.                            cts.Cancel()
  326.                        End If
  327.                        Exit Do
  328.                    End If
  329.                    Thread.Sleep(interval)
  330.                Loop
  331.  
  332.            Finally
  333.                Marshal.FreeHGlobal(buffer)
  334.  
  335.            End Try
  336.  
  337.        End Sub, cts.Token)
  338.  
  339.    t.Start()
  340.    Return cts
  341.  
  342. End Function

Modo de empleo:

Código
  1. Dim pos As New Point(10, 2)
  2. Dim str As String = "Hello World!"
  3.  
  4. Console.SetCursorPosition(pos.X, pos.Y)
  5. Console.Write(str)
  6.  
  7. ' Start blinking the text written.
  8. Dim len As Integer = str.Length
  9. Dim interval As Integer = 500
  10. Dim count As Integer = 10
  11. ConsoleTextBlink(pos, len, interval, count)
  12.  
  13. ' Terminate program.
  14. Console.ReadKey(intercept:=False)

O bien...
Código
  1. Dim pos As New Point(10, 2)
  2. Dim str As String = "Hello World!"
  3.  
  4. Console.SetCursorPosition(pos.X, pos.Y)
  5. Console.Write(str)
  6.  
  7. ' Start blinking the text written.
  8. Dim len As Integer = str.Length
  9. Dim interval As TimeSpan = TimeSpan.FromMilliseconds(500)
  10. Dim blinkCt As CancellationTokenSource = ConsoleTextBlink(pos, len, interval)
  11.  
  12. ' Stop blinking after 5 seconds elapsed.
  13. blinkCt.CancelAfter(5000)
  14.  
  15. ' Terminate program.
  16. Console.ReadKey(intercept:=False)
« Última modificación: 20 Febrero 2019, 20:43 pm por Eleкtro (aliviado) » En línea



Meta


Desconectado Desconectado

Mensajes: 3.501



Ver Perfil WWW
Re: Timer en vez de Sleep.
« Respuesta #6 en: 20 Febrero 2019, 21:22 pm »

Buenas:

No esperaba que usaras el kernel32.dll.

Voy a examinar.

Muchas gracias. ;)
En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.878



Ver Perfil
Re: Timer en vez de Sleep.
« Respuesta #7 en: 20 Febrero 2019, 22:33 pm »

No esperaba que usaras el kernel32.dll.

...¿y eso lo dices por el último código que he compartido?. Sin comentarios. Si cuando digo que una persona no lee nada de lo que le muestran, es por que tengo razón...

Un saludo
En línea



Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.878



Ver Perfil
Re: Timer en vez de Sleep.
« Respuesta #8 en: 22 Febrero 2019, 06:18 am »

La función InternalConsoleTextBlink que compartí arriba quedaría obsoleta, ya que llamando a la función nativa FillConsoleOutputCharacter se puede simplificar y mejorar mucho su rendimiento y también eliminar algunas imperfecciones visuales que había con el cursor de texto de la consola...

Código
  1. <DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
  2. Public Shared Function FillConsoleOutputCharacter(ByVal hConsoleOutput As IntPtr,
  3.                                                  ByVal character As Char,
  4.                                                  ByVal length As UInteger,
  5.                                                  ByVal writeCoord As ConsoleCoordinate,
  6.                                          <[Out]> ByRef refNumberOfCharsWritten As UInteger
  7. ) As <MarshalAs(UnmanagedType.Bool)> Boolean
  8. End Function
  9.  
  10. <DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
  11. Public Shared Function FillConsoleOutputAttribute(ByVal hConsoleOutput As IntPtr,
  12.                                                  ByVal attribute As CharInfoAttributes,
  13.                                                  ByVal length As UInteger,
  14.                                                  ByVal writeCoord As ConsoleCoordinate,
  15.                                          <[Out]> ByRef refNumberOfAttrsWritten As UInteger
  16. ) As <MarshalAs(UnmanagedType.Bool)> Boolean
  17. End Function

El algoritmo final sería este:
Código
  1. <DebuggerStepThrough>
  2. Private Function InternalConsoleTextBlink(ByVal position As System.Drawing.Point, ByVal length As Integer, ByVal interval As TimeSpan, ByVal count As Integer) As CancellationTokenSource
  3.  
  4.    If (count <= 0) Then
  5.        Throw New ArgumentException(paramName:=NameOf(count), message:="Value greater than 0 is required.")
  6.    End If
  7.  
  8.    If (interval.TotalMilliseconds <= 0) Then
  9.        Throw New ArgumentException(paramName:=NameOf(interval), message:="Value greater than 0 is required.")
  10.    End If
  11.  
  12.    Dim cts As New CancellationTokenSource()
  13.  
  14.    Dim t As New Task(
  15.        Sub()
  16.            Dim x As Short = CShort(position.X)
  17.            Dim y As Short = CShort(position.Y)
  18.            Dim width As Short = CShort(length)
  19.            Dim height As Short = 1S
  20.            Dim buffer As IntPtr = Marshal.AllocHGlobal(width * height * Marshal.SizeOf(GetType(CharInfo)))
  21.            Dim blinkCount As Integer
  22.  
  23.            Try
  24.                Dim bufferCoord As New ConsoleCoordinate()
  25.                Dim bufferSize As New ConsoleCoordinate With {
  26.                    .X = width,
  27.                    .Y = height
  28.                }
  29.  
  30.                Dim rc As New NativeRectangleSmall With {
  31.                    .Left = x,
  32.                    .Top = y,
  33.                    .Right = (x + width - 1S),
  34.                    .Bottom = (y + height - 1S)
  35.                }
  36.  
  37.                Dim stdOutHandle As IntPtr = NativeMethods.GetStdHandle(ConsoleStd.StandardOutput)
  38.                If Not NativeMethods.ReadConsoleOutput(stdOutHandle, buffer, bufferSize, bufferCoord, rc) Then
  39.                    ' Not enough storage is available to process this command' may be raised for buffer size > 64K (see ReadConsoleOutput doc.)
  40.                    Throw New Win32Exception(Marshal.GetLastWin32Error())
  41.                End If
  42.  
  43.                Dim charDict As New Dictionary(Of CharInfo, ConsoleCoordinate)
  44.  
  45.                Dim ptr As IntPtr = buffer
  46.                For heightIndex As Integer = 0 To (height - 1)
  47.                    For widthIndex As Integer = 0 To (width - 1)
  48.                        Dim ci As CharInfo = DirectCast(Marshal.PtrToStructure(ptr, GetType(CharInfo)), CharInfo)
  49.                        charDict.Add(ci, New ConsoleCoordinate() With {
  50.                                 .X = CShort(position.X + widthIndex),
  51.                                 .Y = CShort(position.Y + heightIndex)})
  52.                        ptr += Marshal.SizeOf(GetType(CharInfo))
  53.                    Next widthIndex
  54.                Next heightIndex
  55.  
  56.                Do Until cts.Token.IsCancellationRequested
  57.                    ' Remove text.
  58.                    NativeMethods.FillConsoleOutputCharacter(stdOutHandle, " "c, CUInt(length), charDict.Values(0), Nothing)
  59.                    Thread.Sleep(interval)
  60.  
  61.                    ' Write Text.
  62.                    For Each kv As KeyValuePair(Of CharInfo, ConsoleCoordinate) In charDict
  63.                        Dim c As Char = System.Console.OutputEncoding.GetChars(kv.Key.CharData)(0)
  64.                        NativeMethods.FillConsoleOutputAttribute(stdOutHandle, kv.Key.Attributes, 1, kv.Value, Nothing)
  65.                        NativeMethods.FillConsoleOutputCharacter(stdOutHandle, c, 1, kv.Value, Nothing)
  66.                    Next kv
  67.  
  68.                    If Interlocked.Increment(blinkCount) = count Then
  69.                        If (cts.Token.CanBeCanceled) Then
  70.                            cts.Cancel()
  71.                        End If
  72.                        Exit Do
  73.                    End If
  74.                    Thread.Sleep(interval)
  75.                Loop
  76.  
  77.            Finally
  78.                Marshal.FreeHGlobal(buffer)
  79.  
  80.            End Try
  81.  
  82.        End Sub, cts.Token)
  83.  
  84.    t.Start()
  85.    Return cts
  86.  
  87. End Function

EDITO: código actualizado.

Saludos.
« Última modificación: 26 Febrero 2019, 02:01 am por Eleкtro » En línea



Meta


Desconectado Desconectado

Mensajes: 3.501



Ver Perfil WWW
Re: Timer en vez de Sleep.
« Respuesta #9 en: 23 Febrero 2019, 12:00 pm »

Buena explicación. ;)

Gracias.
En línea

Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Mensajes similares
Asunto Iniciado por Respuestas Vistas Último mensaje
problema con API sleep
Programación Visual Basic
vivachapas 7 3,143 Último mensaje 31 Mayo 2007, 01:04 am
por Freeze.
alternativa a sleep
PHP
z_ane_666 5 5,580 Último mensaje 24 Mayo 2011, 05:35 am
por [u]nsigned
Sleep Timer, temporizador automático para Windows
Noticias
wolfbcn 2 3,015 Último mensaje 11 Marzo 2012, 00:30 am
por Kase
Do not Sleep (bloquea el apagado de tu pc)
Seguridad
El Che Guevara 1 2,151 Último mensaje 14 Abril 2013, 01:32 am
por r32
Dudas con Timer como sustituto de Sleep -- por precisión del tiempo
.NET (C#, VB.NET, ASP)
SARGE553413 4 3,350 Último mensaje 25 Julio 2014, 16:19 pm
por SARGE553413
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines