站内搜索

本次搜索找到结果 10 条

基于Electron开发的客户端最终分发时需要打包。 最简单的方式就是大家喜闻乐见的Portable的压缩包形式。 当有某些特殊需求时,就需要制作安装包了。

通过使用PaxScript可以为Delphi应用增加对脚本的支持。

PaxScript支持paxC,paxBasic,paxPascle,paxJavaScript(对ECMA-262做了扩展) 四种脚本语言。它们分别是对应语言的子集。

在为程序增加脚本支持时要解决的主要问题是:本地代码与脚本之间的互相调用。

PaxScript为四种脚本语言都增加的名字空间和模块的概念。某段代码必须属于某个名字空间。执行代码时要指定代码所属的模块。默认的名字空间是全局名字空间。

Delphi中有将窗体持续化的函数

  • ObjectBinaryToText: 将二进制object流转换为文本格式
  • ObjectResourceToText:将Windows资源对象流转换为文本格式
  • ObjectTextToBinary:将文本格式对象流转换为二进制格式
  • ObjectTextToResource:将文本格式对象流转换为Windows资源格式

Phi中可以加载一个DFM文件,然后动态创建DFM对应的窗体。这样就不用手工敲代码创建了。只不过DFM只能包含Phi中的控件。

获取ActionScript 类信息的另类方法: 前一阵写了个提取 ActionScript 类中符号信息的脚本。算是可用。 用于混淆效果还行。

今天突然想到,在ActionScript中可以用

public function describeType(value:*):XML 
flash.util.describeType(KlassName)

获得类的详细信息:

  • 对象的类
  • 类的属性
  • 从类到其基类的继承树
  • 类实现的接口
  • 类的已声明实例属性
  • 类的已声明静态属性
  • 类的实例方法
  • 类的静态方法

(对于类的每个方法)名称、参数的数量、返回类型和参数类型 这样,就可以写一个类,将其他类的信息都打印出来。 但是,ActionScript工程中无法操作本地文件,只能trace到调试窗口。

以前有篇文章是写给Delphi程序增加对Pax JavaScript的支持 今天研究了一下给Java程序增加对 Ruby 的支持

目前可以通过 Apache的 BSF(Bean Script Framework) 和Sun的 Java Scripting(javax.script)

NuTs Launcher

天天用电脑,每次要找东西都要打开我的电脑,一个一个地点击文件夹,每次进入相同的目录都要半天.虽然只有短短的几秒钟,但是总觉的有些浪费生命. 有人建议我给常用的文件夹在桌面建立个快捷方式.不过我还是喜欢干干净净的桌面.很早就想写个能让我想到什么就立即执行的程序. 之后发现了Pointix300,这个软件支持鼠标手势,比如向左转一圈鼠标就会出现一个菜单,然后从这个菜单可以启动程序,访问网页等.我把这个程序推荐给了许多朋友. 不过遗憾的是,这个程序在我使用不久就停止更新了.连开发的公司也倒闭了.

于是就决定自己写一个类似的程序.在于是就有了 NuTs Launcher (下面简称NL).之所以叫 Launcher(发射器),是因为在Pointix300中有程序启动器这个功能,图标是一个正要发射的航天飞机. 而NuTs是来自Walnut中的nut,取狂热(Extremely enthusiastic),表示对技术的狂热.

可以说写这个程序的过程就是我学习编程的过程. 最初这个程序是用C++ Builder 写的,现在再看第一个版本,简直太简陋了,当初还花了不少时间.就这样在C++ Builder下写到第四个版本(好像是吧)后,决定用Delphi来写. 因为当初写这个程序时是一边学C++一边写的,语言还没学懂呢就想写程序了,有点不会走就想跑的意思. 改用Delphi是因为大一学校教的是Pascal.所以学习Delphi上手比较快,而且Delphi的控件相当丰富,对以一个人开发程序的确很方便.

NL最初的功能非常简单:用户按下热键,在鼠标下弹出一个菜单.菜单上有程序的路径,点击相应的菜单项启动相应的程序.

之后发现notepad,cmd,taskmgr这三个程序在平时的操作中使用相当频繁.所以就为这三个程序设置了专门的快捷键.

我还发现,多数用户会将自己的文件分门别类的存放在固定的文件夹中.在日常使用中这些目录会比其他目录更容易被访问.于是我设置了更多的热键,可以通过固定或指定的热键访问指定的目录或程序

