| 프로필从零点起飞사진블로그리스트 | 도움말 |
|
10월 26일 Visual C++中调用DLL实现数据加密 数据加密是计算机安全领域的重要内容,其基本思想是通过变换信息的表现形式来保护敏感信息,使非授权者不能了解被保护信息的内容。常见的数据加密算法有:DES,IDEA,RSA,ECC,AES,MD5,SHA等。 《共享软件加密算法库》是一款针对个人、企业开发共享软件的加密工具,支持Windows平台下各类开发工具:VC、VB、Delphi、PB、VFP等,算法库集成的算法有:BlowFish、MD5、Secret16、AES、SHA、CRC32、RSA、DES、字符串加/解密、文件加/解密等多种功能强大的算法。其提供了DLL文件-Reg.dll,可以通过复用它来实现数据加密与解密。 1、隐式链接 其提供了 Reg.h与Reg.lib两个隐式链接所必须的文件,所以可以采用此种方式。 ①在VC中打开File View一页,选中工程名,单击鼠标右键,然后选中"Add Files to Project"菜单,在弹出的文件对话框中选中要加入Reg.lib。 ②在VC中打开File View一页,选中Header files,单击鼠标右键,然后选中"Add Files to Folder"菜单,在弹出的文件对话框中选中要加入Reg.h,然后在工程相应的头文件中加入#include "Reg.h"。在Reh.h头文件中给出了DLL中定义的函数原型及声明。 如:加密函数原型及声明为extern "C" BOOL WINAPI File Encrypt(LPCTSTR lpInputFileName, LPCTSTR lpOutputFileName, LPCTSTR lpKey, LPCTSTR lpRegisterCode);解密函数原型及声明为extern "C" BOOL WINAPI File Decrypt(LPCTSTR lpInputFileName, LPCTSTR lpOutputFileName, LPCTSTR lpKey, LPCTSTR lpRegisterCode)。其中对于WINAPI宏,把它加到函数原型定义前,系统会把它翻译为适当的调用方式,在Win32中,是把它翻译为_stdcall调用方式。 ③直接调用所需要的加密与解密函数,如调用File Encrypt()函数实现文本文件和二进制文件的加密,调用File Decrypt()函数实现文本文件和二进制文件的解密,调用时的参数要与函数定义参数相符合。 2、显式链接 如果只提供Reg.dll一个文件,那么须用此种方式。 ①加密模块:调用File Encrypt()函数实现文本文件和二进制文件的加密。
②解密模块:调用File Decrypt()函数实现文本文件和二进制文件的解密。
DLL专题学习(一) 比较大的应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作。可能存在一些模块的功能较为通用,在构造其它软件系统时仍会被使用。在构造软件系统时,如果将所有模块的源代码都静态编译到整个应用程序EXE文件中,会产生一些问题:一个缺点是增加了应用程序的大小,它会占用更多的磁盘空间,程序运行时也会消耗较大的内存空间,造成系统资源的浪费;另一个缺点是,在编写大的EXE程序时,在每次修改重建时都必须调整编译所有源代码,增加了编译过程的复杂性,也不利于阶段性的单元测试。
Windows系统平台上提供了一种完全不同的较有效的编程和运行环境,你可以将独立的程序模块创建为较小的DLL(Dynamic Linkable Library)文件,并可对它们单独编译和测试。在运行时,只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。Windows自己就将一些主要的系统功能以DLL模块的形式实现。 一般来说,DLL是一种磁盘文件,以.DLL、.DRV、.FON、.SYS和许多以.EXE为扩展名的系统文件都可以是DLL。它由全局数据、服务函数和资源组成,在运行时被系统加载到进程的虚拟空间中,成为调用进程的一部分。如果与其它DLL之间没有冲突,该文件通常映射到进程虚拟空间的同一地址上。DLL模块中包含各种导出函数,用于向外界提供服务。DLL可以有自己的数据段,但没有自己的堆栈,使用与调用它的应用程序相同的堆栈模式;一个DLL在内存中只有一个实例;DLL实现了代码封装性;DLL的编制与具体的编程语言及编译器无关。 在Win32环境中,每个进程都复制了自己的读/写全局变量。如果想要与其它进程共享内存,必须使用内存映射文件或者声明一个共享数据段。DLL模块需要的堆栈内存都是从运行进程的堆栈中分配出来的。Windows在加载DLL模块时将进程函数调用与DLL文件的导出函数相匹配。Windows操作系统对DLL的操作仅仅是把DLL映射到需要它的进程的虚拟地址空间里去。DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有. 一、关于调用方式: 1、静态调用方式:由编译系统完成对DLL的加载和应用程序结束时DLL卸载的编码(如还有其它程序使用该DLL,则Windows对DLL的应用记录减1,直到所有相关程序都结束对该DLL的使用时才释放它),简单实用,但不够灵活,只能满足一般要求。 隐式的调用:需要把产生动态连接库时产生的.LIB文件加入到应用程序的工程中,想使用DLL中的函数时,只须说明一下。隐式调用不需要调用LoadLibrary()和FreeLibrary()。程序员在建立一个DLL文件时,链接程序会自动生成一个与之对应的LIB导入文件。该文件包含了每一个DLL导出函数的符号名和可选的标识号,但是并不含有实际的代码。LIB文件作为DLL的替代文件被编译到应用程序项目中。当程序员通过静态链接方式编译生成应用程序时,应用程序中的调用函数与LIB文件中导出符号相匹配,这些符号或标识号进入到生成的EXE文件中。LIB文件中也包含了对应的DLL文件名(但不是完全的路径名),链接程序将其存储在EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows根据这些信息发现并加载DLL,然后通过符号名或标识号实现对DLL函数的动态链接。所有被应用程序调用的DLL文件都会在应用程序EXE文件加载时被加载在到内存中。可执行程序链接到一个包含DLL输出函数信息的输入库文件(.LIB文件)。操作系统在加载使用可执行程序时加载DLL。可执行程序直接通过函数名调用DLL的输出函数,调用方法和程序内部其他的函数是一样的。
显式的调用:是指在应用程序中用LoadLibrary或MFC提供的AfxLoadLibrary显式的将自己所做的动态连接库调进来,动态连接库的文件名即是上面两个函数的参数,再用GetProcAddress()获取想要引入的函数。自此,你就可以象使用如同本应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前,应该用FreeLibrary或MFC提供的AfxFreeLibrary释放动态连接库。直接调用Win32 的LoadLibary函数,并指定DLL的路径作为参数。LoadLibary返回HINSTANCE参数,应用程序在调用GetProcAddress函数时使用这一参数。GetProcAddress函数将符号名或标识号转换为DLL内部的地址。程序员可以决定DLL文件何时加载或不加载,显式链接在运行时决定加载哪个DLL文件。使用DLL的程序在使用之前必须加载(LoadLibrary)加载DLL从而得到一个DLL模块的句柄,然后调用GetProcAddress函数得到输出函数的指针,在退出之前必须卸载DLL(FreeLibrary)。 Windows将遵循下面的搜索顺序来定位DLL: DLL专题学习(二)二、MFC中的dll: a、Non-MFC DLL:指的是不用MFC的类库结构,直接用C语言写的DLL,其输出的函数一般用的是标准C接口,并能被非MFC或MFC编写的应用程序所调用。 b、Regular DLL:和下述的Extension Dlls一样,是用MFC类库编写的。明显的特点是在源文件里有一个继承CWinApp的类。其又可细分成静态连接到MFC和动态连接到MFC上的。 静态连接到MFC的动态连接库只被VC的专业般和企业版所支持。该类DLL应用程序里头的输出函数可以被任意Win32程序使用,包括使用MFC的应用程序。输入函数有如下形式: 动态链接到MFC的规则DLL应用程序里头的输出函数可以被任意Win32程序使用,包括使用MFC的应用程序。但是,所有从DLL输出的函数应该以如下语句开始: Regular DLL能够被所有支持DLL技术的语言所编写的应用程序所调用。在这种动态连接库中,它必须有一个从CWinApp继承下来的类,DllMain函数被MFC所提供,不用自己显式的写出来。 c、Extension DLL:用来实现从MFC所继承下来的类的重新利用,也就是说,用这种类型的动态连接库,可以用来输出一个从MFC所继承下来的类。它输出的函数仅可以被使用MFC且动态链接到MFC的应用程序使用。可以从MFC继承你所想要的、更适于你自己用的类,并把它提供给你的应用程序。你也可随意的给你的应用程序提供MFC或MFC继承类的对象指针。Extension DLL使用MFC的动态连接版本所创建的,并且它只被用MFC类库所编写的应用程序所调用。Extension DLLs 和Regular DLLs不一样,它没有一个从CWinApp继承而来的类的对象,所以,你必须为自己DllMain函数添加初始化代码和结束代码。 和规则DLL相比,有以下不同: 1、它没有一个从CWinApp派生的对象; DLL专题学习(三)三、dll入口函数: 1、每一个DLL必须有一个入口点,DllMain是一个缺省的入口函数。DllMain负责初始化(Initialization)和结束(Termination)工作,每当一个新的进程或者该进程的新的线程访问DLL时,或者访问DLL的每一个进程或者线程不再使用DLL或者结束时,都会调用DllMain。但是,使用TerminateProcess或TerminateThread结束进程或者线程,不会调用DllMain。 DllMain的函数原型: 参数: 2、_DllMainCRTStartup 为了使用“C”运行库(CRT,C Run time Library)的DLL版本(多线程),一个DLL应用程序必须指定_DllMainCRTStartup为入口函数,DLL的初始化函数必须是DllMain。 _DllMainCRTStartup完成以下任务:当进程或线程捆绑(Attach)到DLL时为“C”运行时的数据(C Runtime Data)分配空间和初始化并且构造全局“C++”对象,当进程或者线程终止使用DLL(Detach)时,清理C Runtime Data并且销毁全局“C++”对象。它还调用DllMain和RawDllMain函数。 RawDllMain在DLL应用程序动态链接到MFC DLL时被需要,但它是静态的链接到DLL应用程序的。在讲述状态管理时解释其原因。 8월 1일 MDMS中文档日志事件处理程序代码分析(续)using System;
using System.IO; using System.Data; using System.Data.SqlClient; using System.Security.Principal; using System.Runtime.InteropServices; using Microsoft.SharePoint; namespace DMS
{ //定义操作枚举类型 public enum DocLogType { Hits, // 0 Insert, // 1 Delete, // 2 Copy, // 3 Move, // 4 CheckIn, // 5 CheckOut, // 6 UnCheckOut, // 7 Update // 8 }; /// <summary>
/// Class1 的摘要说明。 /// </summary> public class DocumentLog { //以下LogHits,LogCheckIn,LogCheckOut,LogUnCheckOut,LogUpdate,LogInsert,LogDelete,LogCopy,LogRename //均是对这9种操作进行日志纪录,调用的是LogEvent写入日志。其中LogHits还要获得本文档库的一个列表DocHits, // 对其进行操作。另外LogDelete要修改数据库表中的有效位,而LogRename就需要修改数据库表中相关文件日志的文件名字段 public static void LogHits(SPFile file, string userName, SqlConnection connection)
{ SPWeb oldWeb = file.Item.ParentList.ParentWeb; string webUrl = oldWeb.Url; SPSite site = new SPSite(webUrl); SPWeb web = site.OpenWeb(); try
{ web.AllowUnsafeUpdates = true; SPList hitList = web.Lists["DocHits"];//获得当前文档库的列表 SPQuery query = new SPQuery(); query.Query = "<Where><Eq><FieldRef Name='Title'/><Value Type='Text'>" + file.Url + "</Value></Eq></Where>"; SPListItemCollection items = hitList.GetItems(query); if(items.Count == 0) // new item,如果没有则创建新条目 { SPListItem item = hitList.Items.Add(); item["Title"] = file.Url; item["Hits"] = 1; item.Update(); } else // existed item,如果有则原条目+1 { SPListItem item = items[0]; item["Hits"] = Convert.ToInt32(item["Hits"]) + 1; item.Update(); } } catch { } LogEvent(oldWeb, file.Url, "", (int)DocLogType.Hits, userName, connection);
} /// <summary>
/// Log the CheckIn Event /// </summary> /// <param name="file">file object of the file</param> /// <param name="userName">loginName of the User</param> /// <param name="connection">the connection of the DB</param> public static void LogCheckIn(SPFile file, string userName, SqlConnection connection) { SPWeb web = file.Item.ParentList.ParentWeb; LogEvent(web, file.Url, "", (int)DocLogType.CheckIn, userName, connection); } /// <summary> /// Log the CheckOut Event /// </summary> /// <param name="file">file object of the file</param> /// <param name="userName">loginName of the User</param> /// <param name="connection">the connection of the DB</param> public static void LogCheckOut(SPFile file, string userName, SqlConnection connection) { SPWeb web = file.Item.ParentList.ParentWeb; LogEvent(web, file.Url, "", (int)DocLogType.CheckOut, userName, connection); } /// <summary> /// Log the UncheckOut Event /// </summary> /// <param name="file">file object of the file</param> /// <param name="userName">loginName of the User</param> /// <param name="connection">the connection of the DB</param> public static void LogUnCheckOut(SPFile file, string userName, SqlConnection connection) { SPWeb web = file.Item.ParentList.ParentWeb; LogEvent(web, file.Url, "", (int)DocLogType.UnCheckOut, userName, connection); } /// <summary>
/// Log the Update Event /// </summary> /// <param name="file">file object of the file</param> /// <param name="userName">loginName of the User</param> /// <param name="connection">the connection of the DB</param> public static void LogUpdate(SPFile file, string userName, SqlConnection connection) { SPWeb web = file.Item.ParentList.ParentWeb; LogEvent(web, file.Url, "", (int)DocLogType.Update, userName, connection); } /// <summary>
/// Log the Insert Event /// </summary> /// <param name="file">file object of the file</param> /// <param name="userName">loginName of the User</param> /// <param name="connection">the connection of the DB</param> public static void LogInsert(SPFile file, string userName, SqlConnection connection) { SPWeb web = file.Item.ParentList.ParentWeb; LogEvent(web, file.Url, "", (int)DocLogType.Insert, userName, connection); } /// <summary>
/// Log the Delete Event /// </summary> /// <param name="web">web object of the deleted file</param> /// <param name="filename">file url of the deleted file, like "Shared Documents/sample.doc"</param> /// <param name="userName">loginName of the User</param> /// <param name="connection">the connection of the DB</param> public static void LogDelete(SPWeb web, string filename, string userName, SqlConnection connection) { LogEvent(web, filename, "", (int)DocLogType.Delete, userName, connection); // Modify Valid Field string commandStr = String.Format("UPDATE DocumentLog SET Valid=0 WHERE SiteId='{0}' AND WebId='{1}' AND FileName=N'{2}' AND Valid=1", web.Site.ID.ToString(), web.ID.ToString(), filename.ToLower()); SqlCommand command = new SqlCommand(commandStr, connection); command.ExecuteNonQuery(); } /// <summary>
/// Log the Copy Event /// </summary> /// <param name="file">file object of the new file</param> /// <param name="fileFrom">file Url of the source file</param> /// <param name="userName">loginName of the User</param> /// <param name="connection">the connection of the DB</param> public static void LogCopy(SPFile file, string fileFrom, string userName, SqlConnection connection) { SPWeb web = file.Item.ParentList.ParentWeb; LogEvent(web, file.Url, fileFrom, (int)DocLogType.Copy, userName, connection); } /// <summary>
/// Log the Rename Event /// </summary> /// <param name="file">file object of the new file</param> /// <param name="oldName">file Url of the old file</param> /// <param name="userName">loginName of the User</param> /// <param name="connection">the connection of the DB</param> public static void LogRename(SPFile file, string oldName, string userName, SqlConnection connection) { SPWeb web = file.Item.ParentList.ParentWeb; LogEvent(web, file.Url, oldName, (int)DocLogType.Move, userName, connection); // Modify FileName Field string commandStr = String.Format("UPDATE DocumentLog SET FileName=N'{0}' WHERE SiteId='{1}' AND WebId='{2}' AND FileName=N'{3}' AND Valid=1", file.Url.ToLower(), web.Site.ID.ToString(), web.ID.ToString(), oldName.ToLower()); SqlCommand command = new SqlCommand(commandStr, connection); command.ExecuteNonQuery(); } /// <summary>
/// Log An Event of the Document,公用的Log事件函数,用来想数据库表写入有关的日志条目 /// </summary> /// <param name="web">web object of the file</param> /// <param name="fileName">file Url, like "Shared Documents/Dir/DocA.doc"</param> /// <param name="fileNameBefore">old file Url</param> /// <param name="type">event type</param> /// <param name="userName">loginName of the User</param> /// <param name="connection">the connection of the DB</param> private static void LogEvent(SPWeb web, string fileName, string fileNameBefore, int type, string userName, SqlConnection connection) { string commandStr = String.Format("INSERT INTO DocumentLog VALUES('{0}','{1}',N'{2}',{3},'{4}',N'{5}',1,N'{6}')", web.Site.ID.ToString(), web.ID.ToString(), fileName.ToLower(), (int)type, DateTime.Now.ToString(), userName.ToLower(), fileNameBefore.ToLower()); SqlCommand command = new SqlCommand(commandStr, connection); int i = command.ExecuteNonQuery(); // StreamWriter sw = new StreamWriter(@"c:\DocumentLog.log", true); // sw.WriteLine(commandStr); // sw.WriteLine("Change:{0}", i); // sw.Close(); } /// <summary>
/// Get the Hits Count of a Document,获得一个文档的点击数 /// </summary> /// <param name="file">file object of the document</param> /// <param name="connection">the connection of the DB</param> /// <returns>Hits Count of the Document</returns> public static int GetDocHits(SPFile file, SqlConnection connection) { SPWeb web = file.Item.ParentList.ParentWeb; string commandStr = String.Format("SELECT COUNT(*) FROM DocumentLog WHERE WebId='{0}' AND FileName=N'{1}' AND OperationType=0 AND Valid=1", web.ID.ToString(), file.Url.ToLower()); SqlCommand command = new SqlCommand(commandStr, connection); int count = Convert.ToInt32(command.ExecuteScalar()); return count; } /// <summary>
/// Get Logs of Document, whether include Normal Hists log or not /// </summary> /// <param name="file">file object of the document</param> /// <param name="includeNormalHits">whether include Normal Hits</param> /// <param name="connection">the connection of the DB</param> /// <returns>DataTable contains the logs</returns> public static DataTable GetDocLog(SPFile file, bool includeNormalHits, SqlConnection connection) { SPWeb web = file.Item.ParentList.ParentWeb; return GetDocLog_Internal(web, file.Url, includeNormalHits, true, connection); } /// <summary>
/// Get All Logs of Document,获得一个文档的日志,但是不包括被删除文档的日志 /// </summary> /// <param name="file">file object of the document</param> /// <param name="connection">the connection of the DB</param> /// <returns>DataTable contains the logs</returns> public static DataTable GetDocLog(SPFile file, SqlConnection connection) { return GetDocLog(file, true, connection); } /// <summary>
/// Get All Logs of a file, includes the deleted file,获得所有日志,包括被删除文件的日志 /// </summary> /// <param name="web">web object of the file</param> /// <param name="fileName">file url, like "Shared Documents/sample.doc"</param> /// <param name="includeNormalHits">whether include the Normal Hits or not</param> /// <param name="connection">the connection of the DB</param> /// <returns>DataTable contains the logs</returns> public static DataTable GetDocLog(SPWeb web, string fileName, bool includeNormalHits, SqlConnection connection) { return GetDocLog_Internal(web, fileName, includeNormalHits, false, connection); } /// <summary>
/// private method to get the logs,获得日志的内部私有方法 /// </summary> /// <param name="web">web object of the file</param> /// <param name="fileName">file url, like "Shared Documents/sample.doc"</param> /// <param name="includeNormalHits">whether include the Normal Hits or not</param> /// <param name="valid">whether include the deleted file</param> /// <param name="connection">the connection of the DB</param> /// <returns>DataTable contains the logs</returns> private static DataTable GetDocLog_Internal(SPWeb web, string fileName, bool includeNormalHits, bool valid, SqlConnection connection) { // Select Command string str = "SELECT OperationType,OperationTime,Operator,FileNameBefore FROM DocumentLog " + "WHERE WebId='{0}' AND FileName=N'{1}'"; if(valid) str += " AND Valid=1";//如果true,不包含已删除文件,反之包含 if(!includeNormalHits)//是否包含普通点击(纯查看) str += " AND OperationType>0"; string commandStr = String.Format(str, web.ID.ToString(), fileName.ToLower()); SqlCommand command = new SqlCommand(commandStr, connection); // Data Adapter
SqlDataAdapter adapter = new SqlDataAdapter(command); // DataSet
DataSet ds = new DataSet("DocumentLog"); adapter.Fill(ds); return ds.Tables[0];
} #region Impersonate protected static WindowsIdentity CreateIdentity(string User, string Domain, string Password) { // The Windows NT user token. IntPtr tokenHandle = new IntPtr(0); const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_NETWORK = 3; tokenHandle = IntPtr.Zero;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(User, Domain, Password, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, ref tokenHandle); if (false == returnValue)
{ int ret = Marshal.GetLastWin32Error(); throw new Exception("LogonUser failed with error code: " + ret); } System.Diagnostics.Debug.WriteLine("Created user token: " + tokenHandle);
//The WindowsIdentity class makes a new copy of the token.
//It also handles calling CloseHandle for the copy. WindowsIdentity id = new WindowsIdentity(tokenHandle); CloseHandle(tokenHandle); return id; } [DllImport("advapi32.dll", SetLastError=true)]
private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); [DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private extern static bool CloseHandle(IntPtr handle); #endregion } } 7월 30일 MDMS中文档日志事件代码的分析这段代码主要是利用SharePoint Services中可以捕获的8种文档事件(签入,签出,取消签出,复制,删除,插入,移动,修改),对数据库进行操作。
主要是对DocumentLog表插入相关的纪录。如果出现删除操作,则在DocumentLog表中将相应文件的有效位置0,若出现文件改名操作,则在 DocumentLog表中将相应文件的文件名和先前文件名的字段做相应的修改。 using System;
using System.Configuration; using System.Data.SqlClient; using System.IO; using System.Security.Principal; using System.Runtime.InteropServices; using Microsoft.SharePoint; using Microsoft.SharePoint.Administration; namespace DMS
{ //定义文档日志类型的枚举类别 public enum DocLogType { Hits, // 0 Insert, // 1 Delete, // 2 Copy, // 3 Move, // 4 CheckIn, // 5 CheckOut, // 6 UnCheckOut, // 7 Update // 8 }; /// <summary>
/// Class1 的摘要说明。 /// </summary> public class DocumentLogHandler : IListEventSink { #region IListEventSink 成员 public void OnEvent(SPListEvent listEvent)
{ //以下字段从web.config文件中读取 string sAdminName = ConfigurationSettings.AppSettings["AdminName"]; string sDomainName = ConfigurationSettings.AppSettings["DomainName"]; string sPassword = ConfigurationSettings.AppSettings["AdminPassword"]; WindowsImpersonationContext wic = CreateIdentity(sAdminName, sDomainName, sPassword).Impersonate();
// Get the DataBase,打开数据库
SPGlobalAdmin global = new SPGlobalAdmin(); SPContentDatabase db = global.OpenVirtualServer(new Uri(listEvent.Site.Url)).ContentDatabases[0]; string conStr = String.Format("Initial Catalog={0};Data Source={1};Integrated Security=SSPI;", db.Name, db.Server); SqlConnection connection = new SqlConnection(conStr); connection.Open(); // Get objects,获取网站和网页对象
SPSite site = listEvent.Site; SPWeb web = site.OpenWeb(); //Type即文档Log类型,以下操作获取的是文档事件的类型
DocLogType type = DocLogType.Hits; switch(listEvent.Type) { case SPListEventType.CheckIn: type = DocLogType.CheckIn; break; case SPListEventType.CheckOut: type = DocLogType.CheckOut; break; case SPListEventType.UncheckOut: type = DocLogType.UnCheckOut; break; case SPListEventType.Copy: type = DocLogType.Copy; break; case SPListEventType.Delete: type = DocLogType.Delete; break; case SPListEventType.Insert: type = DocLogType.Insert; break; case SPListEventType.Move: type = DocLogType.Move; break; case SPListEventType.Update: type = DocLogType.Update; break; } //确定日志纪录中的当前文件名和先前文件名 string fileName = listEvent.UrlAfter; string fileNameBefore = ""; if(listEvent.Type == SPListEventType.Delete) fileName = listEvent.UrlBefore; if(listEvent.Type == SPListEventType.Copy || listEvent.Type == SPListEventType.Move) fileNameBefore = listEvent.UrlBefore; // Add a record to DataBase,插入日志记录
string commandStr = String.Format("INSERT INTO DocumentLog VALUES('{0}','{1}',N'{2}',{3},'{4}',N'{5}',1,N'{6}')", site.ID.ToString(), web.ID.ToString(), fileName.ToLower(), (int)type, DateTime.Now.ToString(), listEvent.UserLoginName.ToLower(), fileNameBefore.ToLower()); SqlCommand command = new SqlCommand(commandStr, connection); command.ExecuteNonQuery(); //将操作同时也到硬盘上的一个log文件里去 // StreamWriter sw = new StreamWriter(@"c:\DocumentLog.log", true); // sw.WriteLine(commandStr); // sw.Close(); // If Delete, Modify the 'Valid' Field of the records of the file,如果是删除操作,修改有效位
if(listEvent.Type == SPListEventType.Delete) { commandStr = String.Format("UPDATE DocumentLog SET Valid=0 WHERE SiteId='{0}' AND WebId='{1}' AND FileName=N'{2}' AND Valid=1", site.ID.ToString(), web.ID.ToString(), fileName.ToLower()); command = new SqlCommand(commandStr, connection); command.ExecuteNonQuery(); // Delete Hits,删除DocHits列表相应条目
try { SPList hitList= web.Lists["DocHits"]; SPQuery query = new SPQuery(); query.Query = "<Where><Eq><FieldRef Name='Title'/><Value Type='Text'>" + listEvent.UrlBefore + "</Value></Eq></Where>"; SPListItemCollection items = hitList.GetItems(query); foreach(SPListItem item in items) { hitList.Items.DeleteItemById(item.ID); } } catch { } // sw = new StreamWriter(@"c:\DocumentLog.log", true); // sw.WriteLine(commandStr); // sw.Close(); } // If Rename, Modify the FileName Field to the new name of the file,如果是重命名,则修改日志中的文件名
if(listEvent.Type == SPListEventType.Move) { commandStr = String.Format("UPDATE DocumentLog SET FileName=N'{0}' WHERE SiteId='{1}' AND WebId='{2}' AND FileName=N'{3}' AND Valid=1", fileName.ToLower(), site.ID.ToString(), web.ID.ToString(), fileNameBefore.ToLower()); command = new SqlCommand(commandStr, connection); command.ExecuteNonQuery(); // sw = new StreamWriter(@"c:\DocumentLog.log", true); // sw.WriteLine(commandStr); // sw.Close(); } connection.Close();
wic.Undo(); } #endregion
//-----Impersonate -----,生成Windows身份标识的函数
protected static WindowsIdentity CreateIdentity(string User, string Domain, string Password) { // The Windows NT user token. IntPtr tokenHandle = new IntPtr(0); const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_NETWORK = 3; tokenHandle = IntPtr.Zero;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(User, Domain, Password, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, ref tokenHandle); if (false == returnValue)
{ int ret = Marshal.GetLastWin32Error(); throw new Exception("LogonUser failed with error code: " + ret); } System.Diagnostics.Debug.WriteLine("Created user token: " + tokenHandle);
//The WindowsIdentity class makes a new copy of the token.
//It also handles calling CloseHandle for the copy. WindowsIdentity id = new WindowsIdentity(tokenHandle); CloseHandle(tokenHandle); return id; } [DllImport("advapi32.dll", SetLastError=true)]
private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); [DllImport("kernel32.dll", CharSet=CharSet.Auto)] private extern static bool CloseHandle(IntPtr handle); } } 5월 16일 Asp.Net中Cache操作类zz/// <head> namespace Common #region 方法 //实现“一键一值”存储方法,最普通的存储方法 //插入"一键多值"方法 if(enumInfType == StorageInfType.UserInf) //手动移除“一键一值”对应的值 //此方法在值失效之前调用,可以用于在失效之前更新数据库,或从数据库重新获取数据 #endregion Asp.Net性能优化zz(一).选择会话状态存储方式 3.尽量少用: web.config文件详解zz(一).Web.Config是以XML文件规范存储,配置文件分为以下格式 1.配置节处理程序声明 特点: 位于配置文件的顶部,包含在<configSections>标志中。 2.特定应用程序配置 特点: 位于<appSetting>中。 可以定义应用程序的全局常量设置等信息. 3.配置节设置 特点: 位于<system.Web>节中,控制Asp.net运行时的行为. 4.配置节组 特点: 用<sectionGroup>标记,可以自定义分组,可以放到<configSections>内部或其它<sectionGroup>标记的内部. (二).配置节的每一节 1.<configuration>节 根元素,其它节都是在它的内部. 2.<appSetting>节 此节用于定义应用程序设置项。对一些不确定设置,还可以让用户根据自己实际情况自己设置 用法: I. <appSettings> <add key="Conntction" value="server=192.168.85.66;userid=sa;password=;database=Info;"/> <appSettings> 定义了一个连接字符串常量,并且在实际应用时可以修改连接字符串,不用修改程式代码. II.<appSettings> <add key="ErrPage" value="Error.aspx"/> <appSettings> 定义了一个错误重定向页面. 3.<compilation>节 格式: <compilation defaultLanguage="c#" debug="true" /> I.default language: 定义后台代码语言,可以选择C#和VB.net两种语言. IIdebug : 为true时,启动aspx调试; 为false不启动aspx调试,因而可以提高应用程序运行 时的性能。 一般程序员在开发时设置为true,交给客户时设置为false. 4.<customErrors>节 格式: <customErrors mode="RemoteOnly" defaultRedirect="error.aspx" <error statusCode="440" redirect="err440page.aspx"/> <error statusCode="500" redirect="err500Page.aspx"/> /> I.mode : 具有On,Off,RemoteOnly 3种状态。On表示始终显示自定义的信息; Off表示始终显示详细的asp.net错误信息; RemoteOnly表示只对不在本地Web服务器上运行的用户显示自定义信息. II.defaultRedirect: 用于出现错误时重定向的URL地址. 是可选的 III.statusCode: 指明错误状态码,表明一种特定的出错状态. IV. redirect:错误重定向的URL. 5.<globalization>节 格式: <globalization requestEncoding="utf-8" responseEncoding="utf-8" fileEncoding="utf-8" /> I.requestEncoding: 它用来检查每一个发来请求的编码. II.responseEncoding: 用于检查发回的响应内容编码. III.fileEncoding: 用于检查aspx,asax等文件解析的默认编码. 6.<sessionState>节 格式: <sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false" timeout="20" /> I.mode: 分为off,Inproc,StateServer,SqlServer几种状态 这里有详细介绍此属性: http://blog.csdn.net/chengking/archive/2005/10/27/518079.aspx II. stateConnectionString :指定Asp.net应用程序存储远程会话状态的服务器名,默认为本机 III.sqlConnectionString: 当用会话状态数据库时,在这里设置连接字符串 IV. Cookieless: 设置为true时,表示不使用cookie会话状态来标识客户; 否则,相反. V. TimeOut: 用来定义会话状态存储的时间,超过期限,将自动终止会话. 7.<authentication>节 格式: <authentication mode="Forms"> <forms name=".ASPXUSERDEMO" loginUrl="Login.aspx" protection="All" timeout="30"/> </authentication> <authorization> <deny users="?"/> </authorization> I.Windows: 使用IIS验证方式 II.Forms: 使用基于窗体的验证方式 III.Passport: 采用Passport cookie验证模式 IV.None: 不采用任何验证方式 里面内嵌Forms节点的属性涵义: I.Name: 指定完成身份验证的Http cookie的名称. II.LoginUrl: 如果未通过验证或超时后重定向的页面URL,一般为登录页面,让用户重新登录 III.Protection: 指定 cookie数据的保护方式. 可设置为: All None Encryption Validation四种保护方式 a. All表示加密数据,并进行有效性验证两种方式 b. None表示不保护Cookie. c. Encryption表示对Cookie内容进行加密 d. validation表示对Cookie内容进行有效性验证 IV. TimeOut: 指定Cookie的失效时间. 超时后要重新登录. 4월 30일 zz历数10年经历的Microsoft技术(五)五、认识Application对象 如果你熟悉Microsoft Office,你应该进一步的剖析这个大型软件,Microsoft Office中几乎每个程序都是可二次开发的,这一点得益于Microsoft Office内置的二次开发机制,一个是基于COM机制的VBA模型,另一个是基于.NET框架的托管模型:Visual Studio Tools for Office。作为一名程序员,你应当在技术角度解析Office的技术结构。Microsoft的大多数软件的对象结构可以通过Visual Studio提供的工具OLE/COM Object Viewer考察其类型库得到,通过引用类型库,你甚至可以得到描述对象信息的C++头文件。这样做真是好处多多。一个典型的Office通常都有一个Application对象(或其他一个与之相当的对象),这个对象相当于软件枢纽,在这里,我们不讨论Office,借此话题说说Application对象。大多数支持扩展(Addin、Plugin)的软件都存在类似的构造。通常,一个系统得Application对象或者是一个COM对象,或者是一个.NET对象,如果你的系统存在这类对象,你的系统就基本具备支持Addin、Plugin的机制了。一个理想的做法就是在一个MFC系统中,内置一个ATL对象或.NET对象,稍后我们给出方案如何做到这一点。设计Application对象的关键是如何规划这个对象的属性、方法、事件。如果你希望系统具备良好的扩展性,Application对象是十分关键的,这也是构架艺术的体现。所谓Addin(Plugin),是系统运行时根据需要加载的对象库,Addin(Plugin)之所以可以扩展系统,关键的因素就是系统加载Addin(Plugin)时,将Application对象传递给Addin(Plugin)库,设想一下,如果Application恰到好处的触发了系统事件,而Addin(Plugin)库如愿的解释了事件,一个Addin(Plugin)库的任务不就OK了吗!因此Application对象是系统设计的关键。 如果你精通ATL对象,在你的MFC系统中添加一个ATL对象,这个任务可以用VC Wizard完成。你已经接受了一个事实,就是MFC程序中存在一个CXXXApp对象(CWinApp的派生类),现在你要做的是增加一个对应得ATL对象。这个对象可以在CXXXApp::InitInstance()中创建,如果ATL对象的类是CXXXAppObject,建议你在CXXXApp对象对象中增加一个成员变量,例如:CComObject<CXXXAppObject>* m_pAppObj,然后可以入下初始化m_pAppObj: m_pAppObj = new CComObject<CXXXAppObject>; 注意程序结束时在CXXXApp::ExitInstance()中释放m_pAppObj,语句如下: delete m_pAppObj; 你可以将系统得关键属性设置成CXXXAppObject的属性,例如系统得标题、是否为多文档等等。系统希望外部调用的功能可以实现为CXXXAppObject的方法,这一点取决于你的需要。系统需要外部扩展的功能,表现为CXXXAppObject的事件,关键是在恰当的位置触发事件以及提供的事件参数。例如,你可以在CXXXApp::InitInstance()触发应用程序开始的事件OnStartUp,Plugin捕获事件后,可以进行特定的初始化(身份确认、初始信息查询等等); 你可以在CXXXApp::ExitInstance()触发应用程序结束事件,Plugin捕获事件后,处理用户需要的系统退出工作。所有的设计取决于具体设计。 如何加载Plugin,是一个有趣的问题,如果Plugin实现为一个COM范畴(Category),可以运用COM技术枚举这个Category;可以将Plugin安装到一个特定目录,也可以通过注册表。Plugin的实现可以用COM技术、也可以用.NET框架。适当的机会我会提供例子…… zz历数10年经历的Microsoft技术(四)四、为什么用MFC? 经过若干年的竞争,Borland 的OWL几乎消失了,这个OWL是个非常漂亮的C++类库,在Borland C++ 3.1风光无限的年代,OWL真正的做到了独领风骚。然而,Borland C++ 4.0错过了进入32位程序的最佳时机,BC 4.0推出后不久,迎来了Win95,Borland仓促上阵,以一个小的“Pack”使得BC4可以编译基于Win4的程序,当时的Visual C++是2.0版,支持Window16的版本为Visual C++1.51,有意思的是Borland可以用同一个编译器同时支持Win16、Win32,而Microsoft却不得不为Win16、Win32提供不同的编译器。然而,非正式版本的Visual C++ 2.1与Visual C++ 2.2却悄悄地支持了Win95的最新特征,即Win95新提供的一组公共控件,在我的印象中,Borland对Win95新特征的支持不利使得MFC与OWL的距离极大的缩短了。稍后到来的Borland C++ 4.5没有改变这个状况,尽管Borland C++ 5.0同时支持OWL与MFC,可是败象已经显露,Borland C++非常遗憾的只走到了5.5版。C++ Builder虽然形式上引入了Delphi的VCL库,可是许多C++程序员并不买账,因为许多以C++为乐的人更喜欢以编辑的模式进行编码。Visual C++ 4.0的出现,在C++这个战场上,Borland开始落败了。 MFC发展到今天,已经十多年了,尽管褒贬不一,但可以肯定,十几年的技术积累已经奠定了MFC的生存基础,即使Microsoft的长角发布,MFC也不能推出Windows的舞台,事实上,长角(Longhorn)之后的Visual Studio .NET仍将MFC作为一个重要的组成部分,在今年的Visual Studio .NET 2005中,MFC在C++中的位置依然如故。MFC的未来,应该不必担心,只要你深入考察.NET类库,你会发现,MFC的许多思想机制正悄然进入.NET,与此同时,Microsoft的第三方盟友十多年来已为MFC开发了大量的扩展库,如果Microsoft是船,第三方盟友就是载舟之水。许多人认为MFC不发展了,其实是一种错觉,Visual C++ 6的界面十分经典,特别是其中的Docking控制条机制,其实Visual C++ 6的IDE完全就是MFC写的,可是MFC类库中控制条相关的类功能很弱,为什么?你会看到许多与Microsoft友好的公司,他们很快的在MFC基础上实现了Visual C++ 6 的Docking机制,这就是Microsoft的高明之处,Microsoft很会给盟友提供机会,其一贯的做法就是在自己的商品化产品中预先提供一些有趣的特征,使得其他一些公司进行模仿以带动用户群体。Borland不具备这样的储备。MFC第三方市场的繁荣,得益于Microsoft的策略与明智。MFC可否跨平台?理论上完全可以,Microsoft不做,也是策略,但是有许多重要的产品Microsoft却默许MFC移植到其他平台,事实上,Microsoft的合作伙伴之一Mainsoft公司(Windows源码就是从这家公司流失的),几年来就是负责移植MFC程序移植到UINIX、Linux、AIX等操作系统之上。 新版的Visual C++中MFC已经支持.NET开发了,MFC与ATL的协作更好了。根据我的经验,MFC、ATL与.NET库三者完全可以融合在一起综合应用到实际的开发工作中去,如果你是MFC行家,我希望ATL与.NET库能成为你的忠实的左右手。那么有没有同时支持MFC、ATL与.NET库的程序?当然有,Visual Studio .NET IDE就是!而且Visual Studio .NET IDE还支持用ATL与.NET库扩展的Addin,如果你希望用MFC管理ATL与.NET库,请继续支持我!(待续) zz历数10年经历的Microsoft技术(三)三、向Microsoft学习 无论从什么角度评价Microsoft,我觉得Microsoft是值得我们学习的,如果说生活在这个时代有Microsoft存在是一场灾难,你就应该痛恨这个家伙,但你首先要向这个家伙学习!我无意为Microsoft歌功颂德,我只是想说出十几年我对Microsoft技术的感受。 Microsoft在研究式的开发中受益极大,如果你有兴趣,你可以访问http://research.microsoft.com/,虽然部分中国公司也有研究院,但与Microsoft相比,真有“米粒之珠,也放光华?”的感觉。2003年,我在北京的一个地方现场体验了Microsoft亚洲研究院的招聘会,我看到中国的精英们进入Microsoft的渴望,事实上,在中国大陆,Microsoft亚洲研究院的人力资源已经延伸到各著名高校的相关专业的核心层,我感到,Microsoft几乎不需要“求贤”,因为,只要Microsoft需要,精英们会“蜂拥而至”,每个人都有“可以理解”的理由而向往那个地方,如果为搞数学研究蜂拥到加州大学,我觉得可以理解,因为那里有数学土壤,出了成果国人也会感到自豪,因为“科学无国界”。技术是否有国界?不知道是否有定论?!想想DVD等技术专利给国内业界带来的灾难,不知道应不应该痛定思痛,在Microsoft校园招聘现场的气氛中,我似乎明白了为什么国人“原创技术”少得可怜。我读过几本Microsoft亚洲研究院的高手写的书,明显可以看出,Bill gate 是他们的精神领袖以及他们对Microsoft的虔诚,国内的研究机构应当研究一下Microsoft的用人之道,Microsoft好像是三国里的人物,不知是刘备还是曹操,或者二者的混合物。我经常路过西格玛大厦,第一次西格玛大厦进入真有“朝圣”的感觉,也与Microsoft中国的几个层次的人打过交道,各中滋味实在一言难尽。 在Office大战中,国产软件的确在一些方面与Microsoft进行较量,其实给人的感觉很勉强,界面上的似是而非,或用户习惯方面的接近并不能解决根本的问题,一个好的软件开发人员必须是一个软件使用的高手,很难想象一个软件操作水平很拙劣的开发人员能开发出高水平的软件,我最早使用的软件之一就是Microsoft Word,当时的版本是2.0,大概是1992年的事情,给我留下深刻印象的是集成于Word中的Word Basic,后来,我接触到Excel 3.0,不出所料,Excel中集成的是Excel Basic,后来使用的Access中自然内置Access Basic 1.0,在这些软件集成捆绑成Office之前,我就感觉这些产品的构思十分了不起,很具有Microsoft的风格,因为你知道,即使是一个DOS,Microsoft都要提供一个内置的QBasic或GW Basic。虽然关于Microsoft的产品评论很多,作为一个技术人员,我认为Microsoft的产品构思绝对是第一流的,从1994年早期的Office系列到1997年形成的Office 4.2,我认为,技术构思上均领先于我国2002年以后的Office产品,你听说过如下说法吗?“Dos 作为操作系统的时代,Windows是应用软件;Windows是操作系统时,Office成为Dos时代的Windows;那么如果按此规律,Office会不会替代Windows而成为操作系统?”,现在在开发领域Visual Studio( .NET)正在成为另一个Office,你注意到了吗?控制Visual Studio( .NET)集成开发环境的仍然是一个Basic语言引擎(Visual Basic .NET)。 与许多公司不同的是,在技术体系上,Microsoft几乎所有的产品是息息相关的,Windows、Office、Visual Studio .NET虽然各不相同,但公共的核心即将形成,我们已经看到,核心组件方面,Office与Visual Studio .NET日渐趋于一致,例如Microsoft正在将Office 2003的核心组件VBA 6.X逐步用新的Visual Studio Tools for Office替代,而我们依然在一些似是而非的现象上与Microsoft的产品比较差距,国家采购或政府采购支持的公司,不去钻研核心技术,只是急功近利的采用短期行为急于与Microsoft相争,不知是否有蚍蜉撼树的感觉,个人的体验是,先学习Microsoft,踏踏实实的学,了解Microsoft,深入的了解,然后再喊口号。(未完) zz历数10年经历的Microsoft技术(二) 二、关于Microsoft 关于Microsoft,有无数的人要对这个名字叙说感觉,这个令人讨厌的名字!不知道是喜欢还是憎恶,你是程序员,你的心思可能就要因Microsoft的存在而动,即使你用Linux,你可能也是因为Microsoft技术因素。多少年来,这个名字每天都出现在你、我、他的面前,因为你不得不面对Windows的存在,可是你憎恨这个名字吗?你讨厌这个名字吗?我不知道是否已经对这个名字麻木了。1998年我个人订了Microsoft MSDN Universal 版,我开始比较全面接触这个公司的开发技术,你可以想象,1998年当你面对上百张技术光盘的时候,你就知道什么叫做“厚度”,当我们有时说出“赶上”或 “达到”Microsoft某些产品的水平的时候,可能我们缺乏对这个公司“厚度”的真实了解。进入MSDN,我感觉Microsoft简直不是一个“公司”,而是(或者正在形成)一个“社会”。当时著名的技术网站http://www.codeguru.com全部的技术资料是可下载的(那个时候http://www.codeguru.com提供整个网站内容下载服务,大约3M左右),大名鼎鼎的www.codeproject.com还不存在。一开始,我始终潜意识在技术上对比Microsoft与Borland,应当说技术上Borland不比Microsoft弱,即使现在也有人持有这个看法,可是为什么Borland走到今天这个地步?而Microsoft却如日中天?若干年前,这两个公司竞争何等激烈,而现在却是另一番“合作”的景象?可能很多人想过,如果Borland不存在,对Microsoft不是更有力吗?其实Microsoft可能精通中国历史,读过《三国》、十分了解战国时期的中国,其实Borland形式上的存在,对Microsoft是十分有利的,至少形式上还有竞争对手,而事实上Borland已经受控于Microsoft(Microsoft是Borland的大股东)。你可以看到一些微妙的现象:Borland为Microsoft提供了大量的人才,其中包括Delphi总设计师以及Borland C++编译器的核心成员;同时也为Microsoft .NET提供强有力的护航服务(看看C# Builder、Delphi .NET)。1998年Microsoft 的COM技术基本已经成熟,这个技术使人感到震撼,当时Microsoft的对手们提出“OpenDoc”用于对抗“COM”,你看看“OpenDoc”阵营的几个成员:IBM、Apple、Borland、Novell,你会感到这个阵营十分豪华、强大。但结果却差强人意,“OpenDoc”无疾而终,而“COM”依然生机勃勃。 有人说“COM”没落了,那么就太不了解Microsoft了。在与“OpenDoc”的竞争中,“COM”是个彻底的胜利者,在与“Java”的竞争中,“COM”成功的进化了,在这个过程中Microsoft体现了强大的吸收能力、以及无法想象的韧劲。.NET只不过是COM的“别名”而已。对于一个经验丰富的C++程序员而言,.NET就是COM的进化,而Microsoft内部.NET就是“COM 3.0”(OLE2就是COM 2.0),而“CLR”就是一个不择不扣的COM对象。曾经有人问我,既然牛顿时代就奠定了基础(想想著名的牛顿-莱布尼茨公式),几百年后的今天,数学还研究“微积分”吗?回答当然是依然在研究!“微积分”早期是针对函数的,现代“微积分”是针对“流形(Manifold)、纤维丛(Fiber Bundle)”的,概念深奥了,可是基本思想不变,只是“微积分”的思想得到合理的延拓与进化,你了解Microsoft吗?Microsoft Research有一批超一流的数学家在为Microsoft工作,其中一些是斐尔兹奖的得主,Microsoft正在实现如同“微积分”进化到“微分流形”一样将“COM”进化到“.NET”。从科学概念角度上分析COM与Java,可能COM更全面、精确,从实现的成熟度上Java可能更成熟,可是你看到,Microsoft正在不紧不慢的追赶。Microsoft令人联想起战国时期的强秦。 战国时期的秦国,采取“远交近攻”“抚弱掠强”等措施傲视六国,今天的Microsoft也是这样,VB1.0时,Microsoft推出“VBX”控件技术,众多的小公司得以生存,Microsoft自己不开发“VBX”组件,同样“VBX”进化为“OCX”时,Microsoft并不十分强大,可是这种试探得到众多小公司的响应。1997年Microsoft Office 97、1998年Microsoft推出Visual Studio 6.0,给众多中、小公司提供了生存、发展的机会,例如Microsoft Office 97中集成了Visual Basic for Application 5.0,这项技术使得几百家软件开发商与Microsoft签署了VBA技术许可协议,即使AutoDesk这样的公司都与Microsoft签署了这个协议,这个协议使得每个集成VBA的产品的给个用户许可为Microsoft付40$的许可费,如果你了解VSIP(Visual Studio Integration Protocol)协议,以及有多少公司签订了VSIP协议,你就真正感觉到Microsoft的可怕;Microsoft Office 97、Visual Studio 6.0的用户界面十分漂亮,为什么Microsoft自己的开发工具不提供类似的软件组件?你看到众多第三方的Microsoft盟友纷纷推出自己的界面库以模仿Microsoft,他们不会反对Microsoft,因为他们已经形成了使得Microsoft以及这些公司得以生存的生态圈。 Microsoft的技术储备有多少,Microsoft之外的人很难说清楚,Microsoft中国公司也未必了解多少,1999年WTL类库刚刚出现的时候,人们就希望WTL能得到官方的支持,或授权给一个Microsoft之外的一个公司(你能想象出Borland C++ 5.0内置的ActiveX开发机制是基于Microsoft ATL类库吗?),直到今天,WTL依然如故,我们完全相信,如果Microsoft强力推广WTL,WTL完全可以流行,可是Microsoft不缺类似的技术,类似的类库还有BCL(Base Control Library,一个用于开发轻量级ActiveX控件的类库),Microsoft还有一个基于ATL的类库,这个类库用于开发ActiveX Designer,ActiveX Designer是绝大多数程序员不了解得一类对象,如果你熟悉Office开发,你知道Office VBA 中有一类对象,即Form2,此外VB6.0 中的报表设计器(以及著名的Active Reporter),都属于此类对象,用这个类库,你可以为VB6.0以及集成VBA的系统提供定制化的可视化设计机制等等,如今ActiveX Designer已经演化为集成于Visual Studio .NET中的设计器。(未完) zz历数10年经历的Microsoft技术(一)一、初识MFC 我最初知道MFC大概是在1993年,那个时候Visual C++还没面世,当时Microsoft的C++编译器还很弱,官方的名字是Microsoft C/C++ 7.0,MFC的版本是1.0,几乎没有引起什么反响,那个时期最好的C++开发环境是Borland C++ 3.1,其实,大概是1992年11月份,一个偶然的机会,我领略到Borland公司的厉害,记不得在什么地方,我看到一个绝妙的集成开发环境,即Turbo C++ 3.0 for Windows,这是我记忆中第一个真正的Windows环境下的C++集成开发环境,那种激动的感觉至今仍记忆犹新,不客气的说,当时至少在C++方面,Microsoft与Borland不是一个水平的,Borland明显的要高于Microsoft ,Borland的产品在技术上给我留下深刻的印象。那个时候Microsoft最好的开发平台是Visual Basic 3.0,而Borland的Delphi正处于开发阶段(Delphi 的代码名称是:“VB Killer”)……,想起这些十几年前的往事,我不禁感慨万千。 十几年来,我用过许多开发环境,关于Visual Basic,我用过最早的DOS版本,Windows版的Visual Basic我基本上全都用过,至今我还记得每个版本的VB安装盘磁盘的盘数。同样,我用过各个版本的Delphi,特别是Delphi 2.0,给我留下极好的印象。Delphi提供真正编译的可视化开发环境,那个时候(1994年左右),Delphi就可以开发带有GUI的动态链接库,你可以想象,在Microsoft Access 2.0的应用程序中可以加载一个Delphi Form并进行程序交互,那种感觉真是棒极了。 Borland C++是我心中无法抹掉的遗憾,从Turbo C到C++ Builder,我深刻的体验到Borland的辉煌和无奈,Delphi从VB Killer走到为VB护航(你可以想象Delphi一步到位的ActiveX 控件开发技术有多牛,早期的VB有多土,早期的VB不能开发动态链接库,因此无法开发ActiveX 控件,想起来真令人嘘唏不已),Borland C++的命运也是不济。Borland C++ 3.1的辉煌永远不再了,十几年的开发工作中,我在C++上投入了大量的精力,Borland C++曾经给我带来无数的激动,然而这个经典的名字却在与Microsoft的竞争中渐渐的流逝了……。 MFC4.0的出现,使得人们感觉Microsoft在C++方面赶上来了,这一版的MFC是Win95推出后出现在Visual C++ 4中(Microsoft没有VC 3,VC4以前的版本是2.2、2.1、2.0、1.51、1.5、1.0)。也许是对Borland C++的潜意识的失望,我不知不觉的接受了MFC,VC 4.2推出时,我通过正常渠道购买了这个编译器的企业版。 |
|
|||||||
|
|