블로그 이미지
Every unexpected event is a path to learning for you. blueasa

카테고리

분류 전체보기 (2797)
Unity3D (853)
Programming (479)
Python (8)
TinyXML (5)
STL (13)
D3D (3)
MFC (1)
C/C++ (54)
C++/CLI (45)
C# (251)
WinForm (6)
WPF (5)
Math (10)
A.I. (1)
Win32API (11)
Algorithm (3)
Design Pattern (7)
UML (1)
MaxScript (1)
FMOD (4)
FX Studio (1)
Lua (2)
Terrain (1)
Shader (3)
boost (2)
Xml (2)
JSON (4)
Etc (11)
Monad (1)
Html5 (4)
Qt (1)
Houdini (0)
Regex (14)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (185)
협업 (61)
3DS Max (3)
Game (12)
Utility (68)
Etc (98)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (55)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday

Invoke, MethodInvoker, BeginInvoke - EndInvoke

 Introduction

  UI Control들은 폼 구동시 실행되는 하나의 쓰레드에서 구동된다. 따라서 사용자가 실행시킨 쓰레드는 별도로 실행 되기 때문에 이 메인 쓰레드에 적절한 마샬링 없이 다른쓰레드에서 직접 접근하면 다른 쓰레드를 침범하는 것이다. (Cross Thread Problem) 이런 경우에는 프로그램이 개발자가 설계한대로 잘 동작하지 않을 수 있다.(Race Condition,DeadLock)  따라서, 안전하게 동작하게 하기위하여 .Net 환경에서는 Invoke를 제공하고 있다.

 본 내용을 무시한 채 프로그램을 작성하면 InvalidOperationException을 발생시키고 . Debug 창에서 "컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다."라는 메세지가 표시 된다. 하지만 디버깅 환경이 아니라면 프로그램은 겉보기에 정상 동작하는 것 처럼 보일 수 있으므로 조심해야 된다. !

 

난 대충 좀 안되도 상관 없다고 생각하시는 분들은 이 예외를 비활성화 할 수도 있습니다.

 

CheckForIllegalCrossThreadCalls 속성 값을 false로 설정하여 이 예외를 비활성화할 수 있습니다. 그러면 컨트롤이 Visual Studio 2003에서와 같은 방식으로 실행됩니다.

 

이 노트에서 학습하실 때 유념하셔야 될 내용은

  1. Control Invoke 와 Delegate Invoke의 차이점을 숙지한다.
  2. Invoke와 BeginInvoke의 차이점과 사용법을 숙지한다.
  3.  

 자 그럼 쓰레드에서 안전하게 Windows Form control을 제어하게 하는 방법에 대해 알아보자.

우선,  InvokerRequired 를 알고 시작하자.  이 속성은 Invoke메쏘드를 호출해야되는 상황인지 알려준다.

 

Invoke

 

 쓰레드에서 폼 컨트롤 하기 위해서는 별도의(SetTextononTextBox1) 메쏘드를 만들어 사용하면 좋다. 예를들어, Textbox에 값을 입력한다면 아래와 같은 'SetTextonTextBox1' 메쏘드를 만들어서 사용하면 Cross thread 환경이든 내부 쓰레드 사용환경에서든 둘다 사용할 수 있다. 또는 Delegate 메쏘드가 FormControl을 인자로 받아서 사용할 수도 있다.

 Invoke를 사용할 때 인자를 넘겨줘야 하는 메쏘드 필요하면 할때는 반드시 delegate를 선언한 후에 사용하여야 한다.

 

  1.         delegate void SetTextCallback(string txt);                    
            private void SetTextonTextBox1(string txt)
            {
                if (this.textBox1.InvokeRequired)
                {
                    this.Invoke(new SetTextCallback(SetTxtCB), new object[] { txt }); //그냥 txt를 넘겨줘도 된다
                }
                else
                {
                    this.textBox1.Text += txt;
                }
            }
            private void SetTxtCB(string txt)
            {
                this.textBox1.Text+=txt;
            }

 

  1.         public delegate void UpdateText(Control ctrl, string text);
            public void UpdateTextFunc(Control ctrl, string text)
            {
                if (ctrl.InvokeRequired)
                {
                    ctrl.Invoke(new UpdateText(UpdateTextFunc), new object[] { ctrl, text });
                }
                else
                    ctrl.Text += text;
            }

 