既然写这个程序的目的是加快用户的操作频率,使得用户想到什么就可以做到什么.那还有什么操作是我们经常做而又"比较费时"的呢?窗口操作!习惯了Windows 图形界面的用户整天就是在双击窗口标题栏,最小化,最大化,关闭窗口,这些操作都需要用户将鼠标移动到屏幕的特定位置. 我观察了这个移动过程:以一个熟练的用户最小化窗口来说,首先是快速的向最小化按钮移动鼠标,在快到按钮时减慢速度,然后调整鼠标点击按钮. 但是,无论这个用户多么熟练,总要在按钮附近动态调整一下才能准确的点击.于是我想还是设置热键来直接进行这些操作.这样节省了鼠标点击的时间.可能这个时间也就零点几秒.积少成多嘛.

之后我根据平时的需要给程序增加了,便笺,隐藏窗口,剪贴板监控,键盘映射等功能.程序的版本也从4.0增加到了7.0. 在6.1.0的时候我把它发到了天空软件站 http://www.skycn.net/soft/25535.html

其实像NuTs Launcher 这种小软件太多了. 这类软件并不适合做成产品.一个很大的原因就是:你习惯的操作对于另一个人来说可能根本无法接受. 一个人一个习惯,然而让用户来改变习惯来适应你的软件是不可能的,也许这也是Pointx失败的原因.所以我也没指望有多少人喜欢这个软件. 只要我用着方便就行了.

虽然用的人少,但也有人下啊,现在已经有近800人下了,可能绝大多数人使用没多久就删除了.不过我还是要继续写下去.一来我还要继续用,二来也是一个学习的过程.

下面给出如何使用NuTs Launcher来加快日常操作:

Shortcut Menu ->快捷菜单. 这是NL的最基本的功能.下面是我的快捷菜单:

快捷菜单

你会看到在程序名字后面会有一个括号,括号里面有个带下划线的字母.其实这是Windows提供的一个功能--Traversal key,但是大多数Windwos程序员在写程序的时候很少为自己的程序设置Traversal key.

假设我为快捷菜单设置的热键是Ctrl+D, 那么我要启动金山词霸集PowerDic,那么我只要按下Ctrl+D,再按D,金山词霸就启动了.下面看一下如何设置这个快捷项:

如果你是个急性子,经常按错键,或者你的鼠标不好点击快捷菜单项时老点错,那么你可以在 NL的设置页来设置启动延时,这样可以给你反悔的机会,不过不推荐.因为这违背这个软件的初衷.

最后,你看,退出默认是X,你只需按Ctrl+d,x就可一推出NL. 你可以编辑 X:\program files\NuTs\NuTs Launcher\lang*.ini 中的TNuTsLnchr.btnExit.Caption=退出(&X)来改变默认的Traversal Key

其实整个NL都利用了Traversal Key来方便操作,把你的左手放在标准位置上,你会发现NL会更好用Hotkey:You are so hoooot!

上面是最新版的热键页面截图2007-05-09

一直没有时间继续写NL的这个帮助. ....不过一定会继续写滴,恩~~ 其实我一直以为这个程序不太会有人使用. 前几天,惊讶的发信,坐在我旁边的哥们在用. 我以为是他从我的另一个同学那里拷贝来的. 一问才知道, 他是从"天空"下载的, 还是老的6.10版本.他跟我说:"这个软件特爽..." 当时我心里狂喜,只听见这一句. 之后我特美地告诉他,这个软件是我写滴. 他开始还半信半疑, 之后我给他看了源代码. 他高兴的说终于找到根啦,然后就迫不及待的给我提了个关于程序的建议. 我告诉他,他提的建议在新版本中已经实现了. 只是新版本还有待完善,所以迟迟没有发布.

