UI Control들은 폼 구동시 실행되는 하나의 쓰레드에서 구동된다. 따라서 사용자가 실행시킨 쓰레드는 별도로 실행 되기 때문에 이 메인 쓰레드에 적절한 마샬링 없이 다른쓰레드에서 직접 접근하면 다른 쓰레드를 침범하는 것이다. (Cross Thread Problem) 이런 경우에는 프로그램이 개발자가 설계한대로 잘 동작하지 않을 수 있다.(Race Condition,DeadLock) 따라서, 안전하게 동작하게 하기위하여 .Net 환경에서는 Invoke를 제공하고 있다.
본 내용을 무시한 채 프로그램을 작성하면 InvalidOperationException을 발생시키고 . Debug 창에서 "컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다."라는 메세지가 표시 된다. 하지만 디버깅 환경이 아니라면 프로그램은 겉보기에 정상 동작하는 것 처럼 보일 수 있으므로 조심해야 된다. !
자 그럼 쓰레드에서 안전하게 Windows Form control을 제어하게 하는 방법에 대해 알아보자.
우선, InvokerRequired 를 알고 시작하자. 이 속성은 Invoke메쏘드를 호출해야되는 상황인지 알려준다.
Invoke
쓰레드에서 폼 컨트롤 하기 위해서는 별도의(SetTextononTextBox1) 메쏘드를 만들어 사용하면 좋다. 예를들어, Textbox에 값을 입력한다면 아래와 같은 'SetTextonTextBox1' 메쏘드를 만들어서 사용하면 Cross thread 환경이든 내부 쓰레드 사용환경에서든 둘다 사용할 수 있다. 또는 Delegate 메쏘드가 FormControl을 인자로 받아서 사용할 수도 있다.
Invoke를 사용할 때 인자를 넘겨줘야 하는 메쏘드 필요하면 할때는 반드시 delegate를 선언한 후에 사용하여야 한다.
우선 BeginInvoke를 설명하기 전에 Windows Form 의 Control.BeginInvoke 메쏘드와 Delegate.BeginInvoke 메쏘드에 대해서 차이점을 설명하고 본 단락에서는 Winfows Form Control의 BeginInvoke만 설명하도록 하겠다.
Delegate.BeginInvoke는 Asynchronous Delegate를 만들어서 콜백을 하는 것이라고 생각하면 되겠다. 다시말해서, CLR에서 관리하는 쓰레드풀에서 해당 메쏘드를 큐잉한다. 쉽게 얘기해서 별도의 쓰레드를 만들어서 Delegate를 실행한다고 보면 된다. 장점은 IAsyncResult를 이용해서 Object 결과값을 넘겨 받을 수 있다. 보다 자세한 사항은 이곳을 참조!
Windows Form의 Control.BeginInvoke는 Control 내부 핸들이 작성된 쓰레드(메인쓰레드)에서 지정된 대리자를 비동기식으로 실행한다. 비동기식이므로 실행을 대기하지 않고 BeginInvoke는 즉시 Return 한다.
차이점을 정리 하자면 Control.BeginInvoke는 실행코드의 GUI Thread에 작성된 코드이고 Delegate.BeginInvoke는 쓰레드풀 쓰레드에 사용된다. 또한 Control.BeginInvoke의 경우는 EndInvoke를 호출하지 않아도 되나 Delegate.BeginInvoke의 경우는 반드시 Delegate.EndInvoke를 호출해줘야 한다 안그러면 메모리 릭이 발생한다.
public delegate void InvokeDelegate();
private void Invoke_Click(object sender, EventArgs e) { myTextBox.BeginInvoke(new InvokeDelegate(InvokeMethod)); } public void InvokeMethod() { myTextBox.Text = "Executed the given delegate"; }
1. 아래는 모두 같다.. static const int ... static int const ... const static int ... const int static ... int const static ... int static const ... 2. 그러나 미테 2개는 다르다.. static const int * foo; // non-constant pointer to a constant variable. static int * const foo; // constant pointer to a non-constant variable.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Text.RegularExpressions;
R = rgbInputR.Match(colorX); G = rgbInputG.Match(colorX); B = rgbInputB.Match(colorX); //had to flip the R and B ??? b =int.Parse(R.Groups[0].Value); g =int.Parse(G.Groups[0].Value); r =int.Parse(B.Groups[0].Value); } } }
Ogre로 프로젝트를 하면서 가장 시간을 많이 들였던 충돌 처리 부분 입니다. 처음에는 물리 엔진을 써보고자 Physx , bullet, OgreODE 등 많은 물리엔진을 찾아서 사용 해보려 했지만...;; 결국 Ogre로 구현을 하게 되었습니다. ;; 게임 개발에 필요한 수학적 지식이 부족한 상황에서 검색을 하던중 directx 로 구현한 OBB 충돌 소스를 찾게 되어서 그 소스를 이용하여 Ogre에서 구현을 해봤습니다.
dx 소스는 GpgStudy 에서 참고 하였습니다. Link에 추가 해놓으테니까 참고 하시면 좋을 것 같습니다~~ 그리고 이만희님의 OBB 충돌 논문(Fast Overlap Test for OBB) 을 보시면 그래도 이해더 좀더 되 실 겁니다. 첨부 파일로 올려 놓겠습니다.(올려도 되려나??)
Vector3 obb1_center, obb2_center, T, LL; //box1,box2 의 각각 센터 좌표, Real box_len, a1, a2, a3, b1, b2, b3, TL; Matrix4 obb1_invers_mat, obb2_rotae_mat; Matrix4 R ;
//OBB 박스의 월드 좌표?? obb1_invers_mat = OBB1->getCharMat.inverse(); obb2_rotae_mat = OBB2->getMonMat();
//box1 의 좌표계를 box2의 좌표계로 회전변환 행렬 R = RA-1 * RB(-1은 iverse) R = obb1_invers_mat * obb2_rotae_mat;
//box의 좌표축의 normal vector x, y, z 값 (제가 이해한 내용 - 틀린 내용 일 수도 있음 ㅋ)
//box1 의좌표계에서 box1 중심에서 box2의 중심으로의 이동을 나타내는 vector3 T = Vector3(obb1_center.x - obb2_center.x, obb1_center.y - obb2_center.y, obb1_center.z - obb2_center.z);
구현을 해서 실행을 시켜본 결과 충돌 검출은 잘 하는데 살짝 떨어져 있어도 충돌 체크를 하는 경우가 있는데, 이유는 저도 잘...ㅠㅠ 그래도 이렇게해서 충돌 체크를 한다는거에 감사할 따름입니다. ㅋㅋ 혹시 소스 자체에 수정 할 부분이 있다고 생각되시는 분은 가르침 부탁 드리겠습니다^^ㅋ
인증샷!!!!
Ps. box의 Matrix를 받아 오는 함수를 사용해서 Matrix값을 받아오게 했었는데, 값이 이상 했나 보네여 ;; 충돌 처리가 정확히 되지 않았습니다. 그래서 객체의 Entity 의_getBoneMatrices.transpose()를 사용해서 Matrix값을 받아 와서 충돌처리를 했더니 정확하게 OBB의 충돌 위치에서 멈추는 것을 확인 할 수 있었습니다.
아직 정확한 Ogre의 분석이 되지 않은 상태에서 이것 저것 해보다가 얻어 걸린 것이라서 정확하게 어떻다라는 것을 제가 말씀 드릴 수는 없을 것 같습니다. 이해해 주시길..^^;; ㅋㅋ
(소스에서 이상한점을 발견 하시거나 잘못 된 곳을 발견하시면 저에게도 살포시 알려주세요~ㅋㅋㅋ)
Note that for non-relative mouse movement (i.e. if MOUSEEVENTF_ABSOLUTE is not specified as part of dwFlags), negative values for dx and dy are desirable. As such, the "uint" type specification for C# can be safely replaced with Int32.
//Use the values of this enum for the 'dwData' parameter //to specify an X button when using MouseEventFlags.XDOWN or //MouseEventFlags.XUP for the dwFlags parameter. public enum MouseEventDataXButtons : uint { XBUTTON1 = 0x00000001, XBUTTON2 = 0x00000002 }
VB.Net Signature:
This function does indeed return a value, just as the keybd_event API does.
If there is a real error, it will in fact return a value of false, or zero.
The dwExtraInfo appears to be a ULONG_PTR in C++ (IntPtr in VB.NET)
Declare Function apimouse_event Lib "user32.dll" Alias "mouse_event" (ByVal dwFlags As Int32, ByVal dX As Int32, ByVal dY As Int32, ByVal cButtons As Int32, ByVal dwExtraInfo As Int32) As Boolean
Notes:
FYI, Microsoft tells us for "Windows NT/2000/XP: This function has been superseded. Use SendInput instead."
The C# code below works fine. However you have to keep in mind to add the namespace "System.Runtime.InteropServices" and keep in mind to write the code into a class.
Original contributor tells us:aa
I wanted to emulate the scroll. Searching for this information wasn't easy ... but here is how you do the mouse scroll button
The scroll value can actually be any value larger than 1, if used within a loop.
This allows you to smoothly increment the scrollbar, instead of relying on the inconsistent wheel delta,
which is a variable limited by the user, for mouse wheel. Start-Control Panel-Mouse-Wheel-Scrolling
VB.NET Sample Code:
Const MOUSEEVENTF_WHEEL As Int32 = 2048 Const MOUSEEVENTF_WHEEL_DELTA As Int32 = 120
Private Declare Function apimouse_event Lib "user32" Alias "mouse_event" (ByVal dwFlags As Int32, ByVal dX As Int32, ByVal dY As Int32, ByVal cButtons As Int32, ByVal dwExtraInfo As Int32) As Boolean Private Declare Function apiGetMessageExtraInfo Lib "user32" Alias "GetMessageExtraInfo" () As Int32
Private Sub PlayScroll(ByVal number As Int32, Optional ByVal increment As Int32 = 2) On Error Resume Next For i As Int32 = 1 To number apimouse_event(MOUSEEVENTF_WHEEL, 0, 0, increment, apiGetMessageExtraInfo) Next End Sub
C# Sample Code:
[DllImport("user32.dll")] static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
This code assumes a form called frmMain with a command button called cmdClick a picture box called picClicker and a text box called txtResults
Note Twips are no more. Also, I stripped the FOR loop of delta moves from the command button click to the middle of the picture box.
Option Explicit On
Friend Class frmMain
Inherits System.Windows.Forms.Form
Declare Auto Sub mouse_event Lib "user32" (ByVal dwFlags As Int32, ByVal dx As Int32, ByVal dy As Int32, ByVal cButtons As Int32, ByVal dwExtraInfo As IntPtr)
Const MOUSEEVENTF_MOVE As Int32 = &H1 ' mouse move Const MOUSEEVENTF_LEFTDOWN As Int32 = &H2 ' left button down Const MOUSEEVENTF_LEFTUP As Int32 = &H4 ' left button up Const MOUSEEVENTF_RIGHTDOWN As Int32 = &H8 ' right button down Const MOUSEEVENTF_RIGHTUP As Int32 = &H10 ' right button up Const MOUSEEVENTF_MIDDLEDOWN As Int32 = &H20 ' middle button down Const MOUSEEVENTF_MIDDLEUP As Int32 = &H40 ' middle button up Const MOUSEEVENTF_ABSOLUTE As Int32 = &H8000 ' absolute move Const MOUSEEVENTF_WHEEL As Int32 = &H800 ' wheel button rolled
' Simulate moving the mouse to the center of the ' PictureBox and clicking. Private Sub cmdClick_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles cmdClick.Click Dim cur_x As Single Dim cur_y As Single Dim dest_x As Single Dim dest_y As Single
' mouse_event moves in a coordinate system where ' (0, 0) is in the upper left corner and ' (65535,65535) is in the lower right corner.
' Get the current mouse coordinates and convert ' them into this new system. cur_x = System.Windows.Forms.Cursor.Position.X * 65535 / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width cur_y = System.Windows.Forms.Cursor.Position.Y * 65535 / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height
' Convert the coordinates of the center of the ' picClicker PictureBox into this new system. Dim pt As Point = picClicker.PointToScreen(New Point(picClicker.ClientRectangle.Width / 2, picClicker.ClientRectangle.Height / 2))
' Move the mouse to its final destination and click it. mouse_event(MOUSEEVENTF_ABSOLUTE + MOUSEEVENTF_MOVE + MOUSEEVENTF_LEFTDOWN + MOUSEEVENTF_LEFTUP, dest_x, dest_y, 0, 0) End Sub
Private Sub picClicker_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles picClicker.Click txtResults.Text = txtResults.Text & "MouseClick" & vbCrLf End Sub
Private Sub picClicker_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles picClicker.MouseDown txtResults.Text = txtResults.Text & "MouseDown (" & e.X & ", " & e.Y & ")" & vbCrLf End Sub
Private Sub picClicker_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles picClicker.MouseUp txtResults.Text = txtResults.Text & "MouseUp (" & e.X & ", " & e.Y & ")" & vbCrLf End Sub
Dim eMouse As New System.Windows.Forms.MouseEventArgs(Windows.Forms.MouseButtons.Left, 1, pt.X, pt.Y, 0)
Me.OnMouseDown(eMouse)
Me.OnMouseMove(eMouse)
Me.OnMouseUp(eMouse)
to work as desired. Although it did trigger mouse events in the form showing the movement and click action desired, it didn't move the mouse pointer on the screen nor did it trigger events in the picture box located at pt.X, pt.Y
Thus I have resigned myself to unmanaged code for now. I will post the SendInput version shortly as mouse_event has been deprecated by Bill in favor ofSendInput.
As the original contributor noted:
You can use the System.Windows.Forms.Cursor.Position property to set the position of the mouse, if you would like.
You will note I have replaced the GetCursorPos calls in the original VB source with this as suggested in the MSDN URL I mention above.