인자가 없는 메쏘드를 호출할 때는 간단하게 MethodInvoker를 사용하면 좋다.

 

  1.         private void SetTextonTextBox1(string txt)
            {
                if (this.textBox1.InvokeRequired)
                {
  2.                this.Invoke(new MethodInvoker( delegate { this.textBox1.Text+=txt; }) );

                }
                else
                {
                    this.textBox1.Text += txt;
                }
            }

 

BeginInvoke , EndInvoke

 

 우선 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를 호출해줘야 한다 안그러면 메모리 릭이 발생한다.

 

 

  1. public delegate void InvokeDelegate();
  2. private void Invoke_Click(object sender, EventArgs e)
    {
       myTextBox.BeginInvoke(new InvokeDelegate(InvokeMethod));
    }
    public void InvokeMethod()
    {
       myTextBox.Text = "Executed the given delegate";
    }
  3.  

 

 Endinvoke

 Reference
  1. http://msdn.microsoft.com/ko-kr/library/ms171728.aspx
  2. http://xmlangel.textcube.com/6
  3. http://jongkok4.net/entry/펌c-UI-쓰레드-마샬링-Invoke-BeginInvoke?TSSESSIONjongkok4net=6e5ec00b34c31e0126e9a64412f7a627
  4. http://timl.net/2008/01/begininvoke-methodinvoker-and-anonymous.html
  5. http://shiman.wordpress.com/2008/09/10/c-net-delegates-asynchronous-invocation-begininvoke-method/
  6. http://msdn.microsoft.com/ko-kr/library/0b1bf3y3(VS.80).aspx
  7. http://kristofverbiest.blogspot.com/2007/02/don-confuse-controlbegininvoke-with.html
  8. http://www.albahari.com/threading/#_Introduction
  9. http://www.yoda.arachsys.com/csharp/threadstart.html 




반응형
Posted by blueasa
, |

GetWindowRect함수는 화면상의 특정 Window에 대한 위치및 크기를 반환하는 함수입니다.

Declare Function GetWindowRect Lib "user32" Alias "GetWindowRect" (ByVal hwnd As Integer, ByRef lpRect As RECT) As Integer
▶VB.NET 선언

[DllImport("user32")]
public static extern int GetWindowRect(int hwnd, ref RECT lpRect);
▶C# 선언

선언 GetWindowRect함수의 첫번째 인수는 위치및 크기를 얻고자 하는 Window의 Handle을 지정하고 두번째 인수에 실제 위치와 크기에 대한 값이 저장될 구조체를 지정합니다.

여기서 사용되는 구조체는 다음과 같이 선언됩니다.

Public Structure RECT
Public left As Integer
Public top As Integer
Public right As Integer
Public bottom As Integer
End Structure
▶VB.NET

public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
▶C#

만일 실행중인 현재 Form에 대한 위치및 크기값을 가져오려면 GetWindowRect함수를 다음과 같이 선언합니다.

Dim stRect As RECT
GetWindowRect(Me.Handle, stRect)

▶VB.NET 호출

RECT stRect = default(RECT);
GetWindowRect((int)this.Handle, ref stRect);

▶C# 호출



출처 :  http://lab.cliel.com/m/post/view/id/39

반응형
Posted by blueasa
, |

IT, C#, FolderBrowserDialog, C# FolderBrowserDialog, C# 디렉토리, C# OpenFileDialog, OpenFileDialog




How to C# selecting a Directory in an open file dialog
http://ssscripting.wordpress.com/2009/06/07/c-selecting-a-directory-in-an-open-file-dialog/

FolderBrowserDialog dialog = new FolderBrowserDialog();
dialog.SelectedPath  = @"C:\";    // 열 때 폴더를 지정하려면 이런식으로..(추가)

dialog.ShowDialog();
string selected = dialog.SelectedPath;

이렇게 코딩하면 폴더 찾아보기를 구현할 수 있고 선택한 정보는 dialog.SelectedPath 로 넘어오게 되어 폴더 경로 정보를 처리할 수 있습니다.


출처 : http://ndolson.com/1370



[추가] 

폴더 다이얼로그 열릴 때, 시작되는 폴더 위치를 알고싶어서 찾다가

ShowDialog 하기 전에 SelectedPath를 셋팅해주면 된다는 걸 알고 글에 추가해놓음..

근데..

화면 이상 내려가면 스크롤을 내려야 보이는데..

포커스 있는 지점으로 스크롤 이동은 안될려나..

좀 더 알아봐야 될 듯..

