使用MVC设计模式在Access中实现数据的“查改增删”

在知乎上写了一篇文章,有兴趣的朋友可以通过这个链接访问。

使用MVC设计模式在Access中实现数据的“查改增删” – Jasoftiger的文章 – 知乎

因为知乎上不能上传示例文件,可以从这里下载:MVC Design Pattern in Access VBA

使用MVC设计模式在Access中实现数据的“查改增删”后记 – Jasoftiger的文章 – 知乎

MVC Design Pattern in Access VBA v2 彻底解耦MV

《老兵新传》与”公式相声”

最近在看群友介绍的好书:《老兵新传 Visual Basic核心编程及通用模块开发》——张宁编著,精读到第4章,很有感触。遂写一篇以留记忆。

首先这是一本好书,读起来很顺,内容的编排也很合理,在中文版的技术书籍中算是写得非常好的。以前看过一些中文技术类书籍,晦涩难懂,深感失望,所以从此只找英文版的来读。

读英文版的,刚开始英语确实是一个障碍,有很多英文单词不知道意思,就查字典,每次遇到生词,就查一次。好在现在网络发达,生词在线一搜就翻译出来了,耽误不了太多功夫。时间久了,发现查来查去,也就是那些单词。所以越到后来,字典查得也越少。一旦克服了语言的障碍,展现在你眼前的是一片新大陆。因为计算机毕竟是外国人发明的,编程语言也是,所以读那些原汁原味的书,你才知道其中的各种蹊跷。当然我也读过写得很糟糕的英文书,但是还是以好书居多。

话题好像跑偏了,拉回来。这本《老兵新传》的作者,应该是花了很多的心血在里面的。特别是前几章的内容,写得非常细致。从我之前写《VBA类模块》的经历来看,要把一个技术类的东西,写出来让不懂得人看懂,是很不容易的。这个过程需要足够的耐心,还需要有一点编剧的才能。作者这些都做到了。

我突然又联想到这两天的一个热门话题:《交大博士夫妻“公式相声”叫板郭德纲》。我看完了整个视频,平心而论,博士夫妻讲的这段内容,月亮的倒影,永动机这些,不是什么高深的内容,受过初等教育的普罗大众都能听懂。拿这2个点说事,有点言过其辞了。但是,他们最大的问题出在对他们衣服上的公式的解释上。

他们没有用普罗大众能理解的方式,对这2个公式做出解释。更准确的说,他们压根没打算让观众明白这些公式的含义。那他们为什么要把公式印在衣服上呢?唯一的答案,就只能是装逼了。

不解释这些公式就算了,博士男还一副老子天下第一的姿态,打算用蛮横不讲理的态度,逼迫郭德纲和观众信服他们的“公式相声”,这个就显得情商欠费了。犯了众怒,也就不奇怪了。

尽管这样,我还是宁愿相信他们的“公式相声”里面是有技术含量的。毕竟别人出了3本书,没读之前,还是要有所期待。

为什么要扯到这里来?我总是觉得,如果这两博士如果能够将技术应用这两件事情中的任何一件能做好,也不至于落到现在这般境地。比如,运用他们的“公式相声”的技术,说一段脍炙人口的相声。或者将他们研究出来的“公式”用平易近人的语言给观众解释清楚。这2方面,他们都做得很糟糕。

这种2头都很糟糕的例子,在国内的技术群或论坛里面非常常见。风气很不好。国外的论坛,完全是另外一个样子,你可能连装逼的机会都没有,因为在其他人看来,你的发的装逼帖子,没有给其他人带来任何价值,反倒是浪费了大家最宝贵的时间。

回到《老兵新传》这本书,话说2012年就出版了这本书,6年时间不到,就在各大购物购书网站上无法购买了。

而网络上随便一搜就能搜到该书的电子版。不知道作者该作何感想。

中国技术不如美国,中兴通讯被美国啪啪打脸的时候,我们除了吐槽之外,有做过任何一件有助益的事情吗?

P.S. 我看《老兵新传》的也是电子版,如原作者看到本文,欢迎联系提供账号,愿意付费。

