win32程序开发流程

windows程序分为“程序代码”和“UI”资源两大部分,两大部分最后以RC编译器整合为一个完整的exe档案。

需要什么函数库(.LIB)

动态链接库:引用程序所调用的windows API函数是在“执行时期”才链接上的。

并不是扩展名是dll的才是动态链接库,事实上.exe,.dll,.fon,.mod,.drv,.ocx都是所谓的动态链接库。

需要什么头文件

以消息为基础,以事件驱动之

接受并处理消息的主角就是窗口。每一个窗口都应该有一个函数负责处理消息,程序员必须负责设计这个所谓的“窗口函数”。程序员必须负责设计这个所谓的“窗口函数”。如果窗口获得一个消息,则这个窗口函数必须判断消息的类别,决定处理的方式。

程序进入点WinMain

当Windows的外壳shell侦测到使用者欲执行一个Windows程序,于是调用加载器把该程序加载,然后调用C startup code,后者再调用WinMain,开始执行程序。WinMain的四个参数由操作系统传递进来。

回调函数(什么是回调函数)

你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。(作者:常溪玲链接:https://www.zhihu.com/question/19801131/answer/13005983来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。)

对话框的运行

Windows的对话框依其与父窗口的关系,可分为两类:

  1. “令其父窗口失效,直到对话框结束”,这种称为modal对话框;
  2. “父窗口与对话框共同运行”,这种称为modeless对话框。

资源描述文件

RC文件是一个以文字描述资源的地方

SDK程序

指以Windows raw API完成的程序

核心对象

核心对象是系统的一种资源,系用对象一旦产生,任何应用程序都可以开启并使用该对象,系统给予核心对象一个计数值作为管理之用。核心对象包括:event,mutex,semaphore,file,file-mapping,process,thread

虚函数

从操作型定义来看,什么是虚函数?如果你预期派生类有可能重新定义某一个成员函数,那么你就在基类中把此函数设为virtual。MFC有两个十分重要的虚函数:与document有关的Serialize函数和与view有关的OnDraw函数。应该在自己的CMyDoc和CMyView中改写这两个虚函数。

纯虚拟函数不需定义其实际动作,它的存在只是为了在衍生类别中被重新定义,只是为了提供一个多态接口。只要是拥有纯虚拟函数的类别,就是一种抽象类别,它是不能够被具象化(instantiate)的,也就是说,你不能根据它产生一个对象(你怎能说一种形状为’Shape’ 的物体呢)。

■ 如果你期望衍生类别重新定义一个成员函数,那么你应该在基础类别中把此函数设为virtual。

■ 以单一指令唤起不同函数,这种性质称为Polymorphism,意思是” the ability to assume many forms “,也就是多态。

■ 虚拟函数是C++ 语言的Polymorphism性质以及动态绑定的关键。

■ 既然抽象类别中的虚拟函数不打算被调用,我们就不应该定义它,应该把它设为纯虚拟函数(在函数声明之后加上” =0” 即可)。

■ 我们可以说,拥有纯虚拟函数者为抽象类别(abstract Class ),以别于所谓的具象类别(concrete class ) 。

■ 抽象类别不能产生出对象实体,但是我们可以拥有指向抽象类别之指针,以便于操作抽象类别的各个衍生类别。

■ 虚拟函数衍生下去仍为虚拟函数,而且可以省略virtual关键词。

多态

编译器无法在编译时期判断pEmp -> computePay到底是调用哪一个函数,必须在执行时期才能评估之,这称为后期绑定late binding 或动态绑定dynamic binding。

C++ Template Functions

函数声明:

template <class T> T power (T base,int exponent);

T power(T base,int exponent);

容易让人迷惑的是其中的”class”字眼,它其实并不一定表示C++的class,它也可以是一个普通的数据类型。不过是表示:T是一种普通类型,而此一类型将在调用此函数时才给予。

构造式和析构式

所谓构造式(constructor),就是对象诞生后第一个执行(并且是自动执行)的函数,它的函数名称必定要与类别名称相同。

相对于构造式,自然就有个析构式(destructor),也就是在对象行将毁灭但未毁灭之前一刻,最后执行(并且是自动执行)的函数,它的函数名称必定要与类别名称相同,再在最前面加一个~ 符号。

一个有着阶层架构的类别群组,当衍生类别的对象诞生之时,构造式的执行是由最基础类别(most based)至最尾端衍生类别(most derived);当对象要毁灭之前,析构式的执行则是反其道而行。

RTTI运行类型识别

具备IsKindOf的能力,能在执行期侦测某个对象是否“属于某种类”,并传回True或False。

消息映射(Message Mapping)

将消息与表格(消息映射表——Message Map)中的元素比较,然后调用对应的处理程序,这种操作我们也称为消息映射。

XML文件的C#读取

在程序中访问进而操作XML文件一般有两种模型,分别是使用DOM(文档对象模型)和流模型,使用DOM的好处在于它允许编辑和更新XML文档,可以随机访问文档中的数据,可以使用XPath查询,但是,DOM的缺点在于它需要一次性的加载整个文档到内存中,对于大型的文档,这会造成资源问题。流模型很好的解决了这个问题,因为它对XML文件的访问采用的是流的概念,也就是说,任何时候在内存中只有当前节点,但它也有它的不足,它是只读的,仅向前的,不能在文档中执行向后导航操作。

使用XmlDocument是一种基于文档结构模型的方式来读取XML文件.在XML文件中,我们可以把XML看作是由文档声明(Declare),元素(Element),属性(Attribute),文本(Text)等构成的一个树.最开始的一个结点叫作根结点,每个结点都可以有自己的子结点.得到一个结点后,可以通过一系列属性或方法得到这个结点的值或其它的一些属性.

   1: xn 代表一个结点
   2: xn.Name;//这个结点的名称
   3: xn.Value;//这个结点的值
   4: xn.ChildNodes;//这个结点的所有子结点
   5: xn.ParentNode;//这个结点的父结点

1.1 读取所有的数据.

使用的时候,首先声明一个XmlDocument对象,然后调用Load方法,从指定的路径加载XML文件.

   1: XmlDocument doc = new XmlDocument();
   2: doc.Load(@"..\..\Book.xml");

然后可以通过调用SelectSingleNode得到指定的结点,通过GetAttribute得到具体的属性值.参看下面的代码

// 得到根节点bookstore
XmlNode xn = xmlDoc.SelectSingleNode("bookstore");
// 得到根节点的所有子节点
XmlNodeList xnl = xn.ChildNodes;
foreach (XmlNode xn1 in xnl)
{
BookModel bookModel = new BookModel();
// 将节点转换为元素,便于得到节点的属性值
XmlElement xe = (XmlElement)xn1;
// 得到Type和ISBN两个属性的属性值
bookModel.BookISBN = xe.GetAttribute("ISBN").ToString();
bookModel.BookType = xe.GetAttribute("Type").ToString();
// 得到Book节点的所有子节点
XmlNodeList xnl0 = xe.ChildNodes;
bookModel.BookName=xnl0.Item(0).InnerText;
bookModel.BookAuthor=xnl0.Item(1).InnerText;
bookModel.BookPrice=Convert.ToDouble(xnl0.Item(2).InnerText);
bookModeList.Add(bookModel);
}
 dgvBookInfo.DataSource = bookModeList;

在正常情况下,上面的代码好像没有什么问题,但是对于读取上面的XML文件,则会出错,原因就是因为我上面的XML文件里面有注释,大家可以参看Book.xml文件中的第三行,我随便加的一句注释.注释也是一种结点类型,在没有特别说明的情况下,会默认它也是一个结点(Node).所以在把结点转换成元素的时候就会报错.”无法将类型为“System.Xml.XmlComment”的对象强制转换为类型“System.Xml.XmlElement”。”

幸亏它里面自带了解决办法,那就是在读取的时候,告诉编译器让它忽略掉里面的注释信息.修改如下:

XmlDocument xmlDoc = new XmlDocument();
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;//忽略文档里面的注释
XmlReader reader = XmlReader.Create(@"..\..\Book.xml", settings);
xmlDoc.Load(reader);

最后读取完毕后,记得要关掉reader.

   1: reader.Close();

这样它就不会出现错误.

1.2C#读取XML中元素和属性的值(XmlDocument读取Xml)

反射

什么是反射:
1.在程序运行时,
​ 动态获取 加载程序集
​ 动态获取 类型(类,接口)
​ 动态获取 类型的成员 信息(字段,属性,方法)
2.在运行时,
​ 动态创建类型实例,以及调用和访问这些实例成员

程序集

程序集是代码进行编译是的一个逻辑单元,把相关的代码和类型进行组合,然后生成PE文件(例如可执行文件.exe和类库文件.dll)。由于程序集在编译后并不一定会生成单个文件,而可能会生成多个物理文件,甚至可能会生成分布在不同位置的多个物理文件,所以程序集是一个逻辑单元,而不是一个物理单元。即程序集在逻辑上是一个编译单元,但在物理储存上可以有多种存在形式。对于静态程序集可以生成单个或多个文件,而动态程序集是存在于内存中的。在C#中程序集处处可见,因为任何基于.NET的代码在编译时都至少存在一个程序集(所有.NET项目都会默认引用mscorlib程序集)

类库编译成程序集
bin目录下生成.dll或。.exe

程序集包含哪些内容:

类型元数据【以二进制的形式,描述代码中定义的每一个类型和成员】
程序集元数据【程序集清单,版本号,名称等】
IL代码【这些都被装在exe或dll中】
资源文件

反射中要用到动态链接库作为插件,需要声明一个程序集来接收。

Type类

反射中用到的最重要的一种类型就是type类,System.Type类对于反射起着核心的作用。当反射请求加载的类型时,公共语言运行库将为它创建一个 Type。可以使用Type 对象的方法、字段、属性和嵌套类来查找有关该类型的所有信息。

接口

为了规范插件的行为,需要主程序开发者编写接口,统一操作类型及对象。

加载程序集常用到的方法示例:

        private void LoadPlugins()
        {
            // 1.加载plugins目录下的所有的dll文件
            string plugins = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "plugins");
            //   1.1 搜索plugins目录下的所有的dll文件 
            string[] dlls = Directory.GetFiles(plugins, "*.dll");
            // 2.循环将每个dll文件都加载起来
            foreach (string dllPath in dlls)
            {
                //  2.1 动态加载当前循环的dll文件
                Assembly assembly = Assembly.LoadFile(dllPath);
                //  2.2 获取当前dll中的所有的public类型
                Type[] types = assembly.GetExportedTypes();
                //  2.3 获取IEditor接口的Type
                Type typeIEditor = typeof(IEditor);

                for (int i = 0; i < types.Length; i++)
                {
                    // 2.4 验证当前的类型即实现了IEditor接口并且该类型还可以被实例化
                    if (typeIEditor.IsAssignableFrom(types[i]) && !types[i].IsAbstract)
                    {
                        IEditor editor = (IEditor) Activator.CreateInstance(types[i]);
                        // 2.5 向菜单栏中动态添加一个菜单项
                        ToolStripItem toolItem = 编辑ToolStripMenuItem.DropDownItems.Add(editor.PluginName);
                        // 2.6 为刚刚增加的菜单项注册一个单击事件
                        toolItem.Click += new EventHandler(toolItem_Click);
                        toolItem.Tag = editor;
                    }
                }
            }
        }

        private void toolItem_Click(object sender, EventArgs e)
        {
            ToolStripItem item = sender as ToolStripItem;
            if (item != null)
            {
                if (item.Tag != null)
                {
                    IEditor editor = item.Tag as IEditor;
                    if (editor != null)
                    {
                        // 运行该插件
                        editor.Execute(this.textBox1);
                    }
                }
            }
        }

其中,接口代码如下:

    public interface IEditor
    {
        string PluginName
        {
            get;
        }

        void Execute(TextBox txtbox);
    }

网上的一篇不错的总结:

C#学习-程序集和反射

C#反射程序集

待补充…

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