반응형
Posted by blueasa
, |

[DllImport("user32.dll")]

static extern IntPtr GetDC(IntPtr hWnd);


[DllImport("user32.dll")]

static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);


[DllImport("gdi32.dll")]

static extern int GetPixel(IntPtr hDC, int x, int y);


//GetPixel()함수 정의, Color객체 반환

static public Color GetPixel(Control control, int x, int y)

{

Color color = Color.Empty;

if (control != null)

{

IntPtr hDC = GetDC(control.Handle);

int colorRef = GetPixel(hDC, x, y);

color = Color.FromArgb(

  (int)(colorRef & 0x000000FF),

  (int)(colorRef & 0x0000FF00) >> 8,

  (int)(colorRef & 0x00FF0000) >> 16);

ReleaseDC(control.Handle, hDC);

}

return color;

}




반응형
Posted by blueasa
, |

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;


namespace
Color_tool
{
public partial class Form1 : Form
{
   
Regex rgbInputR;
   
Regex rgbInputG;
   
Regex rgbInputB;

   
Match R, G, B;
   
int r;
   
int g;
   
int b;


   
string colorX;

   
[DllImport("gdi32")]
   
private static extern int GetPixel(IntPtr hdc, int x, int y);
   
[DllImport("User32")]
   
private static extern IntPtr GetWindowDC(IntPtr hwnd);

   
private static readonly IntPtr DesktopDC = GetWindowDC(IntPtr.Zero);

   
public static System.Drawing.Color GetPixelAtCursor()
   
{
       
System.Drawing.Point p = Cursor.Position;
       
return System.Drawing.Color.FromArgb(GetPixel(DesktopDC, p.X, p.Y));
   
}

   
public Form1()
   
{
       
InitializeComponent();
   
}

   
private void Form1_Load(object sender, EventArgs e)
   
{
        button1
.BackColor = Color.Black;
   
}

   
private void timer1_Tick(object sender, EventArgs e)
   
{
        colorX
= GetPixelAtCursor().ToString();
       
Color backX = GetPixelAtCursor();
       
this.BackColor = Color.FromArgb(r,g,b);
        label1
.Text = colorX;
        RGB_value
();
   
}

   
private void button1_Click(object sender, EventArgs e)
   
{
       
if (timer1.Enabled == false)
            timer1
.Enabled = true;
       
else
            timer1
.Enabled = false;
   
}

   
private void RGB_value()
   
{
        rgbInputR
= new Regex(@"(?<=R=)\d{0,3}");
        rgbInputG
= new Regex(@"(?<=G=)\d{0,3}");
        rgbInputB
= new Regex(@"(?<=B=)\d{0,3}");

       
Match R, G, B;

        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);
   
}
 
}
}





반응형
Posted by blueasa
, |

mouse_event (user32)

Programming/C# / 2012. 5. 21. 17:12

Summary
The mouse_event API

C# Signature:

[DllImport("user32.dll")]
static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData,
   UIntPtr dwExtraInfo);

or

[DllImport("user32.dll")]
static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData,
  int dwExtraInfo);

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.

User-Defined Types:

  [Flags]
  public enum MouseEventFlags : uint
  {
    LEFTDOWN   = 0x00000002,
    LEFTUP     = 0x00000004,
    MIDDLEDOWN = 0x00000020,
    MIDDLEUP   = 0x00000040,
    MOVE       = 0x00000001,
    ABSOLUTE   = 0x00008000,
    RIGHTDOWN  = 0x00000008,
    RIGHTUP    = 0x00000010,
    WHEEL      = 0x00000800,
    XDOWN      = 0x00000080,
    XUP    = 0x00000100
  }

  //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

dim ScrollValue as Integer

ScrollValue = 120 'or -120 for up or down scrolling

mouse_event(&H800, 0, 0, ScrollValue, 0)

Tips & Tricks:

Another contributor tells us:

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);

    [Flags]
    public enum MouseEventFlags
    {
        LEFTDOWN = 0x00000002,
        LEFTUP = 0x00000004,
        MIDDLEDOWN = 0x00000020,
        MIDDLEUP = 0x00000040,
        MOVE = 0x00000001,
        ABSOLUTE = 0x00008000,
        RIGHTDOWN = 0x00000008,
        RIGHTUP = 0x00000010
    }

    public static void LeftClick(int x, int y)
    {
        Cursor.Position = new System.Drawing.Point(x, y);
        mouse_event((int)(MouseEventFlags.LEFTDOWN), 0, 0, 0, 0);
        mouse_event((int)(MouseEventFlags.LEFTUP), 0, 0, 0, 0);
    }