最上面的图片是前几天写的闪屏. 没别的用处,只是想给NL一个漂亮的出场. 你可以做一个个性的闪屏,将data\splash.bmp覆盖. 为了不妨碍使用,闪屏是在程序完全加载后才出现的.如果你不喜欢它,可以在程序的快捷方式上加上-nosplash参数来取消. 新版本还加了一个简单的ToDoList((我的便见条粘性不够,老从墙上掉下来). 其实我一直在用AbstractSpoon Software的OpenSource的ToDoList.感觉很好用. 但是平时有些杂事不想放到里面,因为那里面都是开发的记录.所以就打算写个简易的ToDoList,原则是够用就好.2007-08-04Hotkey页是个重点, 这个页上设置了最最常用的热键.下面根据上图从上到下依次介绍:显示/隐藏本程序: 用来显示和隐藏NL的主对话框. 比如上面设置的热键是Alt+v 当用户按下Alt+v 后,NL的主对话框就会蹦出来. 当鼠标位置在对话框举行内时, 对话框显示的位置不变, 当鼠标在对话框外时, 则将对话框中心移动到鼠标所在坐标. 备忘录

切换到备忘录页中最后一次使用的备忘录. 技巧: 备忘录页最后一页是一个剪贴板监视器, 用来保存剪贴板历史, 可以将想要保存的文本暂时保存在这里. 这个功能非常实用, 使用频率也很高, 但是我没有给它单独设置快捷键, 而是借用了备忘录的快捷键.比如上面图中设置的备忘录快捷键是Alt+g, 那么剪贴板监视器的快捷键就是Alt+g, g, 即按住Alt键的同时连续快速按两次g, 程序中设置的两次按键最大时间间隔是200毫秒. 显示快捷菜单

比如上图中设置的显示快捷菜单的热键是Alt+d, 那么按下这个热键后, 快捷菜单就会蹦出来 打开剪贴板上的路径

技巧: 看图1和图2,

我在菜单项的名字后面打上了 (&符号), 这样,当你按下Alt+D后, 再按相应的符号, 就可以打开相应的程序或目录. 比如我要打开金山词霸, 只需按 Alt+d,d, 打开Photoshop按Alt+d,p. 符号不光可以是字母, 还可以是数字和标点符号,比如可以是问号. Alt+d,? ,注意, 这是你需要按Alt+d,Shift+/ 自定义常用热键#1->#3 自定义热键来执行自定义的命令, 比如: 在图1中, 自定义常用热键#1的热键是 Alt+z, 对应的命令有两个, 第一个是打开 "d:\program files", 第二个是按主鼠标左键的同时按下Alt+z, 会打开 "g:\program files" 任务管理器,记事本,命令行 这三个程序是平时最常使用的, 所以为它们单独设置的热键. 同样, 和自定义热键一样, 也设置了鼠标左键按下后执行的命令. 提示: 没有为记事本设置鼠标左键按下命令, 是因为, 经常出现这种情况: 用户要拷贝一段文字, 在选中的同时就按下热键打开记事本, 此时鼠标左键是按下的, 那么就会执行按下左键对应的命令, 而不会打开记事本了.Windows开始按钮 其实这个功能是我最早学编程时写的一个改变Windows开始按钮上"开始"二字的小程序. 现在还赖在NL中不走. 取消/启用所有热键 我设置了一个固定的热键Ctrl+Shift+Alt+G, 这个热键一个手比较难按, 需要两个手一块按, 而且这个热键使用了4个键, 所以被误按的几率比较小. 按这个热键后, 所有的热键都会失效, 再按一下又会有效. 更多热键 主要为 Win + 小键盘数字, Alt + 数字键, Alt + 小键盘数字键 设置命令, 其中Alt + 数字键支持鼠标左键按下命令. 技巧: Win + 数字键被定义为打开相应分区: Win+1打开C盘, Win+2打开D盘, 以此类推, 这也是很常用的功能~ 鼠标和窗体 这里的功能用的比较少, 我在考虑删掉或可选择不加载这部分功能 窗口页面 这部分功能也是非常实用的 关闭当前窗口 最小化当前窗口 最大化/还原当前窗口 这3个热键对应平时非常频繁的3个窗口操作, 其中最小化当前窗口和关闭当前窗口都需要鼠标定位到特定的地方. 有了这3个热键, 你会发现, 操纵窗口是一种享受~ 改变窗口属性 令窗口一直显示在最前端的步骤 1. 将鼠标移动到你要操作的窗口上方 2. 按下热键Ctrl+Alt+T (Topmost) 令窗口一直处于最底层与上面类似 移动窗口的操作也类似, 这个功能比较有用, 当一些讨厌的窗口无法移动时, 就可以用到, 你甚至可以将程序中的广告条移到看不见的位置~ 强制关闭窗口(杀死窗口对应进程) 这个功能也很有用, 比如某些程序停止了相应, 而点击窗口右上方的X也不管用时, 那就干脆杀死它~ 鼠标放到它上方, Ctrl+Alt+F (Find)选中该程序, Ctrl+Alt+K (Kill)杀死它

总体来说

  • c++: 完全的控制,极大的灵活性,灵活带来的复杂
  • java:比c++"保守",虚拟机风格的体系结构
  • delphi:很保守

inclusion/using clause

#include <iostream>
using namespace std;
import java.io.*;
uses windows,system,sysutils;

inheritance
class Child:[public | protected | private] Fahter,public Mother{
            ...
}

C++是这三种语言中继承最为复杂的. 分为 public,protected,private继承,同时支持真正的多继承

public继承(又称类型继承)和java的继承一样,保持父类的访问控制

protected继承将父类public成员改变为protected的

private继承(又称实现继承)不直接支持父类的公有接口,而提供自己的公有接口

所以private继承中,子类将父类中原先的protected成员和public成员都置为private的 所以原则上private继承中的子类应该是继承树中的叶节点,它只有private和public成员而 没有protected成员.它不应该再被继承,类似于Java中的final class

class Child extends Father implements IInterface{
            ...
}
TChild = class(TFather,IInterfacedObject)
            ...
end;

java 和 delphi 的继承相似,都是单继承同时可以实现多个接口

Access Control/Organization

class Foo{
   private:
    int i;
             int j;
             void f();
   protected: 
             int k;
   public: 
             int o;
}

class Foo /* extends Object */ {
  private
             int i;
             int j;
             void f(){...};
   protected
             int k;
   public
             int o;
}
TFoo = class(TObject)
   private 
             i:Integer;
             j:Integer;
   protected
             k:Integer;
   public
             o:Integer;
   publicshed
   property
             Tag:Integer read i write i;
end;

如果不给出访问控制, c++默认是private java默认是包内可见的 delphi默认是public的(单元内可见?)

c++的访问控制和代码管理最为丰富: 可以通过#include包含c/cpp头文件,提供向c的兼容 同时可以使用名字空间防止名字冲突,并且像c一样将代码分为头文件和实现文件 同时丰富性也带来了复杂性.使得c++的文件组织没有java和delphi清晰

java提供了松散的代码组织方式--包(package),而delphi提供了单元的代码组织方法 这两种组织方法相似,都是给了我们一个存放一组相关类的地方.而这个地方,在java中 是一个文件系统上的文件夹,在delphi中是一个单元文件 相比之下,我还是喜欢Delphi的组织方法:当类很多的时候java包中的文件变得既多又不好管理

C++中有虚函数。而Delphi中既有虚函数还有动态函数。二者在语义上是等价的。

Virtual and dynamic methods are semantically equivalent. They differ only in the implementation of method-call dispatching at runtime.

区别在于虚函数优化了速度,而动态函数优化了代码大小

Virtual methods optimize for speed, while dynamic methods optimize for code size.

In general, virtual methods are the most efficient way to implement polymorphic behavior.

总体上来讲,虚函数是实现多态行为的最有效的方法

Dynamic methods are useful when a base class declares many overridable methods which are inherited by many descendant classes in an application, but only occasionally overridden.

当基类声明了很多可重写但是很少重写的方法时使用动态函数, 这样可一减小代码大小。在设计Framework时常使用这个技术

作为Windows的Framework需要解决的一个问题就是 消息映射 Message Mapping

Delphi中由于有编译器的支持,可以直接在用户编写的类中处理需要处理的消息:

procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;

在MFC中就比较复杂了. afx使用宏来实现Windows消息到处理函数的映射而没有使用会带 而外的开销:由于C++中实现虚函数需要一个分发表vtable,而无论子类是否重写父类的虚函数, 都需要4个字节的开销.在CWnd类中至少要处理100个以上的消息.这样设计会带来很大的问题.

afx用一组宏将用于消息映射的数据保存在类中.大概情况是:记录该类可以处理的Windows消息, 当消息来到时,对比自己可以处理的消息列表,如果可以处理,就交给列表中记录的函数来处理. 而这个函数就是我们自己些的.比如 OnLButtonDown等

afx.h 中声明了mfc中最基础的类

struct CRuntimeClass;                  // object type information

class CObject;                                // the root of all objects classes

class CException;                         // the root of all exceptions
          class CArchiveException;              // archive exception
          class CFileException;                 // file exception
          class CSimpleException;
           class CMemoryException;               // out-of-memory exception
           class CNotSupportedException; // feature not supported exception

class CFile;                              // raw binary file
          class CStdioFile;                     // buffered stdio text/binary file
          class CMemFile;                       // memory based file

// Non CObject classes
class CString;                                // growable string type
class CTimeSpan;                              // time/date difference
class CTime;                                  // absolute time/date
struct CFileStatus;                           // file status information
struct CMemoryState;                          // diagnostic memory support

class CArchive;                               // object persistence tool
class CDumpContext;                           // object diagnostic dumping

先看一下CRuntimeClass

struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName;//保存类名
int m_nObjectSize;//类对象大小
UINT m_wSchema; // schema number of the loaded class//是否可序列化
CObject* (PASCAL* m_pfnCreateObject)(); // 指向类的构造函数的指针

           //指向父类CRuntimeClass的指针
#ifdef _AFXDLL
CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
CRuntimeClass* m_pBaseClass;
#endif

// Operations
CObject* CreateObject(); //动态创建
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const; //类型鉴别

// Implementation
void Store(CArchive& ar) const; //序列化
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum) ;//"反序列化"

