none
WinAPI function called from .NET are using ANSI instead of UNICODE RRS feed

  • Question

  • using System; using System.Windows.Forms; using System.Runtime.InteropServices; namespace Work_with_WinForm_HWNDs_by_DLL { public partial class Form1 : Form { [DllImport("user32.dll")] public static extern IntPtr FindWindow( IntPtr lpClassName, IntPtr lpWindowName ); [DllImport("user32.dll")] public static extern IntPtr FindWindow( IntPtr lpClassName, String lpWindowName ); [DllImport("user32.dll")] public static extern Int32 GetClassName( IntPtr hWnd, IntPtr lpClassName, Int32 nMaxCount ); public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { //Will not work //////////////////////////////////////////////////////// Char[] cWindowName = this.Name.ToCharArray(); IntPtr ipWindowName = Marshal.AllocCoTaskMem(cWindowName.Length * sizeof(Char)); Marshal.Copy(cWindowName, 0, ipWindowName, cWindowName.Length); IntPtr ipWindow = FindWindow(IntPtr.Zero, ipWindowName); textBox1.Text = ipWindow.ToString(); Marshal.FreeCoTaskMem(ipWindowName); Marshal.FreeCoTaskMem(ipWindow); //////////////////////////////////////////////////////// //Will work //////////////////////////////////////////////////////// ipWindow = FindWindow(IntPtr.Zero, this.Name); textBox2.Text = ipWindow.ToString(); //////////////////////////////////////////////////////// Int32 iClassNameSize = 100; IntPtr ipClassName = Marshal.AllocCoTaskMem(iClassNameSize * sizeof(Char)); GetClassName(ipWindow, ipClassName, iClassNameSize); //Why i have to use PtrToStringAnsi instead of PtrToStringUni? How fix it? String sClassName = Marshal.PtrToStringAnsi(ipClassName); textBox3.Text = sClassName; } } }

    Monday, September 24, 2012 12:30 PM

Answers

  • Hello Ramirag,

    1. You should use the wide versions of the APIs, e.g. FindWindowW() and GetClassNameW().

    2. The string parameters need not be IntPtrs (and allocated via Marshal.AllocCoTaskMem()). You may use the string and the StringBuilder types.

    3. The important thing is to use the MarshalAsAttribute and to indicate the correct character set to use.

    4. The following is an alternate example of the code listing in the OP :

    [DllImport("user32.dll", EntryPoint = "FindWindowW")]
    public static extern IntPtr FindWindow(
        [MarshalAs(UnmanagedType.LPWStr)] string lpClassName,
        [MarshalAs(UnmanagedType.LPWStr)] string lpWindowName
    );
    
    [DllImport("user32.dll", EntryPoint = "GetClassNameW", CharSet=CharSet.Unicode)]
    public static extern Int32 GetClassName(
        IntPtr hWnd,
        StringBuilder lpClassName,
        Int32 nMaxCount
    );
    
    private void button2_Click(object sender, EventArgs e)
    {
        string strWindowName = this.Name;
        IntPtr ipWindow = FindWindow(null, strWindowName);
        textBox1.Text = ipWindow.ToString();
    
        StringBuilder strClassName = new StringBuilder(256);  // Create a StringBuilder of capacity 256 characters.
        GetClassName(ipWindow, strClassName, strClassName.Capacity);
        textBox3.Text = strClassName.ToString();
    }        
    

    4.1 Note that for the GetClassName() API, the 2nd parameter (i.e. lpClassName) is meant to receive the output string value. Hence the use of the StringBuilder type.

    4.2 The StringBuilder must first be initialized with the correct capacity (256 in our example above).

    4.3 Finally, the StringBuilder is able to handle both Ansi and Unicode characters. The way to indicate to it which character set to use is via the DllImport CharSet argument (set to CharSet.Unicode above).

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Monday, September 24, 2012 1:47 PM
  • Call the version you want.  API methods that take or return strings have 2 versions.  The ANSI version has "A" appended to the function name.  The Unicode version has "W" appended to the name.  If you specify the auto attribute, the compiler will call the correct version without an appendage.
    Monday, September 24, 2012 1:52 PM

All replies

  • Hello Ramirag,

    1. You should use the wide versions of the APIs, e.g. FindWindowW() and GetClassNameW().

    2. The string parameters need not be IntPtrs (and allocated via Marshal.AllocCoTaskMem()). You may use the string and the StringBuilder types.

    3. The important thing is to use the MarshalAsAttribute and to indicate the correct character set to use.

    4. The following is an alternate example of the code listing in the OP :

    [DllImport("user32.dll", EntryPoint = "FindWindowW")]
    public static extern IntPtr FindWindow(
        [MarshalAs(UnmanagedType.LPWStr)] string lpClassName,
        [MarshalAs(UnmanagedType.LPWStr)] string lpWindowName
    );
    
    [DllImport("user32.dll", EntryPoint = "GetClassNameW", CharSet=CharSet.Unicode)]
    public static extern Int32 GetClassName(
        IntPtr hWnd,
        StringBuilder lpClassName,
        Int32 nMaxCount
    );
    
    private void button2_Click(object sender, EventArgs e)
    {
        string strWindowName = this.Name;
        IntPtr ipWindow = FindWindow(null, strWindowName);
        textBox1.Text = ipWindow.ToString();
    
        StringBuilder strClassName = new StringBuilder(256);  // Create a StringBuilder of capacity 256 characters.
        GetClassName(ipWindow, strClassName, strClassName.Capacity);
        textBox3.Text = strClassName.ToString();
    }        
    

    4.1 Note that for the GetClassName() API, the 2nd parameter (i.e. lpClassName) is meant to receive the output string value. Hence the use of the StringBuilder type.

    4.2 The StringBuilder must first be initialized with the correct capacity (256 in our example above).

    4.3 Finally, the StringBuilder is able to handle both Ansi and Unicode characters. The way to indicate to it which character set to use is via the DllImport CharSet argument (set to CharSet.Unicode above).

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Monday, September 24, 2012 1:47 PM
  • Call the version you want.  API methods that take or return strings have 2 versions.  The ANSI version has "A" appended to the function name.  The Unicode version has "W" appended to the name.  If you specify the auto attribute, the compiler will call the correct version without an appendage.
    Monday, September 24, 2012 1:52 PM