VB.NET 2005 Sample Code:

Based on http://vb-helper.com/howto_move_click_mouse.html

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))

    dest_x = pt.X * 65535 / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width
    dest_y = pt.Y * 65535 / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height

    txtResults.Text = txtResults.Text & "From " & System.Windows.Forms.Cursor.Position.X & " " & System.Windows.Forms.Cursor.Position.Y & " to " & pt.X & " " & pt.Y & vbCrLf
    txtResults.Text = txtResults.Text & "From " & cur_x & " " & cur_y & " to " & dest_x & " " & dest_y & vbCrLf

    ' 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

End Class

Alternative Managed API:

Per http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/win32map.asp , mouse_event is supposed to be replaced by .NET Framework API System.Windows.Forms.MouseEventArgs but this is not the case. MouseEventArgs is just a data structure w/out event capability. I tried to get:

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.

Documentation
mouse_event on MSDN




반응형
Posted by blueasa
, |

keybd_event (user32)

Programming/C# / 2012. 5. 21. 11:48

KEYEVENTF_KEYUP 상수 찾다가 발견..


도움 될 것 같아서 퍼옴..



Summary
This function is useful to simulate Key presses to the window with focus.

It will return a value of false, if there is an error simulating the key press.

C# Signature:

[DllImport("user32.dll")]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags,
   UIntPtr dwExtraInfo);

or

[DllImport("user32.dll")]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags,
   int dwExtraInfo);

VB Signature:

<DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, _
           CharSet:=CharSet.Unicode, EntryPoint:="keybd_event", _
           ExactSpelling:=True, SetLastError:=True)> _
Public Shared Function keybd_event(ByVal bVk As Byte, ByVal bScan As Byte, _
                              ByVal dwFlags As Int32, ByVal dwExtraInfo As Int32) As Boolean
End Function

User-Defined Types:

None

Notes:

None.

Tips & Tricks:

This function is useful to simulate Key presses (for input use the virtual keycodes from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp or windows CE universal core virtual key code compact charthttp://msdn2.microsoft.com/en-us/library/ms927178.aspx ).

Use FindWindow and SetForegroundWindow to direct input to the desired window.

Note
WaitForInputIdle (Warning this will only wait once! See Raymond Chen's Blog Post http://blogs.msdn.com/b/oldnewthing/archive/2010/03/25/9984720.aspx ), or a Sleep may be required to assure Window is ready for input:

  RUN('NOTEPAD.EXE')
  Sleep(2000,0)
  SetForegroundWindow (FindWindow('Untitled - Notepad'))

(see also VkKeyScan):

    void PressKey( byte keyCode )
    {
        const int KEYEVENTF_EXTENDEDKEY = 0x1;
        const int KEYEVENTF_KEYUP       = 0x2;
        keybd_event( keyCode, 0x45, KEYEVENTF_EXTENDEDKEY, 0 );
        keybd_event( keyCode, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0 );
    }

Sample Code:

VB Sample:

This sample will generate a 'Scroll Lock' key press event when the user clicks 'Button1'.

  • Create a new Windows Application project;
  • Drop a button control in the recently created form;
  • Add the following code to the form class:

Imports System.Runtime.InteropServices

Public Class Form1

    <DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, _
           CharSet:=CharSet.Unicode, EntryPoint:="keybd_event", _
           ExactSpelling:=True, SetLastError:=True)> _
    Public Shared Sub keybd_event(ByVal bVk As Byte, ByVal bScan As Byte, _
                                  ByVal dwFlags As Integer, ByVal dwExtraInfo As Integer) As Boolean
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Const VK_SCROLL As Byte = &H91
        Const KEYEVENTF_KEYUP As Byte = &H2

        keybd_event(VK_SCROLL, 0, 0, 0)               ' Generates a KEY_DOWN
        keybd_event(VK_SCROLL, 0, KEYEVENTF_KEYUP, 0) ' Generates a KEY_UP

    End Sub

End Class

Please add some more!

Alternative Managed API: System.Windows.Forms.SendKeys

Documentation
keybd_event on MSDN

PROBLEM: How do i use combination of shift and tab keys at the same time ?

Answer:posted by dokks http://www.ravensmyst.com

define the shift key as a const

    public const byte VK_LSHIFT= 0xA0; // left shift key
    public const byte VK_TAB = 0x09;
    public const int KEYEVENTF_EXTENDEDKEY = 0x01;
    public const int KEYEVENTF_KEYUP = 0x02;

    //press the shift key
    keybd_event(VK_LSHIFT, 0x45, 0, 0);

    //press the tab key
    keybd_event(VK_TAB, 0x45, 0, 0);

    //release the tab key
    keybd_event(VK_TAB, 0x45, KEYEVENTF_KEYUP, 0);

    //release the shift key
    keybd_event(VK_LSHIFT, 0x45, KEYEVENTF_KEYUP, 0);

I'm using this to create automated UI testing for a custom Textbox control. After a lot of trial and error the following code worked well for me.

    public partial class Form2 : Form
    {
    [DllImport("user32.dll", SetLastError = true)]
    static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);

    Keys[] numberKeys = new Keys[10] { Keys.D0, Keys.D1, Keys.D2, Keys.D3, Keys.D4, Keys.D5, Keys.D6, Keys.D7, Keys.D8, Keys.D9 };

    void PressKey(Keys key)
    {
        const int KEYEVENTF_EXTENDEDKEY = 0x1;
        const int KEYEVENTF_KEYUP = 0x2;
        // I had some Compile errors until I Casted the final 0 to UIntPtr like this...
        keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY, (UIntPtr)0);
        keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, (UIntPtr)0);
    }

    void PressKeyArray(Keys[] keys)
    {
        foreach (Keys key in keys)
        {
        PressKey(key);
        }
    }

    private void Compare(object sender, string expected, string actual)
    {
        Button ClickedButton = (Button)sender;

        if (expected == actual)
        ClickedButton.Text = "Pass";
        else
        ClickedButton.Text = "Fail";
    }

    private void buttonNumericAccept_Click(object sender, EventArgs e)
    {
        string expected = "0123456789";

        NumericTextbox.AcceptNumeric = true;

        //
        // Send Appropriate Key Presses
        //
        NumericTextbox.Focus();
        PressKeyArray(numberKeys);
        Application.DoEvents();

        // Process Results        
        Compare(sender, expected, NumericTextbox.Text);        
    }
    }



