我目前正在编写自定义游戏引擎(用于自学)并致力于窗口系统。我需要创建基本的OS窗口(以便以后可以将其链接为DirectX的设备或为OpenGL创建上下文等)。我找到了一个使用C#创建Win32窗口的示例,因此我将其用作原型(示例代码在我的计算机上正常工作),但是源代码像一个单独的程序一样构成,因此我需要将其作为一个类来实现。我可以将IntPtr转到创建的窗口。
我编写了自己的Win32Window类,实现了所有必需的功能。然后,我使用另一个项目对其进行了测试(我正在将此类作为我的gameengine dll的一部分编写),但是它无法创建窗口。 另一个与该主题相关的问题(在Stackoverflow和其他论坛上)有不同的问题,这些问题来自使用我不需要的winApi功能(例如控件和其他内容),或者注册窗口类和创建窗口的顺序不正确。
正如标题所述,我发现CreateWindowEx返回null。
Marshal.GetLastWin32Error说CreateWindowEx找不到注册的类。
我试图在ClassName位置使用变量字符串,所以我不会错印名称。
我尝试将带有IntPtr的CreateWindowEx重载版本用于注册类。
他们都不起作用。
WinApi的所有托管代码均来自Pinvoke.com和MSDN文档。
绝对不是由于没有将实际的hInstance指针提供给RegisterClassEx和CreateWindowEx而不是IntPtr.Zero引起的。
这是窗口类代码:
public sealed class Win32Window : NativeWindow // NativeWindow is my engine's window API containing some size, name and pointer properties
{
-- Some properties and fields routine --
//Constructor takes job of registering class
public Win32Window(string WindowName, WndProc CallBack)
{
this.WindowName = WindowName;
this.Callback = CallBack;
this.wc = WNDCLASSEX.Build();
this.wc.style = (int)(CS.HRedraw | CS.VRedraw);
this.wc.lpfnWndProc = this.Callback;
this.wc.hInstance = IntPtr.Zero;
this.wc.hCursor = LoadCursor(IntPtr.Zero, (int)IDC.Arrow);
this.wc.hbrBackground = IntPtr.Zero;
this.wc.lpszClassName = ClassName;
this.wc.cbClsExtra = 0;
this.wc.cbWndExtra = 0;
this.wc.hIcon = LoadIcon(IntPtr.Zero,(IntPtr)IDI_APPLICATION);
this.wc.lpszMenuName = null;
this.wc.hIconSm = IntPtr.Zero;
ClassPtr = (IntPtr)RegisterClassEx(ref this.wc);
Console.WriteLine(ClassPtr); //Outputs negative integer, so i can conclude this part works properly
}
public void Create()
{
this.WindowHandle = CreateWindowEx(0,
ClassName,
this.WindowName,
(uint)WS.OverlappedWindow,
this.PosX,
this.PosY,
this.Width,
this.Height,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero);
Console.WriteLine($"{WindowHandle == IntPtr.Zero} {Marshal.GetLastWin32Error()}"); //Outputs "True 1407"
}
public void Show()
{
ShowWindow(this.WindowHandle, 1);
}
public void Iterate()
{
while (GetMessage(out msg, IntPtr.Zero, 0, 0) > 0)
{
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
--- Some [DllImport] routine ---
}
这是TestWindow类代码:
public class TestWindow
{
Win32Window.WndProc callback;
Win32Window Window;
private static IntPtr WndProc(IntPtr hWnd, Win32Window.WM message, IntPtr wParam, IntPtr lParam)
{
Console.WriteLine(message);
switch (message)
{
case Win32Window.WM.Destroy:
Win32Window.PostQuitMessage(0);
return IntPtr.Zero;
default:
return (Win32Window.DefWindowProc(hWnd, message, wParam, lParam));
}
}
public TestWindow()
{
callback = WndProc;
Window = new Win32Window("TestWindow", callback);
Window.Create();
Window.Show();
Window.Iterate();
}
}
测试控制台应用程序的主要方法只是创建一个TestWindow的新实例。
答案 0 :(得分:0)
我们有许多可执行文件链接到C ++,C#和C ++ / CLI中的许多不同的DLL(我们编写)。突然之间,我开始在我们的应用程序中收到错误1407,该错误仅在程序在Visual Studio 2015调试器中运行时发生。
在将调试器类型选项更改为“仅限本机”之后,我终于设法通过将“调试器类型”选项更改为“混合”来解决了该问题。
出于某种原因,改回“仅限本机”并不会导致错误1407错误再次出现。
希望这对任何与上述答案无关的问题有帮助的人
答案 1 :(得分:-1)
对于string
的{{1}}参数,您可以将C#中的LPCWSTR
映射到C ++中的lpClassName
。他们不平等。
-一种解决方案:
请参阅@ dan04的answer:
C#使用UTF-16字符串,因此您希望使用“ W”版本的 这些功能。使用PdhOpenQueryW。然后第一个参数具有C ++ 输入const wchar_t *。 C#类型为[MarshalAs(UnmanagedType.LPWStr)] 字符串。
尝试以下代码:
CreateWindowEx()
更新:
注意:感谢@IInspectable的提醒。编辑代码以使用Unicode API(例如RegisterClassExW和CreateWindowExW)来确保答案的一致性。
但是我不建议在新的Windows应用程序中使用Unicode API。相反,对于所有带有文本参数的函数,应用程序通常应使用通用函数原型并定义UNICODE,以将函数编译为Unicode函数。
参考:
Unicode in the Windows API, Conventions for Function Prototypes, Default Marshaling for Strings
-另一种解决方案:
CreateWindowEx()的参数 delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
class Win32Window
{
const UInt32 WS_OVERLAPPEDWINDOW = 0xcf0000;
const UInt32 WS_VISIBLE = 0x10000000;
const UInt32 CS_USEDEFAULT = 0x80000000;
const UInt32 CS_DBLCLKS = 8;
const UInt32 CS_VREDRAW = 1;
const UInt32 CS_HREDRAW = 2;
const UInt32 COLOR_WINDOW = 5;
const UInt32 COLOR_BACKGROUND = 1;
const UInt32 IDC_CROSS = 32515;
const UInt32 WM_DESTROY = 2;
const UInt32 WM_PAINT = 0x0f;
const UInt32 WM_LBUTTONUP = 0x0202;
const UInt32 WM_LBUTTONDBLCLK = 0x0203;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct WNDCLASSEX
{
[MarshalAs(UnmanagedType.U4)]
public int cbSize;
[MarshalAs(UnmanagedType.U4)]
public int style;
public IntPtr lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpszMenuName;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpszClassName;
public IntPtr hIconSm;
}
private WndProc delegWndProc = myWndProc;
[DllImport("user32.dll")]
static extern bool UpdateWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool DestroyWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true, EntryPoint = "CreateWindowExW")]
public static extern IntPtr CreateWindowExW(
int dwExStyle,
[MarshalAs(UnmanagedType.LPWStr)]
string lpClassName,
[MarshalAs(UnmanagedType.LPWStr)]
string lpWindowName,
UInt32 dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
[DllImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassExW")]
static extern System.UInt16 RegisterClassExW([In] ref WNDCLASSEX lpWndClass);
[DllImport("kernel32.dll")]
static extern uint GetLastError();
[DllImport("user32.dll")]
static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern void PostQuitMessage(int nExitCode);
[DllImport("user32.dll")]
static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
internal bool create()
{
WNDCLASSEX wind_class = new WNDCLASSEX();
wind_class.cbSize = Marshal.SizeOf(typeof(WNDCLASSEX));
wind_class.style = (int)(CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS);
wind_class.hbrBackground = (IntPtr)COLOR_BACKGROUND + 1;
wind_class.cbClsExtra = 0;
wind_class.cbWndExtra = 0;
wind_class.hInstance = Marshal.GetHINSTANCE(this.GetType().Module);
wind_class.hIcon = IntPtr.Zero;
wind_class.hCursor = LoadCursor(IntPtr.Zero, (int)IDC_CROSS);
wind_class.lpszMenuName = null;
wind_class.lpszClassName = "myClass";
wind_class.lpfnWndProc = Marshal.GetFunctionPointerForDelegate(delegWndProc);
wind_class.hIconSm = IntPtr.Zero;
ushort regResult = RegisterClassExW(ref wind_class);
if (regResult == 0)
{
uint error = GetLastError();
return false;
}
IntPtr hWnd = CreateWindowExW(0, wind_class.lpszClassName, "Hello Win32", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 300, 400, IntPtr.Zero, IntPtr.Zero, wind_class.hInstance, IntPtr.Zero);
Console.WriteLine($"{hWnd == IntPtr.Zero} {Marshal.GetLastWin32Error()}");
if (hWnd == ((IntPtr)0))
{
return false;
}
ShowWindow(hWnd, 1);
UpdateWindow(hWnd);
return true;
}
private static IntPtr myWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
switch (msg)
{
// All GUI painting must be done here
case WM_PAINT:
break;
case WM_DESTROY:
DestroyWindow(hWnd);
break;
default:
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
也接受通过先前调用RegisterClass或RegisterClassEx函数创建的类原子。
因此,如果RegisterClassEx()成功,则可以使用其return value (ATOM)作为CreateWindowExW()中类名的替换,以查看其是否有效。在C ++中,它将是这样的:
lpClassName
在C#中,基于上述C#示例,使用UInt16替换ATOM,它将如下:
ATOM myClassAtom = RegisterClassExW(&wcex);
HWND hWnd = CreateWindowEx(0, (LPCWSTR)myClassAtom, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);