Visual Basic 的秘密(全文翻译完毕)

本文主要讲的是VB中的指针,是我看过的讲得最好的一篇文章。很底层的东西,有兴趣的可以看看,对学习Windows API编程很有帮助。若翻译有欠妥的地方,欢迎指正^_^


  • 原文网址:http://www.thevbzone.com/secrets.htm
  • 作者:Kevin Wilson
  • 目录:
    1. 介绍
    2. 在Visual Basic 中使用指针
    3. VarPtr,StrPtr和ObjPtr
    4. ByRef / ByVal
    5. AddressOf 和回调
    6. 访问“隐藏的”API

1 介绍:

Visual Basic被称为“快速应用程序开发(RAD)开发工具”,因为它旨在为你处理Windows“基础工作”,从而使你可以专注于重要的东西,如程序的功能和文档。

例如,你打开VB,新建一个项目,然后在项目中添加一个标准的“窗体”,按“F5”来执行程序,该窗体就被显示出来,我们操作起来非常简单,但是,你可能不知道,VB在背后帮我们做了很多事情。 VB需要调用“CreateWindow”来实际创建“窗体”,并为构成它界面的属性赋值。 然后,需要通过调用各种Win32 API来修改窗体上的字体,前景色,背景色,设备上下文(device context)等。 最后,VB需要用子类化的方式,将新创建的窗体挂钩(hook)到Windows消息流中,让窗体捕获到发送给它的Windows消息,最后使用“WindowProc”回调函数,处理每个Windows消息。 窗体的接口越复杂,窗体对象的创建和功能处理代码就越复杂。 而C和C ++程序员就没有这么轻松了,他们需要亲自编写代码,来创建所有的对象,处理消息流以及销毁对象(或者通过模板来生成代码)。

对于会正确使用VB开发工具的程序员来讲,Visual Basic能帮助你做一些类似上述“基础”的事情,是一个非常强大的功能。但是,与此同时,那些不太了解如何编程的人,也拥有了很大的能力。也正是这个原因,Visual Basic被C和C++程序员嘲笑。他们说,“任何人都可以用VB做开发,但只有真正的程序员才能使用C / C++做开发。我认为,聪明的程序员选择Visual Basic,因为VB在对象创建,消息处理,对象销毁过程中,可以帮你消除潜在的Bug。VB能提供更简单更快捷Windows事件处理,VB能为你提供更强大的界面功能,VB能让你更轻松的访问COM对象和第三方控件,VB更容易阅读,因为它非常接近阅读英语,而C/C++看上去却非常神秘。VB允许你轻松访问Win32 API (这使得程序员有驾驭Windows的强大功能的能力)。以及最最重要的是,Visual Basic可以通过组件,库,和其他用C/C++编写的代码来挂钩住(hook)C/C++的强大功能和速度。嘿。。。。。。C/C++程序员,现在怎么不继续吹牛逼了?^_^

事情就是这样。。。。。。即使是工作多年的VB程序员也没有意识到VB的真正力量,因为他们没有掌握(或意识到)VB提供的一些关键概念和功能。这些概念很少被大家知道,或者受重视的程度还远远不够,因为我将它们称之为“VB的秘密”。

2 在VB中使用指针:

我曾经在求职面试中被问到一个问题,现在我意识到这是一个有坑的问题。“Visual Basic 是否有或者使用‘指针’?”任何用过VB的人都会显而易见的回答“No”,在VB中,你看不到任何像C/C++中那样的指针的声明,这就是当时我认为面试官要问的点,而她对我的答案也表示认同。然而,正确的答案应该是“Yes”。

Visual Basic (就像几乎所有其他编程语言一样)确实使用指针,广泛的使用。不同的是,Visual Basic会尽可能的将它们隐藏起来,或者将它们称为不同的东西,以免给你带来负担。

接下来,我们谈谈,如何使用指针直接访问变量中的信息(VarPtr / StrPtr / ObjPtr),通过指针将信息传递给函数(ByRef / ByVal),取回和传递指向函数的指针(AddressOf)。

3 VarPtr, StrPtr 和 ObjPtr:

VB函数“VarPtr”(变量指针),“StrPtr”(字符串指针)和“ObjPtr”(对象指针)是无官方文档(undocumented)无技术支持的(unsupported)的函数,微软在VB5.0和VB6.0中引入。这些函数(跟随很多其他的函数一起)在VB.Net中不再使用。这些函数允许你获取VB变量在内存中的地址(指针),以及变量所指向的实际数据在内存中的地址。这些函数为什么作用这么大?因为,如果你知道数据的内存地址,你可以任意操控它,直接复制或者传递出去,而不需要借助VB的帮助。这样做,速度上更快,并且(在某些情况下)你可以做到VB本身做不到的事情。

下面是微软MSDN关于“VarPtr”的说明:


该函数可以用来获取一个变量或数组元素的地址。它使用变量名或数组元素作为参数,返回它的地址。然而,你需要注意的是,未锁定的动态数组可能会被Visual Basic重新分配地址,所以你当你使用VarPtr获取数组元素地址时,必须非常小心。

下面的示例获取变量的地址:

下面的示例获取某个数组的第4个元素的地址:

限制:VarPtr函数不能用来获取数组的地址。。。。。。


下面是微软MSDN关于“StrPtr”的说明:


在Visual Basic 中,字符串是以BSTR来存储的。如果你对一个字符串变量使用VarPtr,你将得到BSTR的地址,它是该字符串指针的指针。要获取字符串缓冲本身的地址,你需要使用StrPtr函数。该函数返回字符串第一个字符的地址。需要考虑到在Visual Basic中,字符串是以UNICODE来存储的。

要获取一个字符串的第一个字符的地址,请将该字符串变量传递给StrPtr函数。

示例:

当你需要传递一个指向UNICODE字符串指针给API时,你可以使用StrPtr函数。


下面是微软MSDN关于“ObjPtr”的说明:


ObjPtr函数使用一个对象变量名作为参数,获取该对象变量所引用的接口的地址。

一种试用该函数的情况,是当你需要处理集合对象的时候。相比使用Is操作符遍历集合对象而言,通过使用对象地址作为索引关键字,你可以获取更快的访问速度。在很多情况下,对象的地址是唯一可以信赖的键值。

示例:


注意在“VarPtr”说明的底部的“限制”,它说你不能使用VarPtr获取数组的地址。在某种程度上,说得没错。你不能将变量“MyArray”传递给它(因为VB将数组保存在一个叫做“SafeArray”的OLE对象中),但是,如果你获取了数据的第一个元素“MyArray(0)”的地址,你就有了整个数组的地址,因为数组元素在内存中是连续存储的(按照数字顺序从第一个元素到最后一个元素)。所以,如果某个Win32 API 或者C / C++ 函数需要一个指向某个字节数组的指针,像这样:

你可以像这样调用它:

注意:将坐标点(pointers)保存到动态数组中要小心,因为当动态数组重新分配内存地址后,或者动态数组改变了大小后,或者被重新定义(ReDim)后,实际数据将非常有可能被保存到一个新的内存地址。

关于VarPtr,StrPtr,ObjPtr的更多信息,可以参见以下链接:

http://support.microsoft.com/default.aspx?scid=kb;en-us;Q199824

http://msdn.microsoft.com/library/en-us/dnw32dev/html/ora_apiprog6_topic1.asp

http://msdn.microsoft.com/library/en-us/dnovba00/html/LightningStrings.asp

http://msdn.microsoft.com/library/en-us/dnovba01/html/Lightweight.asp

4 ByRef / ByVal

到目前为止,VB程序员在调用Win32 API(或任何C/C++导出函数)时遇到的最大的问题,就是参数的正确传递。在应该使用“ByVal”的地方使用了“ByRef”(或者反过来也一样),或者当函数期望传入一个指针时,你传入的却是一个值或者一个变量,这些都可能会导致系统崩溃。要想搞懂如何正确的传递参数,就要了解Windows程序如何调用堆栈的(calling stacks),以及主调程序与被调函数之间的内存分配。