출처 : http://www.pinvoke.net/default.aspx/user32.keybd_event

반응형
Posted by blueasa
, |

C# 코드

[DllImport("StoreUI_ko_Http.dll", CharSet = CharSet.Auto)]
public static extern void GetPage(string url, out string data);

 

C++ 코드

__declspec(dllexport) void GetPage(BSTR url, BSTR* Result)
 {
         char* pBuffer = NULL;

         HttpConnectGet(url, &pBuffer);
 
         CComBSTR bstrResult = UTF8ToUNICODE(pBuffer);
        *Result = bstrResult.Detach();

        free(pBuffer);
  } 

 

WinXP 환경에서는 위의 코드가 정상적으로 동작하지만

 

Vista에서는 타입변환 오류가 발생되고 만다.

 

스트링 대신에 바이트 타입으로 하면 에러는 발생되지 않지만

 

한글의 경우 유니코드 환경에서 데이타가 다 깨져버린다.

 

해결 방법은 아래처럼 string을 IntPtr로 변경해서 처리하면 된다.

 

IntPtr 는 C#처럼 포인터를 지원하지 않는 어플리케이션과 데이터를 주고 받을때

 

사용하면 편리하다.

 

IntPtr는 C++에서 넘기는 스트링에 대한 포인터 주소값을 저장하게 된다.

 

C# 코드

[DllImport("StoreUI_ko_Http.dll", CharSet = CharSet.Auto)]
 public static extern void GetPage(string url, out IntPtr data);

 

IntPtr param = IntPtr.Zero;  // 초기값 할당 

GetPage("", param);             // C++ 함수 호출

string msg = System.Runtime.InteropServices.Marshal.PtrToStringAuto(param); //string 변환

 

C++ 코드

__declspec(dllexport) void GetPage(BSTR url, BSTR* Result)
 {
         char* pBuffer = NULL;

         HttpConnectGet(url, &pBuffer);
 
         CComBSTR bstrResult = UTF8ToUNICODE(pBuffer);
        *Result = bstrResult.Detach();

        free(pBuffer);
  } 




[출처] C#과 C++ DLL 간의 스트링 데이타 교환|작성자 옆집엉아

