I've started to learn the Windows API and I try to create a window in C#, but I always get error 1407, "Cannot find window class". I have written [MarshalAs(UnmanagedType.LPWStr)]
for lpClassName
and lpWindowName
but it didn't help.
But the window class registration returns 49856.
Here's my code:
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern ushort RegisterClassEx(ref WNDCLASSEX lpwcx);
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern IntPtr CreateWindowExW(
uint dwExStyle,
[MarshalAs(UnmanagedType.LPWStr)]
string lpClassName,
[MarshalAs(UnmanagedType.LPWStr)]
string lpWindowName,
uint dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetModuleHandle(string lpModuleName);
[StructLayout(LayoutKind.Sequential)]
public struct WNDCLASSEX
{
public uint cbSize;
public uint style;
public IntPtr lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
public string lpszMenuName;
public string lpszClassName;
public IntPtr hIconSm;
}
private static IntPtr WindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
if (msg == 0x0010)
{
Environment.Exit(0);
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
static void Main()
{
const string CLASS_NAME = "SampleWindowClass";
IntPtr hInstance = GetModuleHandle(null);
WNDCLASSEX wc = new WNDCLASSEX
{
cbSize = (uint)Marshal.SizeOf(typeof(WNDCLASSEX)),
style = 0,
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(new WndProcDelegate(WindowProc)),
hInstance = hInstance,
hbrBackground = (IntPtr)1,
lpszClassName = CLASS_NAME, // Имя класса
hIconSm = IntPtr.Zero,
hIcon = IntPtr.Zero,
hCursor = IntPtr.Zero,
cbClsExtra = 0,
cbWndExtra = 0,
lpszMenuName = null
};
ushort classAtom = RegisterClassEx(ref wc);
if (classAtom == 0)
{
int errorCode = Marshal.GetLastWin32Error();
Console.WriteLine("Class registration error: " + errorCode);
return;
}
IntPtr hwnd = CreateWindowExW(
0,
CLASS_NAME,
"Learn to Program Windows",
0x00040000,
100,
100,
500,
500,
IntPtr.Zero,
IntPtr.Zero,
hInstance,
IntPtr.Zero
);
if (hwnd == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
Console.WriteLine("Window creation error: " + errorCode);
return;
}
ShowWindow(hwnd, 1);
}
private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
What could be the problem?
WNDCLASSEX.lpszMenuName
and WNDCLASSEX.lpszClassName
are members of a struct, therefore marshaling rules for strings in a struct apply here. Since you didn't specify any such rule for the struct, the default rule applies. From the documentation:
Customize structure marshalling: Customizing string field marshalling
By default, .NET marshals a string as a pointer to a null-terminated string. The encoding depends on the value of the StructLayoutAttribute.CharSet field in the System.Runtime.InteropServices.StructLayoutAttribute. If no attribute is specified, the encoding defaults to an ANSI encoding.
So, basically the strings in your WNDCLASSEX
struct are marshaled as pointers to ANSI strings. But you call the Unicode version of the RegisterClassEx
API, which is using the WNDCLASSEXW
structure featuring LPCWSTR
pointers (pointers to wide/Unicode strings). Kaboom!
Set the CharSet
parameter of the WNDCLASSEX
[StructLayout]
attribute to Unicode
(or perhaps Auto
) to address this particular bug in your code. I don't know if there are further issues lurking in your code, but this one was relatively easy for me to spot.