首先,我们来讨论一下什么是“调用堆栈”,以及当传递参数给函数时,它是怎样受内存分配影响的。“调用堆栈”说白了,就是内存中的一段空间,那些传递给函数的变量和值,以及函数返回值都存储在这段内存空间中。它被叫做“堆栈”(stack)是因为参数值是一个挨着一个被存储在这段内存空间中,访问这些参数值时,也是按照这种假定来访问。正因为如此,从纵向角度从下到上来看,这些参数一个堆在另一个上面,组成了要给到函数的全部参数信息。当参数被添加到函数的调用堆栈上时,它被称为“压入”(push)到调用堆栈上面。当参数从函数的调用堆栈移除的时候,它被称之为从堆栈“弹出”(pop)。“堆栈”,“压入”,“弹出”这些都是汇编术语(是的,我们现在讨论得非常底层),如果你将程序或者DLL反编译到汇编语言,你会看到一些代码行上有“push”,“pop”等单词。

当Visual Basic调用Win32 API(或者任何导出的C/C++函数)时,它期望被调函数使用“标准调用惯例”(Standard Calling Convention)(_stdcall),这与C/C++默认的调用惯例(_cdecl)刚好不同。这就意味着,当函数被调用的时候,参数被从右到左传入到内存中(或者压入到堆栈上面),被调函数负责清理参数的内存(或者从堆栈弹出)。如果调用一个声明为任何非“标准调用惯例”_stdcall的导出函数,Visual Basic不知道如何处理堆栈和传出传入的参数,VB就会跳出信息说“错误的DLL调用惯例”(Bad DLL Calling Convention)。

调用参数时内存如何分配和收回?调用堆栈是什么?以及它们在Windows中是怎么工作的?想知道更多更深入的解释,我强烈的推荐一本Dan Appleman写的书:“Dan Appleman’s Win32 API Puzzle Book and Tutorial for Visual Basic Programmers”。

现在我们跳出底层的内存运作机制,回到VB上面来。调用函数时,给它传递参数的方式有2种,要么传递给它明确的值,要么传递给它一个指针,该指针指向内存中的某个存储了值的地址。当你传递像数字,大小,标志等简单信息时,你希望用值的方式(ByVal: By Value)传递信息,因为你想让函数获取你所传递的值,而不是这个值当前被存储在内存中的地址。现在当你想要传递更加复杂的数据的时候,比如一个数据类型,一组数组,或者一个对象引用,你需要传递一个引用(或指针)到被调函数,告诉它数据在内存中的地址是多少。这是通过指定ByRef(By Reference)关键字完成的。这样,被调函数到内存中找到这个地址,读取相关的数据。这里有一个例外,当你传递字符串(String)参数给Win32 API函数时(或者任何C/C++导出函数),请使用ByVal方式(除非你传递字符串数组,这种情况下使用ByRef,或者用ByVal传递字符串数组的第一个元素)。

所以到现在为止,你可以说:“我已经知道ByRef/ByVal这两种方式传递参数了”。是的,但是你意识到通过ByRef传递参数时,是在传递指针吗?如果你把这个概念再往前推进一步,你可以让函数接口变得更加通用:把通过引用传递(ByRef)改为通过值传递(ByVal),然后传递一个明文的指针。所以,你可以像这样声明函数:

如果你仔细想想的话,在声明函数和参数的时候,它可以给你各种选择。你不在局限于特定的变量类型。你可以将所有参数都变成“Long”型变量,然后将指针传递给任何函数参数(只要你小心翼翼的话)。比如说在传递自定义类型的时候遇到麻烦了,忘掉它,传递指针就好了。再比如在传递对象的时候遇到麻烦了,忘掉它,传递指针就好了。VB5.0中不允许将变量数组作为函数返回类型,忘掉它,返回一个长整型,指向数组的内存地址,然后用API函数CopyMemory将它拷贝到本地数组中。看出来我要干什么了吗?

联合ByRef和ByVal一起使用VarPtr,StrPtr和ObjPtr,可以让你传递任何格式的数据,如果你知道自己在做什么的话。

5 AddressOf 和回调

