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

카테고리

분류 전체보기 (2803)
Unity3D (859)
Programming (479)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (234)
협업 (61)
3DS Max (3)
Game (12)
Utility (140)
Etc (98)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (55)
Android (16)
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

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

When you want to set a timer working with GUI, you always come across threading problem. In such scenario, .Net indeed makes programmers life easier. It only matters that you choose the right timer to use.


In Win Form, you need to use System.Windows.Forms.Timer.


In WPF, the one is System.Windows.Threading.DispatcherTimer.


Here is a simple sample code for DispatcherTimer.


{

DispatcherTimer timer = new DispatcherTimer();

timer.Interval = TimeSpan.FromMilliseconds(someInterval);

timer.Tick += new EventHandler(someEventHandler);

timer.Start();

}


{

private void someEventHandler(Object sender, EventArgs args)

{

some operations

//if you want this event handler executed for just once

// DispatcherTimer thisTimer = (DispatcherTimer)sender;

// thisTimer.Stop();

}


P.S.


For general purpose, you can use System.Threading.Timer.


For server-based purpose, System.Timers.Timer can be the right choice.



출처 : http://wangmo.wordpress.com/2007/09/07/dispatchertimer-in-wpf/

반응형
Posted by blueasa
, |

WPF가 C#이랑은 참조하는 게 약간 달라서 적어놔야겠다.


간단하게 리스트로 계속 추가해 나가야겠음.


- DllImport

using System.Runtime.InteropServices;


- ArrayList

using System.Collections;


- DispatcherTimer

using System.Windows.Threading;

반응형
Posted by blueasa
, |

미끄러짐 벡터는 충돌시 입사벡터가 입사면을 따라 미끄러지게 하기위해 수평성분만을 남긴 벡터이다.

왼쪽 그림에서, 벡터 P 가 법선벡터 n 을 가진 입사면에 충돌했을때의 슬라이딩벡터S 를 볼수 있다.

슬라이딩벡터 S 는 여러가지 방법으로 구할 수 있지만, 여기서는 이전에 설명했던
반사벡터의 과정을 이용하는 방법과, 일반적인 방법을 설명한다.
물론, 그 결과는 완전히 같다.



이전에 설명했던 반사벡터에서, 입사벡터 P  n(-P·n) 을 한번 더해주면, 입사면에 투영된 접선벡터(Tangent Vector)를 구할 수 있다고 했다.

Fig. 2 를 보면, 입사벡터의 역벡터 -P 가 n 에 투영된 n(-P·n) 을 이용하여 슬라이딩벡터 S 를 구하고 있다.

그러므로 슬라이딩벡터 S 

S = P + n(-P·n)



일반적인 방법은, 입사벡터 P  n 에 바로 투영시키는 것이다.
입사벡터 P 와 법선벡터 n 의 끼인각이 0≤ θ ≤ π/2 일때, P·n 의 값은 음수가 되므로, n 벡터의 역벡터 방향으로 투영벡터가 생성된다.

이렇게 얻어진 투영벡터 n(P·n) 을 입사벡터 P 에서 빼주면,
슬라이딩벡터 S 를 얻을수 있다.

그러므로 슬라이딩벡터 S 

S = P - n(P·n)

반응형
Posted by blueasa
, |

정반사는 입사벡터와 반사벡터의 크기가 같고, 입사각과 반사각의 크기가 같은것을 말한다.

Fig.1 을 보면 입사벡터 P 와 법선벡터 n 이 주어졌을때,
반사벡터 R 은 벡터 P 와 크기가 같고, 입사각과 반사각이
같음을 확인할 수 있다.
여기서는 P  n 만으로 반사벡터 을 구하는 방법을 알아보자.



 

 
우선, 입사 벡터 P 의 역벡터 -P  n 의 연장선상에 투영시켜
투영벡터 n(-P·n) 를 구한다.





입사 벡터 P 의 시작 위치를 원점에 위치시키고, 여기에 n(-P·n) 를 더하면, 입사면에 투영된 벡터의 위치를 구할수 있다.

Fig. 3 을 보면, 입사벡터 P  n(-P·n) 를 1번 더하면, 입사면에 투영된
위치를 구할 수 있고, 2번 더하면  반사벡터 R 을 구할 수 있음을
알수 있다.

결국, 반사벡터 R 
R = P  2n(-P·n)

반응형
Posted by blueasa
, |

좌측 그림을 보면 단위벡터 n에 대해 벡터P로부터의 투영벡터(Projection Vector) Pproj 를 구하고 있다.

이와같은 Pproj 벡터는 어떻게 구해지는가를 살펴 보기로 한다.






P
 n과의 내적은 ||P||||n||cosθ 이다.
여기서 n이 단위 벡터이기 때문에 ||n|| = 1 이므로,

P·n = ||P||cosθ

이는 P n에 투영했을때의 길이를 말하므로, 여기서 구하고자 하는 Pproj 는 단위벡터 n P·n 만큼 연장한 값이다.

그러므로 투영벡터는

Pproj = n(P·n)

가 된다.

반응형

'Programming > Math' 카테고리의 다른 글

미끄러짐 벡터 ( Sliding Vector )  (0) 2012.05.16
반사 벡터 ( Reflection Vector )  (0) 2012.05.16
동차좌표 ( 同次座標, Homogeneous coordinate )  (0) 2012.05.16
[펌] 3D공간구조 기본충돌  (0) 2011.09.08
좌표계  (0) 2010.07.01
Posted by blueasa
, |

동차좌표란?

동차좌표를 사전에서 찾아보면, '사영기하학에서 무한원점의 불편한점을 고려해서 보통의 점과 같이 취급하기 위함' 이라 적어뒀다. 외계어인가 -_-;

일단 동차란 같은 차원이란 뜻이다. 비동차(inhomogeneous, 非同次) 좌표계 에서 무한대는 ∞ 라는 기호를 이용해서 표시하는데, 다른 원소들과 달리 일반적인 수가 아니다. 이를 일반적인 수로 표현하기 위해 차수를 높여 좌표의 표현력을 늘리기 위해 등장한 개념이다.

다시 말해 현재 차원의 점을 더 높은 차원의 공간상의 직선에 사상(mapping) 시킨것이 동차 좌표이다. 2차원의 점 (1, 2)는 원점에서 출발해 (1, 2, 1)를 통과하는 모든 3차원 공간상의 좌표와 같으며, 이것이 동차좌표이다.

다음 표를 보자 

 inhomogeneous coordinate homogeneous coordinate 
 0 (0, 1) 
 -7/3 (7, -3)
 ∞ (1, 0)
 not a point (0, 0)
 (5, -7/3) (5, -7/3, 1)
 (∞, 0) (1, 0, 0)
 (0, ∞) (0, 1,0)
 (2, 3/4) (8, 3, 4)
 (-3∞, 2∞) (-3, 2, 0)
위와 같이 일반적인 수로 표현이 안되던 것이 동차좌표에서는 표현 가능하게 되었다.

좌표계에서 무한한 값인 방향을 뜻하는 벡터(vector)와 점(vertex)을 동차좌표를 이용해 구분 표시할수 있다. 마지막 요소가 0이면 벡터를, 0이 아닌 다른 값이면 점을 의미한다.


반응형

'Programming > Math' 카테고리의 다른 글

반사 벡터 ( Reflection Vector )  (0) 2012.05.16
투영 벡터 ( Projection Vector )  (0) 2012.05.16
[펌] 3D공간구조 기본충돌  (0) 2011.09.08
좌표계  (0) 2010.07.01
역행렬(Inverse Matrix)  (0) 2010.03.17
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
, |