I'm looking for the fastest way to take a print-screen, and i found out that using Bitblt was my better choice, however, it only works for device context handle's, which means for me to retrieve a bitmap from that, i'd have to use multiple API's including CreateCompatibleBitmap, which in the end it probably takes the same time as using a managed way, like graphics.CopyFromScreen (which is a bit slow for me and also consumes alot of CPU, between 7-10% on a 2.3ghz quad-core processor...)
However, i still searched for a cleaner way of retrieving a bitmap from it, so i came up with this code:
<DllImport("user32.dll")> _
Public Shared Function GetDC(ByVal hWnd As IntPtr) As IntPtr
End Function
<DllImport("user32.dll")> _
Public Shared Function ReleaseDC(ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Integer
End Function
<DllImport("gdi32.dll")> _
Public Shared Function BitBlt(ByVal hdcDest As IntPtr, ByVal xDest As Integer, ByVal yDest As Integer, ByVal wDest As Integer, ByVal hDest As Integer, ByVal hdcSource As IntPtr, _
ByVal xSrc As Integer, ByVal ySrc As Integer, ByVal rop As TernaryRasterOperations) As Boolean
End Function
Dim hwNd As IntPtr = Nothing
hwNd = GetDC(GetDesktopWindow)
picHandle = GetDC(Me.PictureBox1.Handle)
BitBlt(picHandle, 0, 0, PictureBox1.Width, PictureBox1.Height, hwNd, 0, 0, TernaryRasterOperations.SRCCOPY)
ReleaseDC(hwNd, picHandle)
I can reach ~30 fps with this... But it has two problems as i said above:
Even if displaying it on a picturebox as i'm doing it above accomplished what i want, it doesn't resize to the picturebox control, even if i change those "0" values to the picturebox x and y coordinates.
I further searched and found there's a StretchBit API for that, and it does stretch, but it also reduces quality, (Even with the necessary call to SetStretchBltMode with parameter "HALFTONE" so it doesn't "corrupt" the pixels), it also reduces performance at least in 10+ fps...
But as i need to get it as bitmap object, with the other necessary API's for that, i ended up with almost half the performance (15~ fps) which is equivalent of graphics.CopyFromScreen.
So, i'm asking, is there another way to get a bitmap from the screen using Bitblt or similar without losing performance?
If there isn't a .Net way, i kindly ask for any language-way of doing that.
If you want raw performance, you will have to get away from managed code. This is easy enough using C++ with Visual Studio. You can make calls directly to the Windows API, bypassing the .NET runtime, managed code for your application, and the overhead of p/invokes in .NET.
If you are familiar with C#, you can take your C# code, convert it to C++ (which should be straightforward, with a lot of work to replace the CLI).
Private Declare Function BitBlt Lib "GDI32" ( _
ByVal hdcDest As Integer, _
ByVal nXDest As Integer, _
ByVal nYDest As Integer, _
ByVal nWidth As Integer, _
ByVal nHeight As Integer, _
ByVal hdcSrc As Integer, _
ByVal nXSrc As Integer, _
ByVal nYSrc As Integer, _
ByVal dwRop As System.Int32) As Boolean
Declare Function QueryPerformanceCounter Lib "Kernel32" (ByRef X As Long) As Short
Declare Function QueryPerformanceFrequency Lib "Kernel32" (ByRef X As Long) As Short
Const SRCCOPY As Integer = &HCC0020
Use a form with only a picturebox and a label in it. Set the anchors of picbox accordingly. In picbox down event:
Private Sub PictureBox1_MouseDown(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
Dim Ctr1, Ctr2, Freq As Long
Dim dbl As Double
QueryPerformanceCounter(Ctr1)
Dim desktopDC As IntPtr = Nothing
Dim picboxDC As IntPtr = Nothing
desktopDC = GetDC(New IntPtr(0))
picboxDC = GetDC(PictureBox1.Handle)
BitBlt(picboxDC, 0, 0, PictureBox1.Width, PictureBox1.Height, desktopDC, 0, 0, SRCCOPY)
QueryPerformanceCounter(Ctr2)
QueryPerformanceFrequency(Freq)
dbl = (Ctr2 - Ctr1) / Freq
dbl *= 1000000
Label1.Text = dbl.ToString 'it is in microseconds
ReleaseDC(New IntPtr(0), desktopDC)
ReleaseDC(PictureBox1.Handle, picboxDC)
End Sub
Maximize your form and click in picturebox.
Related
I'm making a text editor application using vb2010 WinForm. Instead of scrolling with scrollbar, users can scroll directly on the richtextbox with the mouse, similar to adobe acrobat reader. To scroll richtextbox programatically I'm using SendMessage user32 API.
I have two problems:
If the text in richtextbox is big and I scrolled near the end of integer value then scrollbar will scroll back to its initial position.
The scrollbar value that has been set using SendMessage is not the same when we read it later with GetScrollPos. As a result, when I dragged the text using mouse, the richtextbox does not scroll smoothly at the beginning, it's jump.
Here's what I've done:
Public Class Form1
Dim StartMouseDownPos As New Point
Dim StartScrollBarPos As New Point
Const WM_USER = &H400
Const EM_GETSCROLLPOS = WM_USER + 221
Const EM_SETSCROLLPOS = WM_USER + 222
Public Declare Auto Function RtfScroll Lib "user32.dll" Alias "SendMessage" ( _
ByVal hWnd As IntPtr, _
ByVal Msg As Integer, _
ByVal wParam As IntPtr, _
ByRef lParam As System.Drawing.Point) As Integer
Private Sub RichTextBox1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles RichTextBox1.MouseDown
'Capture the initial mouse position
StartMouseDownPos.X = e.X
StartMouseDownPos.Y = e.Y
'Capture the initial scrollbar position
RtfScroll(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, StartScrollBarPos)
End Sub
Private Sub RichTextBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles RichTextBox1.MouseMove
'Verify left button is pressed while the mouse is moving
If e.Button = Windows.Forms.MouseButtons.Left Then
'Prevent the text in RichTextBox1 to be unintentionally selected when user dragged the text while the cursor shape at that moment is a hand.
ActiveControl = Nothing
NewScrollBarPos.X = StartScrollBarPos.X + (StartMouseDownPos.X - e.X)
NewScrollBarPos.Y = StartScrollBarPos.Y + (StartMouseDownPos.Y - e.Y)
RtfScroll(RichTextBox1.Handle, EM_SETSCROLLPOS, 0, NewScrollBarPos)
End If
End Sub
I tried to change the problematic statement above: RtfScroll(RichTextBox1.Handle, EM_SETSCROLLPOS, 0, NewScrollBarPos) with the following:
Public Declare Function GetScrollPos Lib "user32.dll" ( _
ByVal hWnd As IntPtr, _
ByVal nBar As Integer) As Integer
Public Declare Function SetScrollPos Lib "user32.dll" ( _
ByVal hWnd As IntPtr, _
ByVal nBar As Integer, _
ByVal nPos As Integer, _
ByVal bRedraw As Boolean) As Integer
Public Declare Function PostMessageA Lib "user32.dll" ( _
ByVal hwnd As IntPtr, _
ByVal wMsg As Integer, _
ByVal wParam As Integer, _
ByVal lParam As Integer) As Boolean
'Scroll the horizontal scrollbar according to the drag of the mouse
SetScrollPos(RichTextBox1.Handle, SBS_HORZ, NewScrollBarPos.X, True)
SetScrollPos(RichTextBox1.Handle, SBS_VERT, NewScrollBarPos.Y, True)
'Scroll the text according to the drag of the mouse
PostMessageA(RichTextBox1.Handle, WM_HSCROLL, SB_THUMBPOSITION + &H10000 * GetScrollPos(RichTextBox1.Handle, SBS_HORZ), Nothing)
PostMessageA(RichTextBox1.Handle, WM_VSCROLL, SB_THUMBPOSITION + &H10000 * GetScrollPos(RichTextBox1.Handle, SBS_VERT), Nothing)
The result is even worse: an overflow exception raised at multiplication of &H10000 * GetScrollPos(RichTextBox1.Handle, SBS_HORZ), Nothing), that happen when I tried to scroll beyond integer value.
So, my question is how to solve these two problems?
I've made a small program to find out what the cause of my problem, and it turns out that the problem is this: RtfScroll function alias "SendMessage" which uses Point as input doesn't get or set the correct value. This doesn't happen to GetScrollPos, SetScrollPos, and PostMessageA.
So, don't use a combination of RtfScroll(RichTextBox1.Handle, EM_SETSCROLLPOS, 0, New Point(countX, countY)) & RtfScroll(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, point).
Just use a combination of GetScrollPos, SetScrollPos, and PostMessageA.
I am trying to create handle to Windows Phone device. But CreateFileW returns -1 and Error code is 3 ERROR_PATH_NOT_FOUND. Any help to this problem?
My code:
handle = CreateFileW("\\\\.\\NOKIA_TOUCH", GENERIC_READ Or GENERIC_WRITE, FILE_SHARE_READ Or FILE_SHARE_WRITE, IntPtr.Zero, 3, 0, IntPtr.Zero)
If handle = -1 Then
ShowMsg(Marshal.GetLastWin32Error)
Else
ShowMsg("Success!")
End If
EDIT: P/Invoke code:
<DllImport("kernelBase.dll", CharSet:=CharSet.Unicode, ExactSpelling:=False, PreserveSig:=True, SetLastError:=True)>
Public Shared Function CreateFileW(ByVal lpFileName As String, ByVal dwDesiredAccess As Integer, ByVal dwShareMode As Integer, ByVal lpSecurityAttributes As IntPtr, ByVal dwCreationDisposition As Integer, ByVal dwFLagsAndAttributes As Integer, ByVal hTemplateFile As IntPtr) As IntPtr
End Function
This is unlikely to work (you probably don't have security capability to talk to the Nokia driver) and even if it does work while debugging on your device you won't be able to submit it to the Windows Store (unless you work for an OEM that needs to use this driver).
What are you trying to do with the driver that can't be done through the public API?
I'm running a program that makes some ALPHA effects on labels, fonts, images using the Windows´ SetLayeredWindowAttributes function. It´s functioning prefectly within Visual Studio 2013, running without Administrator priviledge.
I run in DEBUG and RELEASE mode from VS2013 and I know the ADMIN is not set because I call VS normally and I execute some "drag-and-drop" operations (which require a NORMAL priviledge instead ADMIN).
But, if I compile and run the program within its normal folder (C:\ProgramFiles\etc) in the SAME computer, the function does not appear to be executing - all labels, images and so forth appear suddenly.
So, is this function an ADMIN-PRIVILEDGE required?
The code for testing is:
Private Const GWL_EXSTYLE = (-20)
Private Const WS_EX_LAYERED = &H80000
Private Const LWA_ALPHA = &H2
Private Declare Function GetWindowLong Lib "user32" _
Alias "GetWindowLongA" (ByVal hWnd As Integer, _
ByVal nIndex As Integer) As Integer
Private Declare Function SetWindowLong Lib "user32" _
Alias "SetWindowLongA" (ByVal hWnd As Integer, _
ByVal nIndex As Integer, ByVal dwNewLong As Integer) _
As Integer
Private Declare Function SetLayeredWindowAttributes Lib _
"user32" (ByVal hWnd As Integer, ByVal crKey As Integer, _
ByVal bAlpha As Byte, ByVal dwFlags As Integer) As Integer
Public Function TransForm(ByVal fhWnd As Long, ByVal Alpha As Byte) As Boolean
'Set alpha between 0-255
' 0 = Invisible , 128 = 50% transparent , 255 = Opaque
SetWindowLong(fhWnd, GWL_EXSTYLE, WS_EX_LAYERED)
SetLayeredWindowAttributes(fhWnd, 0, Alpha, LWA_ALPHA)
LastAlpha = Alpha
TransForm = True
End Function
------ CODE -----
TransForm(lblBottomLeft.Handle.ToInt32, CByte(i))
TransForm(lblBottomRight.Handle.ToInt32, CByte(i))
For i = 255 To 0 Step -5
TransForm(Me.Handle.ToInt32, CByte(i))
sleep(10)
Next
TransForm(Me.Handle.ToInt32, 0)
Thanks for any help.
UPDATE:
Just the labels are not fading, although they fade within VS2013. Since labels have no OPACITY property, it´s clear they won´t fade - at least in the very first moment. But using that API they fade like the form, but ONLY within the VS2013.
Edit: Fixed, I created a compatibleDC for the graphics object, and a handle for the bitmap (using b.gethbitmap), then used the SelectObject function inside GDI to select those two, and used the compatibleDC instead of hDc in the BitBlt function
I've been trying to draw a bitmap to the screen (device 0), however I have encountered a problem copying the graphics using BitBlt.
Initially, I was drawing directly to the desktop using SetPixel (gdi32), but it was slow, so now I am setting the pixels of a bitmap object and then creating graphics from that object, and copying the hdc of the graphics to the screen.
My guess is that I am adding the HDC of the graphics object to an intptr, which essentially gives me the HDC of the container of the graphics object, which is not what I need. However even so, I have not found any information on how I could copy a bitmap to a device other than using BitBlt.
This is my current code (Windows forms app, textbox, button) The textbox is the device to copy to, and the button starts it. For testing purposes, set the textbox text to 0, and press the button. You should see a black box (50x50px) in the top left corner of your screen. The colour should be blue if it is working correctly:
Public Class Form1
Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Int32) As Int32
Declare Function ReleaseDC Lib "user32.dll" (ByVal hwnd As Int32, ByVal hdc As Int32) As Int32
Declare Function SetPixel Lib "gdi32.dll" (ByVal hdc As Integer, ByVal x As Integer, ByVal y As Integer, ByVal crColor As Integer) As Integer
Declare Function BitBlt Lib "gdi32.dll" (ByVal hdcDest As IntPtr, ByVal nXDest As Integer, ByVal nYDest As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hdcSrc As IntPtr, ByVal nXSrc As Integer, ByVal nYSrc As Integer, ByVal dwRop As Int32) As Boolean
Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As IntPtr, ByVal nWidth As Integer, ByVal nHeight As Integer) As IntPtr
Declare Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hdc As IntPtr) As IntPtr
Dim x As Integer
Sub setpx(ByVal location As Point, ByVal color As Color)
b.SetPixel(location.X, location.Y, color)
End Sub
Sub drawrectangle(ByVal device As Integer, ByVal location As Point, ByVal size As Point, ByVal color As Color)
b = New Bitmap(size.X, size.Y)
For i = location.X To size.X - 1
For z = location.Y To size.Y - 1
setpx(New Point(i, z), color)
Next
Next
g = Graphics.FromImage(b)
Dim hDc As IntPtr = g.GetHdc
BitBlt(GetDC(device), location.X, location.Y, size.X, size.Y, hDc, location.X, location.Y, 13369376)
ReleaseDC(device, GetDC(device))
End Sub
Dim b As Bitmap
Dim g As Graphics
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Buttnon1.Click
Dim r As New Random
Dim timestart As Integer = Now.TimeOfDay.TotalMilliseconds
drawrectangle(TextBox1.Text, New Point(1, 1), New Point(50, 50), Color.Blue)
MsgBox(Now.TimeOfDay.TotalMilliseconds - timestart)
End Sub
End Class
The way it works is it calls the function setpx, given a location and color from within a loop iterating through all of the pixels in a box (50x50 in my code). The setpx function will then call the setpixel function on a bitmap b. This part is working fine.
Next, it will define a graphics object g from the bitmap, and I use BitBlt to copy g's hdc (g.gethdc) to the screen. This is not working correctly, is this the correct way of doing this?
You are passing Textbox1.text as a device descriptor, that won't work...You want textbox1.hwnd
First of all I'm a total newby in visual basic, I needed to hack an application that kept clicking (don't ask).
Everything is nice and dandy on my pc, then I compile, move it to its final destination and I doesn't work! At first I thought it was a OS problem, but both machines has win7, I then thought it was a compilation problem, installed visual studio on the other pc, recompiled still with no luck, then it dawned on me, may it be a problem of 32bit vs 64bit?
Sadly I don't have enough knowledge about it and so I ask you.
The piece of code is this:
Private Sub mainTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mainTimer.Tick
Dim keyStart As Boolean
keyStart = GetAsyncKeyState(Keys.A)
If keyStart = True Then
timeClicker.Enabled = True
timeClicker.Start()
End If
Dim keyStop As Boolean
keyStop = GetAsyncKeyState(Keys.S)
If keyStop = True Then
timeClicker.Stop()
timeClicker.Enabled = False
End If
End Sub
Private Sub timeClicker_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles timeClicker.Tick
mouse_event(mouseclickdown, 0, 0, 0, 0)
mouse_event(mouseclickup, 0, 0, 0, 0)
End Sub
MainTimer has an interval of 100 and timeClicker has an interval of 10, both are declared on the form project (not in the code).
The MainTimer works perfectly (I've done tests) it's the timeClicker that doesn't work at all!
Can somebody tell me why and possibly help me understand the issue?
Thank you very much
EDIT: as requested by max
Private Declare Sub mouse_event Lib "user32" (ByVal dwflags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cbuttons As Long, ByVal dwExtraInfo As Long)
Private Const mouseclickup = 4
Private Const mouseclickdown = 2
By the way is not a problem of mouse_event, is the timer that doesn't work.
Yes, this cannot work on a 32-bit machine, it manages to scrape by on a 64-bit machine but that's sheer luck. Your pinvoke declaration dates from the VB6 era, it is quite wrong for VB.NET. Watch out for this, there are a lot of junk declarations out there. The pinvoke.net site is a good bet to get it right. Fix:
Private Declare Sub mouse_event Lib "user32" (ByVal dwflags As Integer, ByVal dx As Integer, _
ByVal dy As Integer, ByVal cbuttons As Integer, ByVal dwExtraInfo As IntPtr)
Another thing you want to do on your dev machine so you can debug this for a 32-bit machine is Project + Properties, Compile tab, scroll down, Advanced Compile Options, Target CPU = x86. Also enables Edit+Continue, you'll love it.
Note that your GetAsyncKeyState() declaration is almost certainly wrong as well. It returns Short, not Integer.
Try to declare mouse_event this way:
Private Declare Sub mouse_event Lib "user32" (ByVal dwflags As Integer, ByVal dx As Integer, ByVal dy As Integer, ByVal cbuttons As Integer, ByVal dwExtraInfo As IntPtr)
And call it this way:
mouse_event(0, 0, 0, 0, IntPtr.Zero)