看到这个标题,你也许会惊讶:c#也能这样创建?是的,没错,大多数使用API来创建窗口的是c/c++,当然也有其他的语言,但是几乎没有看到使用c#来创建的(不包括.net框架的),因为.net有着非常高效的开发方式,所以大部分人选择了规避。
传统的win32窗口程序入口点是 WinMain 函数,但是我们在使用c#开发程序的时候,无论是控制台还是窗口应用程序,入口点永远是 Main 函数,我们熟悉的 HINSTANCE 这些参数不见了,至于去了哪里不是这篇文章所探究的,没有了 hInstance 这个参数我们是无法创建的,但是.net框架依然能够使用我们平时所创建的窗体跑起来,说明这个参数在Main函数之前被获取到了,我花了点时间查阅了很多资料,在网上找到了解决办法,有 Marshal 这样的一个类,里面有个静态函数 GetHINSTANCE,参数是Module,这样一来似乎找到了解决方案,确实如此,至于 Module 的获取办法更是简单了 typeof(你的类名).Module,障碍清除了,接下来就好办多了,按照正常的创建流程来写,不过你必须得事先声明API函数才行。接下来,笔者带你创建一个窗口程序。
1.创建一个控制台应用程序,修改项目的属性,改为窗口应用程序。
2.在你的 Main 函数里面添加一些代码:
static void Main(string[] args)
{
Type t = typeof(Program);
Module m = t.Module;
IntPtr hInstance = Marshal.GetHINSTANCE(m);
}
记得添加命名空间:
using System.Runtime.InteropServices;
using System.Reflection;
接下来是注册窗口类,不过还得先声明好窗口类
struct WNDCLASS
{
public ClassStyle style;
public WNDPROC proc;
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;
}
我们注意到窗口类的第2个字段是个函数地址,那么对应.net的是委托类型,这个WNDPROC声明如下:
public delegate int WNDPROC(IntPtr hwnd,uint uMsg,IntPtr wParam,IntPtr lParam);
然后声明API函数:
[DllImport("user32.dll", EntryPoint = "RegisterClassA")]
public static extern IntPtr RegisterClass(ref WNDCLASS cls);
再然后你仅仅只是对这个结构进行填充再注册:
WNDCLASS cls = new WNDCLASS();
cls.cbClsExtra = 0;
cls.cbWndExtra = 0;
cls.hbrBackground = IntPtr.Zero;
cls.hCursor = IntPtr.Zero;
cls.hIcon = IntPtr.Zero;
cls.hIconSm = IntPtr.Zero;
cls.hInstance = hInstance;
cls.lpszClassName = "DotNet_Window";
cls.lpszMenuName = null;
cls.proc = WindowProc;
cls.style = ClassStyle.CS_VREDRAW|ClassStyle.CS_HREDRAW;
RegisterClass(ref cls);//注册窗口类
...
当然了,代码不是到此结束,因为接下来的代码仅仅只是照搬创建win32窗口的流程,所以笔者不打算继续讲,有需要的可以去下载源码。
源码地址:点击打开链接