// CRuntimeClass objects linked together in simple list
CRuntimeClass* m_pNextClass;               // linked list of registered classes //注册类列表
};

CRuntimeClass 是MFC中很重要的一个类,动态创建实例是最常用的功能之一

// Helper macros for declaring CRuntimeClass compatible classes

#ifdef _AFXDLL
#define DECLARE_DYNAMIC(class_name) \
protected: \
static CRuntimeClass* PASCAL _GetBaseClass(); \
public: \
static const AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \

#define _DECLARE_DYNAMIC(class_name) \
protected: \
static CRuntimeClass* PASCAL _GetBaseClass(); \
public: \
static AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \

#else
#define DECLARE_DYNAMIC(class_name) \
public: \
static const AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \

#define _DECLARE_DYNAMIC(class_name) \
public: \
static AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \

#endif

// not serializable, but dynamically constructable
#define DECLARE_DYNCREATE(class_name) \
DECLARE_DYNAMIC(class_name) \
static CObject* PASCAL CreateObject();

#define _DECLARE_DYNCREATE(class_name) \
_DECLARE_DYNAMIC(class_name) \
static CObject* PASCAL CreateObject();

#define DECLARE_SERIAL(class_name) \
_DECLARE_DYNCREATE(class_name) \
AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb);

CObject 基本服务:

BOOL IsSerializable() const;
BOOL IsKindOf(const CRuntimeClass* pClass) const; //类型鉴别

// Overridables
virtual void Serialize(CArchive& ar); //序列化

// Diagnostic Support //诊断支持
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;

//动态创建,利用CRuntimeClass里的m_pfnCreateObject
CObject* CRuntimeClass::CreateObject()

CWnd 在afxwin.h中定义,在wincore.cpp中实现 CWnd封装的功能

// Window tree access
// Message Functions
// Message processing for modeless dialog-like windows
// Window Text Functions
// CMenu Functions - non-Child windows only
// Window Size and Position Functions
// Coordinate Mapping Functions
// Update/Painting Functions
// Timer Functions
// ToolTip Functions
// Window State Functions
// Dialog-Box Item Functions
// Scrolling Functions
// Window Access Functions
// Alert Functions
// Clipboard Functions
// Caret Functions
// Shell Interaction Functions
// Icon Functions
// Context Help Functions
// Dialog Data support
// Help Command Handlers
// Layout and other functions
// OLE control wrapper functions

/* 消息处理函数 */
// Window-Management message handler member functions
// Nonclient-Area message handler member functions
// System message handler member functions
// Input message handler member functions
// Initialization message handler member functions
// Clipboard message handler member functions
// Control message handler member functions
// MDI message handler member functions
// Menu loop notification messages

Windows 控件的实现文件在 winctrl1.cpp~winctrl7.cpp

在Delphi 例程中没有类似于C中的system函数

int system(const char* command);

用下面的方法可以让你在Delphi中使用msvc runtime的函数

program YourProg;
function system(command:PChar):Integer;cdecl;external "msvcrt.dll" name "system";
begin
    system("pause");
end.