操作符“AddressOf”跟回调(call back)息息相关。“但什么是回调?”你问。回调是VB事件在Windows中的等价物。事实上,从底层来讲,当回调函数捕获了以Windows系统消息形式存在的原始事件之后,回调函数会触发VB事件。回调函数常见于Win32 API内部(和其他C/C++代码中),特别是当你的应用程序中需要监测用户和/或Windows的活动的时候。在VB中你不怎么看到回调函数,是因为VB通过“事件”(Events)处理消息和通知,这种方式比起使用回调函数更加简单和安全。

假如说,在你的项目中,Windows发送给窗体的每一个消息你都想收到通知,(很多消息也许你根本不会用到),当然还有一些由于其他API调用,可能发送到你的窗体的个性化消息。要想达到这个目的,你所要做的就是设置一个被Windows认可的回调函数(“WindowProc”),然后告诉Windows(通过“SetWindowLong” API函数)将它的所有与你的窗体有关的消息发送给你的回调函数,这样你就能够检查它们,对消息做出反应,将消息传递下去(通过“CallWindowProc” API函数)。所有这一切,就叫做“子类化”(Sub-Classing),功能非常强大(同时也是非常危险的)的技术,你可以用这个技术来个性化的重画你的窗体,菜单和内容(或你想对你的窗体干的任何事情)。

使用“AddressOf”有2个缺点:

1)你只能获取VB标准模块中的函数或子程序(公有或私有)的地址。没有办法能绕过这个限制。

2)它只能被作为函数或子程序参数列表的一部分被调用。绕过这个限制的办法如下:

你会注意到我们传递“AddressOf”和我们想得到地址(内存指针)的函数名到“GetProcAddress”函数,该函数只是简单的返回地址值,简单又非常有效。函数和子程序的地址不会变,所以你可以将想要调用的函数和子程序的地址保存下来,而不必在每次调用的时候都使用AddressOf。

“那我们来看看实际应用吧!”你说。ok!

如上所述,这里有一个“子类化”的示例:

下面是一个“列举”的示例,它是反馈Windows列表信息非常流行的方法(例如所有窗体列表,窗体上所有对象的列表,已安装的所有字体列表等):

6 访问“隐藏的”API

这部分绝对是这篇文章所述所有秘密中最“秘密”的部分。在Windows中确实有很多隐藏的Win32 API调用……诀窍是找到它们并找出如何调用它们,因为微软肯定不会告诉你。

但是微软为什么要隐藏它们呢?”你可能会问。因为这些隐藏的函数为API添加了额外的功能,只有微软知道如何使用它们。这使得他们自己的产品(在Windows下运行)可以获得优势地位。因为只有他们知道如何访问这些更强大的,更快的隐藏着的API函数,而其他人只能使用官方在MSDN中公开披露的正规的API函数。“不公平的竞争优势”?你的想法的一点没错!但是谁也没有说过微软在公平的展开业务,或者有道德的做生意。这些商业行为正不断地将微软置于法庭和报纸的头条新闻中。

“这些隐藏的API都是什么样的?如何找出它们?如何使用它们?”非常好的问题。网络上有很多网页,它们致力于找到这些隐藏的API,纰漏它们的功能给“平等的竞争环境”,并为像你我这样的开发者提供更酷的功能。这里有一些很好的网页:

你可以在“VB标准模块”下的modCOMDLG32.bas模块中找到一些这些“隐藏的API”。它们看起来像这样:

你会注意到它们的别名是数字“#90”,“#91”。这些被称为“序数”,它们是一种通过DLL公开API而不暴露它们的名称的方法。由此可见,如果你编写了一个函数,只想给自己使用,而不想让其他人知道该函数的更多信息,那么你可以通过数字公开它。除了一个数字,其他人对此是一无所知的,但只有你知道如何调用它。

偷偷摸摸的,是吧?:)

好了!这就是我要说的所有内容了。如果我想到了VB中的其他的秘密,或者“隐藏的”和“模糊的”功能(或想起来我还打算放在这里的任何内容),我会把它们加进来。编码愉快!

(全文结束)