我已经开始学习winAPI,并尝试在c#中使用winAPI创建窗口,但我总是收到1407错误,“找不到窗口类”。我已经为 lpClassName 和 lpWindowName 编写了 [MarshalAs(UnmanagedType.LPWStr)] 但没有帮助
但是 Window 类注册返回 49856 代码。
这是我的代码:
[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);
可能是什么问题?
由于
WNDCLASSEX.lpszMenuName
和 WNDCLASSEX.lpszClassName
是结构体的成员,因此结构体中字符串的编组规则适用于此处。由于您没有为结构指定任何此类规则,因此应用默认规则。来自文档https://learn.microsoft.com/en-us/dotnet/standard/native-interop/customize-struct-marshalling#customizing-string-field-marshalling:
默认情况下,.NET 将字符串编组为指向以 null 结尾的字符串的指针。编码取决于 System.Runtime.InteropServices.StructLayoutAttribute 中的 StructLayoutAttribute.CharSet 字段的值。 如果未指定属性,则编码默认为 ANSI 编码。
因此,基本上 WNDCLASSEX 结构中的字符串被编组为 ANSI 字符串。但是您调用
RegisterClassEx
函数的 Unicode 版本,该函数需要宽/Unicode 字符串。轰隆隆!
将 WNDCLASSEX [StructLayout] 属性的
CharSet
参数设置为 Unicode 以解决代码中的此特定错误。我不知道您的代码中是否还潜藏着其他问题,但是这个问题对我来说相对容易发现......