반응형

'Programming > C#' 카테고리의 다른 글

mouse_event (user32)  (0) 2012.05.21
keybd_event (user32)  (0) 2012.05.21
C# String을 C++ char로 변환  (0) 2012.05.14
Returning Strings from a C++ API to C#  (0) 2012.05.14
SendMessage C# -> C++ with String  (0) 2012.05.09
Posted by blueasa
, |



1
2
3
4
5
6
7
8
9
10
using System.Runtime.InteropServices;
 
String strHello = "Hello";
// IntPtr나 System::String^로 넘겨 주면 됨
IntPtr pStr = Marshal.StringToHGlobalUni(strHello );
 
// pStr  사용
 
// 사용 후 메모리 해제
Marshal.FreeHGlobal(pStr);
단순한 코드라 설명을 생략



반응형
Posted by blueasa
, |

1. Introduction.

1.1 APIs that return strings are very common. However, the internal nature of such APIs, as well as the use of such APIs in managed code, require special attention. This blog will demonstrate both concerns.

1.2 I will present several techniques for returning an unmanaged string to managed code. But before that I shall first provide an in-depth explanation on the low-level activities that goes on behind the scenes. This will pave the way towards easier understanding of the codes presented later in this blog.

2. Behind the Scenes.

2.1 Let’s say we want to declare and use an API written in C++ with the following signature :

char* __stdcall StringReturnAPI01();

This API is to simply return a NULL-terminated character array (a C string).

2.2 To start with, note that a C string has no direct representation in managed code. Hence we simply cannot return a C string and expect the CLR to be able to transform it into a managed string.

2.3 The managed string is non-blittable. It can have several representations in unmanaged code : e.g. C-style strings (ANSI and Unicode-based) and BSTRs. Hence, it is important that you specify this information in the declaration of the unmanaged API, e.g. :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string StringReturnAPI01();

In the above declaration, note that the following line :

[return: MarshalAs(UnmanagedType.LPStr)]

indicates that the return value from the API is to be treated as a NULL-terminated ANSI character array (i.e. a typical C-style string).

2.4 Now this unmanaged C-style string return value will then be used by the CLR to create a managed string object. This is likely achieved by using the Marshal.PtrToStringAnsi() method with the incoming string pointer treated as an IntPtr.

2.5 Now a very important concept which is part and parcel of the whole API calling operation is memory ownership. This is an important concept because it determines who is responsible for the deallocation of this memory. Now the StringReturnAPI01() API supposedly returns a string. The string should thus be considered equivalent to an “out” parameter, It is owned by the receiver of the string, i.e. the C# client code. More precisely, it is the CLR’s Interop Marshaler that is the actual receiver.

2.6 Now being the owner of the returned string, the Interop Marshaler is at liberty to free the memory associated with the string. This is precisely what will happen. When the Interop Marshaler has used the returned string to construct a managed string object, the NULL-terminated ANSI character array pointed to by the returned character pointer will be deallocated.

2.7 Hence it is very important to note the general protocol : the unmanaged code will allocate the memory for the string and the managed side will deallocate it. This is the same basic requirement of “out” parameters.

2.8 Towards this protocol, there are 2 basic ways that memory for an unmanaged string can be allocated (in unmanaged code) and then automatically deallocated by the CLR (more specifically, the interop marshaler) :

  • CoTaskMemAlloc()/Marshal.FreeCoTaskMem().
  • SysAllocString/Marshal.FreeBSTR().

Hence if the unmanaged side used CoTaskMemAlloc() to allocate the string memory, the CLR will use the Marshal.FreeCoTaskMem() method to free this memory.

The SysAllocString/Marshal.FreeBSTR() pair will only be used if the return type is specified as being a BSTR. This is not relevant to the example given in point 2.1 above. I will demonstrate a use of this pair in section 5 later.

2.9 N.B. : Note that the unmanaged side must not use the “new” keyword or the “malloc()” C function to allocate memory. The Interop Marshaler will not be able to free the memory in these situations. This is because the “new” keyword is compiler dependent and the “malloc” function is C-library dependent. CoTaskMemAlloc(), and SysAllocString() on the other hand, are Windows APIs which are standard.

Another important note is that although GlobalAlloc() is also a standard Windows API and it has a counterpart managed freeing method (i.e. Marshal.FreeHGlobal()), the Interop Marshaler will only use the Marshal.FreeCoTaskMem() method for automatic memory freeing of NULL-terminated strings allocated in unmanaged code. Hence do not use GlobalAlloc() unless you intend to free the allocated memory by hand using Marshal.FreeHGlobal() (an example of this is give in section 6 below).

3. Sample Code.

3.1 In this section, based on the principles presented in section 2, I shall present sample codes to demonstrate how to return a string from an unmanaged API and how to declare such an API in managed code.

3.2 The following is a listing of the C++ function which uses CoTaskMemAlloc() :

extern "C" __declspec(dllexport) char*  __stdcall StringReturnAPI01()
{
    char szSampleString[] = "Hello World";
    ULONG ulSize = strlen(szSampleString) + sizeof(char);
    char* pszReturn = NULL;

    pszReturn = (char*)::CoTaskMemAlloc(ulSize);
    // Copy the contents of szSampleString
    // to the memory pointed to by pszReturn.
    strcpy(pszReturn, szSampleString);
    // Return pszReturn.
    return pszReturn;
}

3.4 The C# declaration and sample call :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string StringReturnAPI01();

static void CallUsingStringAsReturnValue()
{
  string strReturn01 = StringReturnAPI01();
  Console.WriteLine("Returned string : " + strReturn01);
}

3.5 Note the argument used for the MarshalAsAttribute : UnmanagedType.LPStr. This indicates to the Interop Marshaler that the return string from StringReturnAPI01() is a pointer to a NULL-terminated ANSI character array.

3.6 What happens under the covers is that the Interop Marshaler uses this pointer to construct a managed string. It likely uses the Marshal.PtrToStringAnsi() method to perform this. The Interop Marshaler will then use the Marshal.FreeCoTaskMem() method to free the character array.

4. Using a BSTR.

4.1 In this section, I shall demonstrate here how to allocate a BSTR in unmanaged code and return it in managed code together with memory deallocation.

4.2 Here is a sample C++ code listing :

extern "C" __declspec(dllexport) BSTR  __stdcall StringReturnAPI02()
{
  return ::SysAllocString((const OLECHAR*)L"Hello World");
}

4.3 And the C# declaration and usage :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string StringReturnAPI02();

static void CallUsingBSTRAsReturnValue()
{
  string strReturn = StringReturnAPI02();
  Console.WriteLine("Returned string : " + strReturn);
}

Note the argument used for the MarshalAsAttribute : UnmanagedType.BStr. This indicates to the Interop Marshaler that the return string from StringReturnAPI02() is a BSTR.

4.4 The Interop Marshaler then uses the returned BSTR to construct a managed string. It likely uses the Marshal.PtrToStringBSTR() method to perform this. The Interop Marshaler will then use the Marshal.FreeBSTR() method to free the BSTR.

5. Unicode Strings.

5.1 Unicode strings can be returned easily too as the following sample code will demonstrate.

5.2 Here is a sample C++ code listing :

extern "C" __declspec(dllexport) wchar_t*  __stdcall StringReturnAPI03()
{
  // Declare a sample wide character string.
  wchar_t  wszSampleString[] = L"Hello World";
  ULONG  ulSize = (wcslen(wszSampleString) * sizeof(wchar_t)) + sizeof(wchar_t);
  wchar_t* pwszReturn = NULL;

  pwszReturn = (wchar_t*)::CoTaskMemAlloc(ulSize);
  // Copy the contents of wszSampleString
  // to the memory pointed to by pwszReturn.
  wcscpy(pwszReturn, wszSampleString);
  // Return pwszReturn.
  return pwszReturn;
}

5.3 And the C# declaration and usage :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string StringReturnAPI03();

static void CallUsingWideStringAsReturnValue()
{
  string strReturn = StringReturnAPI03();
  Console.WriteLine("Returned string : " + strReturn);
}

The fact that a wide charactered string is now returned requires the use of the UnmanagedType.LPWStr argument for the MarshalAsAttribute.

5.4 The Interop Marshaler uses the returned wide-charactered string to construct a managed string. It likely uses the Marshal.PtrToStringUni() method to perform this. The Interop Marshaler will then use the Marshal.FreeCoTaskMem() method to free the wide-charactered string.

6. Low-Level Handling Sample 1.

6.1 In this section, I shall present some code that will hopefully cement the reader’s understanding of the low-level activities that had been explained in section 2 above.

6.2 Instead of using the Interop Marshaler to perform the marshaling and automatic memory deallocation, I shall demonstrate how this can be done by hand in managed code.

6.3 I shall use a new API which resembles the StringReturnAPI01() API which returns a NULL-terminated ANSI character array :

extern "C" __declspec(dllexport) char*  __stdcall PtrReturnAPI01()
{
  char   szSampleString[] = "Hello World";
  ULONG  ulSize = strlen(szSampleString) + sizeof(char);
  char*  pszReturn = NULL;

  pszReturn = (char*)::GlobalAlloc(GMEM_FIXED, ulSize);
  // Copy the contents of szSampleString
  // to the memory pointed to by pszReturn.
  strcpy(pszReturn, szSampleString);
  // Return pszReturn.
  return pszReturn;
}

6.4 And the C# declaration :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr PtrReturnAPI01();

Note that this time, I have indicated that the return value is an IntPtr. There is no [return : ...] declaration and so no unmarshaling will be performed by the Interop Marshaler.

6.5 And the C# low-level call :

static void CallUsingLowLevelStringManagement()
{
  // Receive the pointer to ANSI character array
  // from API.
  IntPtr pStr = PtrReturnAPI01();
  // Construct a string from the pointer.
  string str = Marshal.PtrToStringAnsi(pStr);
  // Free the memory pointed to by the pointer.
  Marshal.FreeHGlobal(pStr);
  pStr = IntPtr.Zero;
  // Display the string.
  Console.WriteLine("Returned string : " + str);
}

This code demonstrates an emulation of the Interop Marshaler in unmarshaling a NULL-terminated ANSI string. The returned pointer from PtrReturnAPI01() is used to construct a managed string. The pointer is then freed. The managed string remains intact with a copy of the returned string.

The only difference between this code and the actual one by the Interop Marshaler is that the GlobalAlloc()/Marshal.FreeHGlobal() pair is used. The Interop Marshaler always uses Marshal.FreeCoTaskMem() and expects the unmanaged code to use ::CoTaskMemAlloc().

7. Low-Level Handling Sample 2.

7.1 In this final section, I shall present one more low-level string handling technique similar to the one presented in section 6 above.

7.2 Again we do not use the Interop Marshaler to perform the marshaling and memory deallocation. Additionally, we will also not release the memory of the returned string.

7.3 I shall use a new API which simply returns a NULL-terminated Unicode character array which has been allocated in a global unmanaged memory :

wchar_t gwszSampleString[] = L"Global Hello World";

extern "C" __declspec(dllexport) wchar_t*  __stdcall PtrReturnAPI02()
{
  return gwszSampleString;
}

This API returns a pointer to the pre-allocated global Unicode string “gwszSampleString”. Because it is allocated in global memory and may be shared by various functions in the DLL, it is crucial that it is not deleted.

7.4 The C# declaration for PtrReturnAPI02() is listed below :

[DllImport("<path to DLL>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr PtrReturnAPI02();

Again, there is no declaration for interop marshaling (no use of the [return : ...] declaration). The returned IntPtr is returned as is.

7.5 And a sample C# code to manage the returned IntPtr :

static void CallUsingLowLevelStringManagement02()
{
  // Receive the pointer to Unicde character array
  // from API.
  IntPtr pStr = PtrReturnAPI02();
  // Construct a string from the pointer.
  string str = Marshal.PtrToStringUni(pStr);
  // Display the string.
  Console.WriteLine("Returned string : " + str);
}

Here, the returned IntPtr is used to construct a managed string from an unmanaged NULL-terminated Unicode string. The memory of the unmanaged Unicode string is then left alone and is not deleted.

Note that because a mere IntPtr is returned, there is no way to know whether the returned string is ANSI or Unicode. In fact, there is no way to know whether the IntPtr actually points to a NULL-terminated string at all. This knowledge has to be known in advance.

7.6 Furthermore, the returned IntPtr must not point to some temporary string location (e.g. one allocated on the stack). If this was so, the temporary string may be deleted once the API returns. The following is an example :

extern "C" __declspec(dllexport) char* __stdcall PtrReturnAPI03()
{
  char szSampleString[] = "Hello World";
  return szSampleString;
}

By the time this API returns, the string contained in “szSampleString” may be completely wiped out or be filled with random data. The random data may not contain any NULL character until many bytes later. A crash may ensue a C# call like the following :

IntPtr pStr = PtrReturnAPI03();
// Construct a string from the pointer.
string str = Marshal.PtrToStringAnsi(pStr);




반응형
Posted by blueasa
, |