Python中使用lua续,让lupa调用原生模块

前文曾经讲过python中使用lua的方法,目前用的最多的还是lupa模块,但是通过pip安装的lupa存在一个问题,无法支持lua原生模块,因此本文介绍一下如何用lupa调用原生模块。

首先需要卸载掉原来的lupa

pip uninstall lupa

接下来安装lua,这个不多做介绍了,下载->解压->编辑,本人用的是lua5.1.5,linux和macos都没问题

安装lua之后,需要把lua源码目录里的./etc/lua.pc拷贝到/usr/local/lib/pkg-config/,这里的/usr/local/lib是你lua库安装的位置,这个很重要,否则后边进行不下去

接下来从pip官网下载lupa源码,解压后修改setup.py,搜索–exists 把他替换为–libs,因为lupa的安装程序这里有个逻辑错误,是通过pkg-config的exists参数来判断库是否存在,修改为–libs参数以后可以绕过这个检查,让lupa直接使用你本机安装的lua库。

接下来就是

python setup.py --no-bundle
sudo python setup.py install

为了验证原生模块是否能够编译使用,可以下载一个lua的json扩展,叫lua-cjson,下载地址

下载解压后,如果是linux,直接sudo make install

如果是macos,需要修改Makefile,把macos相关部分的CFLAG配置拷贝过来即可。否则编译参数是不对的,会找不到库

最后就是写一个lua脚本,引用cjson库,通过python去执行这个脚本就可以了

微信小程序发红包发零钱双向验证攻略

首先需要明确几个概念,小程序、公众号各自的能力范围。

本文涉及到的功能均需要TLS的双向验证机制,因此证书的处理是开发中最容易遇到的问题,而且这些肯定是运行在服务器上的,绝大多数情况下都是证书使用错误导致,其次就是IP白名单的问题。关于TLS双向验证,这不是传统意义上的http多次交互握手,而是基于TLS协议的一个长连接,在握手、协议选择、校验私钥之后才进行数据传输。

设计双向验证的,需要3个pem文件,其中2个是微信商户平台自行下载的,或者通过商户平台的p12文件来生成。而最后一个pem则是双向验证必须的文件,即根证书,根证书下载地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=23_4

这个搞错了,势必会报出非认证机构颁发的证书。

接下来,就是涉及功能。小程序体系(参数指定的appid为小程序)可以调用企业红包服务,但是不能掉起微信红包功能,微信红包功能依赖的wxappi是指一个公众号的appid,这个坑千万不要踩。

先讲这么多

Python和lua互相调用

本帖中用到的py模块是lupa,目前版本是1.7,通过pip可以直接安装。说明一下,由于这个模块的文档非常少,只有几个代码例子,所以以下所说都是根据已有的例子自己测出来的。如果有更好的办法请回帖。

一、python调用lua,照搬lupa在python社区的所有例子都可以,但要注意一点,整段的lua文件代码,不能使用eval方法,一定会报错。原因不多讲了。整段lua代码的调用直接execute()

二、lua调用python,这就是亮点,也是一个容易踩的坑。首先你不能在lua代码里直接使用py的任何变量,如果要使用,需要通过eval来执行一个匿名定义,然后通过eval返回值(他是py的一个function)来当做py本地函数来调用。

下文是例子:

import lupa
from lupa import LuaRuntime
lua = LuaRuntime(unpack_returned_tuples=True)

def py_func(p):
    print p
    return 'hello '+str(p)

#execute无返回,打印出来是None
lua.execute('var_before=1;print(var_before);')

#eval返回的是一个lua call到py call的映射
pycall = lua.eval('function(pyfunc,param1) var_before=pyfunc(param1) end')

pycall(py_func,'world')

lua.execute('print(var_before)')

通过以上例子应该能看明白二者互相调用的方式。重要的事情多说几遍,eval内部只能是匿名过程。所以要互相调用,目前我所知道的办法就是频繁的切换eval和execute来控制lua执行

以上执行过程都是在lua这个luaruntime实例里运行的,所以属于一个上下文,这点极其重要,直接关乎到性能问题,同时共享上下文变量的值得以继承,真正实现了无缝切换,多实例的lua vm没有测试过,理论上是不影响的。

Node.js原生模块开发之N-API开发教程

长话短说,N-API在node8.0以后开始加入支持,目的在于解决node跨版本之间原生模块的兼容问题,Nan原生模块无法兼容不同版本的Node,需要重新编译(看脸和google的心情)。N-API做了一层抽象,实现了跨版本支持。

需要注意一点,10.0之前,N-API是试验模式,使用该模块的时候会在stderr也可能是stdout输出一段警告,程序不依赖控制外可以无视此警告。10.0以后默认支持,警告不再复现

准备工作:

node环境:node 10.X

node开发环境:https://github.com/nodejs/node

需要安装的模块:npm/node-gyp

本人开发环境:debian 9/Linux kernel 4.9/gcc 6.3.0

最简易的开发方式,进入源码目录下的~/node-master/test/addon-napi/1_hello_world/

直接编译:#node-gyp configure &&node-gyp build

结果呢,当然是一堆错误,参照第二个例子修改这个hello world即可

ANSI C的库可以直接使用,三方库需要在binding.gpy里配置,相当于-l -L这些参数去搞gpy文件即可,google搜node-gpy使用

下边简单介绍一下API调用

NAPI_CALL(env, napi_throw_error(env,”timeout 001″,”time out”));

通过NAPI_CALL宏来调用N-API,N-API帮助去官网看,各种函数,上边这个例子是调用了一个抛出异常的API。

napi_value val1;

通过napi_value声明一个nodejs变量。

变量的初始化通过NAPI_CALL来调用对应的API来实现,可以创建

这么多类型的js变量。

NAPI_CALL(env, napi_create_string_utf8(env, str, str_len, &val1));

这里创建了一个val1的js字符串,还是utf8版本的

如果你的缓存区是个字节流,NAPI能自动给你转UTF-8,大写的赞。

没了,就这么多,不会的看例子。

有心情继续写。

我刚发布的npm模块叫xd-synchttp

执行的功能是同步获取http请求结果。解决http模块全异步的局限性。有些场景必须使用同步。模块内加了超时设定,超时了就会抛出异常。欢迎使用

const sync = require('xd-synchttp');
let content = "";
try{
    content = sync.http_get('http://www.csdn.net',0);//0为不超时
}
catch(err)
{
console.log(err);
}

C# Socket的Send实现完全阻塞

C#中Socket的Send方法即使是在阻塞模式下也会立即返回,查了不少资料,都没什么结果,最后在MSDN找到了答案。
Send的发送默认是不带参数的,其实是写入了本地缓存区,然后基础系统拆分后分批次发送。如果想要实现真正的阻塞,需要使用SocketFlag参数
但SocketFlag参数参考MSDN的例子,现在把代码贴上来,一看便知




// Displays sending with a connected socket 
// using the overload that takes a buffer, message size, and socket flags. public static int SendReceiveTest3(Socket server) 
{ 
byte[] msg = Encoding.UTF8.GetBytes("This is a test"); 
byte[] bytes = new byte[256]; 
try {
 // Blocks until send returns. 
int i = server.Send(msg, msg.Length, SocketFlags.None); Console.WriteLine("Sent {0} bytes.", i); 
// Get reply from the server. 
int byteCount = server.Receive(bytes, server.Available, SocketFlags.None); if (byteCount > 0)
 Console.WriteLine(Encoding.UTF8.GetString(bytes)); 
} 
catch (SocketException e) 
{ 
Console.WriteLine("{0} Error code: {1}.", e.Message, e.ErrorCode); 
return (e.ErrorCode); 
} 
return 0; } 

文本比较算法Ⅰ——LD算法

文本比较算法Ⅰ——LD算法

  在日常应用中,文本比较是一个比较常见的问题。文本比较算法也是一个老生常谈的话题。

  文本比较的核心就是比较两个给定的文本(可以是字节流等)之间的差异。目前,主流的比较文本之间的差异主要有两大类。一类是基于编辑距离(Edit Distance)的,例如LD算法。一类是基于最长公共子串的(Longest Common Subsequence),例如Needleman/Wunsch算法等。

  LD算法(Levenshtein Distance)又成为编辑距离算法(Edit Distance)。他是以字符串A通过插入字符、删除字符、替换字符变成另一个字符串B,那么操作的过程的次数表示两个字符串的差异。

  例如:字符串A:kitten如何变成字符串B:sitting。

    第一步:kitten——》sitten。k替换成s

    第二步:sitten——》sittin。e替换成i

    第三步:sittin——》sitting。在末尾插入g

  故kitten和sitting的编辑距离为3

  定义说明:

  LD(A,B)表示字符串A和字符串B的编辑距离。很显然,若LD(A,B)=0表示字符串A和字符串B完全相同

  Rev(A)表示反转字符串A

  Len(A)表示字符串A的长度

  A+B表示连接字符串A和字符串B

  有下面几个性质:

  LD(A,A)=0

  LD(A,””)=Len(A)

  LD(A,B)=LD(B,A)

  0≤LD(A,B)≤Max(Len(A),Len(B))

  LD(A,B)=LD(Rev(A),Rev(B))

  LD(A+C,B+C)=LD(A,B)

  LD(A+B,A+C)=LD(B,C)

  LD(A,B)≤LD(A,C)+LD(B,C)(注:像不像“三角形,两边之和大于第三边”)

  LD(A+C,B)≤LD(A,B)+LD(B,C)

  为了讲解计算LD(A,B),特给予以下几个定义

  A=a1a2……aN,表示A是由a1a2……aN这N个字符组成,Len(A)=N

  B=b1b2……bM,表示B是由b1b2……bM这M个字符组成,Len(B)=M

  定义LD(i,j)=LD(a1a2……ai,b1b2……bj),其中0≤i≤N,0≤j≤M

  故:  LD(N,M)=LD(A,B)

      LD(0,0)=0

      LD(0,j)=j

      LD(i,0)=i

  对于1≤i≤N,1≤j≤M,有公式一

  若ai=bj,则LD(i,j)=LD(i-1,j-1)

  若ai≠bj,则LD(i,j)=Min(LD(i-1,j-1),LD(i-1,j),LD(i,j-1))+1

  举例说明:A=GGATCGA,B=GAATTCAGTTA,计算LD(A,B)

  第一步:初始化LD矩阵  



GAATTCAGTTA

01234567891011
G1










G2










A
3










T4










C5










G6










A7










  第二步:利用上述的公式一,计算第一行



GAATTCAGTTA

01234567891011
G1012345678910
G2










A3










T4










C5










G6










A7










  第三步,利用上述的公示一,计算其余各行 



GAATTCAGTTA

01234567891011
G1012345678910
G211234566789
A321123456788
T432212345678
C543322234567
G654433333456
A765444434455

  则LD(A,B)=LD(7,11)=5

  下面是LD算法的代码,用的是VB2005。代码格式修正于2012年1月6日。Public Class clsLD
  Private Shared mA() As Char
  Private Shared mB() As Char

  Public Shared Function LD(ByVal A As String, ByVal B As String) As Integer

    mA = A.ToCharArray
    mB = B.ToCharArray

    Dim L(A.Length, B.Length) As Integer
    Dim i As Integer, j As Integer

    For i = 1 To A.Length
      L(i, 0) = i
    Next
    For j = 1 To B.Length
      L(0, j) = j
    Next

    For i = 1 To A.Length
      For j = 1 To B.Length
        If mA(i – 1) = mB(j – 1) Then
          L(i, j) = L(i – 1, j – 1)
        Else
          L(i, j) = Min(L(i – 1, j – 1), L(i – 1, j), L(i, j – 1)) + 1
        End If
      Next
    Next

    Return L(A.Length, B.Length)
  End Function

  Public Shared Function Min(ByVal A As Integer, ByVal B As Integer, ByVal C As Integer) As Integer
    Dim I As Integer = A
    If I > B Then I = B
    If I > C Then I = C
    Return I
  End Function
End Class

  这个LD算法时间复杂度为O(MN),空间复杂度为O(MN),如果进行优化的话,空间复杂度可以为O(M),优化的代码这里不再详述了。参看“计算字符串的相似度(VB2005)

  我们往往不仅仅是计算出字符串A和字符串B的编辑距离,还要能得出他们的匹配结果。

  以上面为例A=GGATCGA,B=GAATTCAGTTA,LD(A,B)=5

  他们的匹配为:

    A:GGA_TC_G__A

    B:GAATTCAGTTA

  如上面所示,蓝色表示完全匹配,黑色表示编辑操作,_表示插入字符或者是删除字符操作。如上面所示,黑色字符有5个,表示编辑距离为5。

  利用上面的LD矩阵,通过回溯,能找到匹配字串

  第一步:定位在矩阵的右下角  



GAATTCAGTTA

01234567891011
G1012345678910
G211234566789
A321123456788
T432212345678
C543322234567
G654433333456
A765444434455

  第二步:回溯单元格,至矩阵的左上角

    若ai=bj,则回溯到左上角单元格



GAATTCAGTTA

01234567891011
G1012345678910
G211234566789
A321123456788
T432212345678
C543322234567
G654433333456
A765444434455

    若ai≠bj,回溯到左上角、上边、左边中值最小的单元格,若有相同最小值的单元格,优先级按照左上角、上边、左边的顺序



GAATTCAGTTA

01234567891011
G1012345678910
G211234566789
A321123456788
T432212345678
C543322234567
G654433333456
A765444434455

    若当前单元格是在矩阵的第一行,则回溯至左边的单元格

    若当前单元格是在矩阵的第一列,则回溯至上边的单元格



GAATTCAGTTA

01234567891011
G1012345678910
G211234566789
A321123456788
T432212345678
C543322234567
G654433333456
A765444434455

    依照上面的回溯法则,回溯到矩阵的左上角

  第三步:根据回溯路径,写出匹配字串

    若回溯到左上角单元格,将ai添加到匹配字串A,将bj添加到匹配字串B

    若回溯到上边单元格,将ai添加到匹配字串A,将_添加到匹配字串B

    若回溯到左边单元格,将_添加到匹配字串A,将bj添加到匹配字串B

    搜索晚整个匹配路径,匹配字串也就完成了

  从上面可以看出,LD算法在不需要计算出匹配字串的话,时间复杂度为O(MN),空间复杂度经优化后为O(M)

  不过,如果要计算匹配字符串的话,时间复杂度为O(MN),空间复杂度由于需要利用LD矩阵计算匹配路径,故空间复杂度仍然为O(MN)。这个在两个字符串都比较短小的情况下,能获得不错的性能。不过,如果字符串比较长的情况下,就需要极大的空间存放矩阵。例如:两个字符串都是20000字符,则LD矩阵的大小为20000*20000*2=800000000Byte=800MB。呵呵,这是什么概念?故,在比较长字符串的时候,还有其他性能更好的算法。留待后文详述。

Windows编程之滚动条—滚动条消息

在用鼠标单击滚动条或者拖动卷动方块时,Windows给窗口消息处理程序发送WM_VSCROLL(供上下移动)和WM_HSCROLL(供左右移动)消息。在滚动条上的每个鼠标动作都至少产生两个消息,一条在按下鼠标按钮时产生,一条在释放按钮时产生。

和所有的消息一样,WM_VSCROLL和WM_HSCROLL也带有wParam和lParam消息参数。对于来自作为窗口的一部分而建立的滚动条消息,您可以忽略lParam;它只用于作为子窗口而建立的滚动条(通常在对话框内)。

wParam消息参数被分为一个低字组和一个高字组。wParam的低字组是一个数值,它指出了鼠标对滚动条进行的操作。这个数值被看作一个「通知码」。通知码的值由以SB(代表「scroll bar(滚动条)」)开头的标识符定义。以下是在WINUSER.H中定义的通知码:

#define SB_LINEUP       0
        
#define SB_LINELEFT           0
        
#define SB_LINEDOWN           1
        
#define SB_LINERIGHT          1
        
#define SB_PAGEUP         2
        
#define SB_PAGELEFT           2
        
#define SB_PAGEDOWN           3
        
#define SB_PAGERIGHT          3
        
#define SB_THUMBPOSITION   4
        
#define SB_THUMBTRACK         5
        
#define SB_TOP                6
        
#define SB_LEFT           6
        
#define SB_BOTTOM        7
        
#define SB_RIGHT          7
        
#define SB_ENDSCROLL          8

包含LEFT和RIGHT的标识符用于水平滚动条,包含UP、DOWN、TOP和BOTTOM的标识符用于垂直滚动条。鼠标在滚动条的不同区域单击所产生的通知码如图4-7所示。

 ​

如果在滚动条的各个部位按住鼠标键,程序就能收到多个滚动条消息。当释放鼠标键后,程序会收到一个带有SB_ENDSCROLL通知码的消息。一般可以忽略这个消息,Windows不会去改变卷动方块的位置,而您可以在程序中呼叫SetScrollPos来改变卷动方块的位置。

当把鼠标的光标放在卷动方块上并按住鼠标键时,您就可以移动卷动方块。这样就产生了带有SB_THUMBTRACK和SB_THUMBPOSITION通知码的滚动条消息。在wParam的低字组是SB_THUMBTRACK时,wParam的高字组是使用者在拖动卷动方块时的目前位置。该位置位于卷动列范围的最小值和最大值之间。在wParam的低字组是SB_THUMBPOSITION时,wParam的高字组是使用者释放鼠标键后卷动方块的最终位置。对于其它的卷动列操作,wParam的高字组应该被忽略。

为了给使用者提供回馈,Windows在您用鼠标拖动卷动方块时移动它,同时您的程序会收到SB_THUMBTRACK消息。然而,如果不通过呼叫SetScrollPos来处理SB_THUMBTRACK或SB_THUMBPOSITION消息,在使用者释放鼠标键后,卷动方块会迅速跳回原来的位置。

程序能够处理SB_THUMBTRACK或SB_THUMBPOSITION消息,但一般不同时处理两者。如果处理SB_THUMBTRACK消息,在使用者拖动卷动方块时您需要移动显示区域的内容。而如果处理SB_THUMBPOSITION消息,则只需在使用者停止拖动卷动方块时移动显示区域的内容。处理SB_THUMBTRACK消息更好一些(但更困难),对于某些型态的数据,您的程序可能很难跟上产生的消息。

WINUSER.H表头文件还包括SB_TOP、SB_BOTTOM、SB_LEFT和SB_RIGHT通知码,指出滚动条已经被移到了它的最小或最大位置。然而,对于作为应用程序窗口一部分而建立的滚动条来说,永远不会接收到这些通知码。

在滚动条范围使用32位的值也是有效的,尽管这不常见。然而,wParam的高字组只有16位的大小,它不能适当地指出SB_THUMBTRACK和SB_THUMBPOSITION操作的位置。在这种情况下,需要使用GetScrollInfo函数(在下面描述)来得到信息。

在SYSMETS中加入卷动功能

前面的说明已经很详尽了,现在,要将那些东西动手做做看了。让我们开始时简单些,从垂直卷动着手,因为我们实在太需要垂直卷动了,而暂时还可以不用水平卷动。SYSMET2如程序4-3所示。这个程序可能是滚动条的最简单的应用。

程序4-3 SYSMETS2.C        

/*------------------------------------------------------------------
        
    SYSMETS2.C -- System Metrics Display Program No. 2
        
            (c) Charles Petzold, 1998
        
------------------------------------------------------------------*/
        
#include <windows.h>
        
#include "sysmets.h"
        
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
        
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
        
                 PSTR szCmdLine, int iCmdShow)
        
{
        
    static TCHAR szAppName[] = TEXT ("SysMets2") ;
        
    HWND   hwnd ;
        
    MSG    msg ;
        
    WNDCLASS wndclass ;
        
    wndclass.style           = CS_HREDRAW | CS_VREDRAW ;
        
    wndclass.lpfnWndProc  = WndProc ;
        
    wndclass.cbClsExtra   = 0 ;
        
    wndclass.cbWndExtra   = 0 ;
        
    wndclass.hInstance       = hInstance ;
        
    wndclass.hIcon        = LoadIcon (NULL, IDI_APPLICATION) ;
        
    wndclass.hCursor         = LoadCursor (NULL, IDC_ARROW) ;
        
    wndclass.hbrBackground        = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
        
    wndclass.lpszMenuName         = NULL ;
        
    wndclass.lpszClassName        = szAppName ;
        

    if (!RegisterClass (&wndclass))
        
    {
        
    MessageBox (NULL, TEXT ("This program requires Windows NT!"),
        
           szAppName, MB_ICONERROR) ;
        
    return 0 ;
        
    }
        

    hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 2"),
        
                   WS_OVERLAPPEDWINDOW | WS_VSCROLL,
        
                   CW_USEDEFAULT, CW_USEDEFAULT,
        
                   CW_USEDEFAULT, CW_USEDEFAULT,
        
                   NULL, NULL, hInstance, NULL) ;
        
    ShowWindow (hwnd, iCmdShow) ;
        
    UpdateWindow (hwnd) ;
        

    while (GetMessage (&msg, NULL, 0, 0))
        
    {
        
       TranslateMessage (&msg) ;
        
        DispatchMessage (&msg) ;
        
    }
        
    return msg.wParam ;
        
}
        
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        
{
        
    static int  cxChar, cxCaps, cyChar, cyClient, iVscrollPos ;
        
    HDC         hdc ;    
        
    int         i, y ;   
        
    PAINTSTRUCT ps ;
        
    TCHAR       szBuffer[10] ;   
        
    TEXTMETRIC  tm ;     
        
    switch (message)     
        
    {
        
case WM_CREATE:
        
    hdc = GetDC (hwnd) ;
        
    GetTextMetrics (hdc, &tm) ;
        
    cxChar = tm.tmAveCharWidth ;
        
    cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
        
    cyChar = tm.tmHeight + tm.tmExternalLeading ;
        

    ReleaseDC (hwnd, hdc) ;
        
    SetScrollRange (hwnd, SB_VERT, 0, NUMLINES - 1, FALSE) ;
        
    SetScrollPos   (hwnd, SB_VERT, iVscrollPos, TRUE) ;
        
            return 0 ;
        

    case WM_SIZE:
        
            cyClient = HIWORD (lParam) ;
        
            return 0 ;
        

    case WM_VSCROLL:
        
            switch (LOWORD (wParam))
        
         {
        
    case SB_LINEUP:
        
          iVscrollPos -= 1 ;
        
            break ;
        
   
        
    case SB_LINEDOWN:
        
            iVscrollPos += 1 ;
        
            break ;
        

    case SB_PAGEUP:
        
            iVscrollPos -= cyClient / cyChar ;
        
            break ;
        
   
        
    case SB_PAGEDOWN:
        
            iVscrollPos += cyClient / cyChar ;
        
            break ;
        
   
        
    case SB_THUMBPOSITION:
        
            iVscrollPos = HIWORD (wParam) ;
        
            break ;
        
   
        
    default :
        
            break ;
        
         }
        

    iVscrollPos = max (0, min (iVscrollPos, NUMLINES - 1)) ;
        
    if (iVscrollPos != GetScrollPos (hwnd, SB_VERT))
        
         {
        
            SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
        
            InvalidateRect (hwnd, NULL, TRUE) ;
        
         }
        
            return 0 ;
        
    case WM_PAINT:
        
            hdc = BeginPaint (hwnd, &ps) ;
        
            for (i = 0 ; i < NUMLINES ; i++)
        
            {
        
                   y = cyChar * (i - iVscrollPos) ;
        
                   TextOut (hdc, 0, y,
        
                           sysmetrics[i].szLabel,
        
                           lstrlen (sysmetrics[i].szLabel)) ;
        
   
        
                   TextOut (hdc, 22 * cxCaps, y,
        
                           sysmetrics[i].szDesc,
        
                           lstrlen (sysmetrics[i].szDesc)) ;
        
   
        
                   SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
        
                   TextOut (hdc, 22 * cxCaps + 40 * cxChar, y, szBuffer,
        
                           wsprintf (szBuffer, TEXT ("%5d"),
        
                                          GetSystemMetrics (sysmetrics[i].iIndex))) ;
        
                   SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
        
        }
        
            EndPaint (hwnd, &ps) ;
        
            return 0 ;
        

    case WM_DESTROY:
        
            PostQuitMessage (0) ;
        
            return 0 ;
        
    }
        
    return DefWindowProc (hwnd, message, wParam, lParam) ;
        
}
        

新的CreateWindow呼叫在第三个参数中包含了WS_VSCROLL窗口样式,从而在窗口中加入了垂直滚动条,其窗口样式为:

WS_OVERLAPPEDWINDOW | WS_VSCROLL
       

WndProc窗口消息处理程序在处理WM_CREATE消息时增加了两条叙述,以设置垂直滚动条的范围和初始位置:

SetScrollRange (hwnd, SB_VERT, 0, NUMLINES – 1, FALSE) ;
       
SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
       

sysmetrics结构具有NUMLINES行文字,所以滚动条范围被设定为0至NUMLINES-1。滚动条的每个位置对应于在显示区域顶部显示的一个文字行。如果卷动方块的位置为0,则第一行会被放置在显示区域的顶部。如果位置大于0,其它行就会出现在显示区域的顶部。当位置为NUMLINES-1时,则最后一行文字出现在显示区域的顶部。

为了有助于处理WM_VSCROLL消息,在窗口消息处理程序中定义了一个静态变量iVscrollPos,这一变量是滚动条内卷动方块的目前位置。对于SB_LINEUP和SB_LINEDOWN,只需要将卷动方块调整一个单位的位置。对于SB_PAGEUP和SB_PAGEDOWN,我们想移动一整面的内容,或者移动cyClient /cyChar个单位的位置。对于SB_THUMBPOSITION,新的卷动方块位置是wParam的高字组。SB_ENDSCROLL和SB_THUMBTRACK消息被忽略。

在程序依据收到的WM_VSCROLL消息计算出新的iVscrollPos值后,用min和max宏来调整iVscrollPos,以确保它在最大值与最小值之间。程序然后将iVscrollPos与呼叫GetScrollPos取得的先前位置相比较,如果卷动位置发生了变化,则使用SetScrollPos来进行更新,并且呼叫InvalidateRect使整个窗口无效。

InvalidateRect呼叫产生一个WM_PAINT消息。SYSMETS1在处理WM_PAINT消息时,每一行的y坐标计算公式为:

cyChar * i
       

在SYSMETS2中,计算公式为:

cyChar * (i – iVscrollPos)
       

循环仍然显示NUMLINES行文字,但是对于非零值的iVscrollPos是负数。程序实际上在显示区域以外显示这些文字行。当然,Windows不会显示这些行,因此屏幕显得干净和漂亮。

前面说过,我们一开始不想弄得太复杂,这样的程序代码很浪费,效率很低。下面我们对此加以修改,但是先要考虑在WM_VSCROLL消息之后更新显示区域的方法。

<br />本文来自【C语言中文网】:<a href=”http://see.xidian.edu.cn/cpp/html/1112.html” target=”_blank”>http://see.xidian.edu.cn/cpp/html/1112.html</a>

C#为RichTextBox控件添加行号

写过程序的人应该不会对RichTextBox感到陌生,与TextBox相比,它封装了更丰富的对象,使你的程序使用起来更加方便。但是它也有美中不足的地方。比如说没有行号,滚动条滚动进度无法控制等一系列的问题。

今天,就据我所熟悉的,向大家介绍介绍.希望达到抛砖引玉的效果,有不足的地方,还请大家积极指出来!

首先介绍如何为自己的RichTextBox增加行号。

新建一个用户控件类(UserControl),取名叫“AdaScrollRichTextBox ”继承自“System.Windows.Forms.RichTextBox ”。我们要给这个RichTextBox类加上行号,有一个比较巧妙的办法。那就是在这个类里再加上一个RichTextBox类。如下面这个方法:
         private RichTextBox   _LineId;
        private void InitializeLineId()
        {
            _LineId = new RichTextBox ();
            _LineId.Name = “LineId”;
            //_LineId.Text = “1”;
            _LineId.Width = 40;
            _LineId.Dock = DockStyle.Left;
            _LineId.Font = this.Font;
            _LineId.ForeColor = Color.Green;
            _LineId.ReadOnly = true;
            _LineId.BackColor = Color.White;
            _LineId.TabStop = false;
            _LineId.BorderStyle = BorderStyle.None;
            _LineId.ScrollBars = RichTextBoxScrollBars.None;
            _LineId.Cursor = Cursors.Arrow;
            this.SelectionIndent = 40;//由于左边行号占用了40象素,所以AdaScrollRichTextBox 需要向右边缩进40象素。
            this.Controls.Add(_LineId);//注意这里,将_LineId添加进AdaScrollRichTextBox Controls。
        }

在AdaScrollRichTextBox 的构造函数里调用这个方法,就可以实现行号了。

为了便于动态的绑定行号,最好是重写AdaScrollRichTextBox 的OnTextChanged方法。如下所示:
        protected override void OnTextChanged(EventArgs e)
        {
            int liLineCount = this.Lines.Length;
            for (int i = 1; I <= liLineCount; i++)
            {
                this._LineId.AppendText(i.ToString() + “\r\n”);
            }
            base.OnTextChanged(e);
        }

这样,当你在为AdaScrollRichTextBox 赋值的时候,便会自动为你添加行号。值得注意的是,前面我们提了,RichTextBox的ScrollBar有问题,不能控制滚动条的滚动进度。所以,程序写到目前,在效果上还是有些问题的。细心的人就会发现,当拉动滚动条的时候,行号不能跟随着滚动条的滚动一起滚动,更加不要提鼠标的滚轮了!

我们新建一个类,他继承自RichTextBox,取名“adaLineIdTxt ”。
我们知道Windows事件处理都是消息机制,你触动键盘、拖动鼠标、拖动滚动条,都会触发消息。拖动滚动条?消息?对,拖动滚动条的消息就是“Message.Msg”里的“WM_HSCROLL = 0x0114”和“WM_VSCROLL = 0x0115”。只要我们捕获到这两个消息,就可以控制拖动滚动条的滚动了。

首先,我们定义一个委托
public delegate void SendMessage(Message poMsg);
光有委托不行,我们还得定义一个事件
public event SendMessage SendMessageEvent;

以上做好之后,我们还需要重载WndProc方法。
        protected override void WndProc(ref Message m)
        {
            if ( m.Msg == WM_VSCROLL)
            {
                if (SendMessageEvent != null)
                {
                    SendMessageEvent(m);
                }
            }
            base.WndProc(ref m);
        }

//如果拖动滚动条是垂直方向(因为这里只让行号滚动,所以只有垂直方向),就触发消息。
定义一个滚动方法。
        public void Scroll(Message poMsg)
        {
            if (this.Handle != null)
            {
                poMsg.HWnd = this.Handle;
                WndProc(ref poMsg);
            }
        }
        这样,我们的行号随滚动条滚动的功能就基本上实现了。下面是完整代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace AdaEniac
{
    public delegate void SendMessage(Message poMsg);
    public partial class adaLineIdTxt : RichTextBox
    {
        private int WM_HSCROLL = 0x0114;
        private int WM_VSCROLL = 0x0115;
        public event SendMessage SendMessageEvent;
        public adaLineIdTxt()
        {
            InitializeComponent();
        }
        protected override void WndProc(ref Message m)
        {
            if ( m.Msg == WM_VSCROLL)
            {
                if (SendMessageEvent != null)
                {
                    SendMessageEvent(m);
                }
            }
            base.WndProc(ref m);
        }
        public void Scroll(Message poMsg)
        {
            if (this.Handle != null)
            {
                poMsg.HWnd = this.Handle;
                WndProc(ref poMsg);
            }
        }
    }
}

需要说明的是,我前天写的“RichTextBox知识–为RichTextBox添加行号”需要做部分修改。将“_LineId”换成“adaLineIdTxt ” 类型。然后,还需要在“AdaScrollRichTextBox ”里增加一个方法
        public void LineIdScroll(Message poMsg)
        {
            if (poMsg.Msg == (int)wm.WM_VSCROLL)
            {
                _LineId.Scroll(poMsg);
            }
        }

在你使用该控件“AdaScrollRichTextBox ”的时候,需要调用其“SendMessageEvent”事件。在该事件里,可以让行号随滚动条滚动起来。

如例private void txtView_SendMessageEvent(Message poMsg)
        {
            this.txtView.LineIdScroll(poMsg);
        }

至此,我们为RichTextBox增加的行号已经可以随滚动条的滚动而滚动了。但是还是存在些问题,什么问题呢?

那就是滚轮滑动时,行号没滚动。

在  RichTextBox知识(二)里,我们为RichTextBox添加了随滚动条滚动的行号,但是滚轮滚动的时候,行号却没有跟随滚动!今天我们再让行号随滚轮滚动起来.具体代码见下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace AdaWinControls
{
    public partial class adScrollRichTextBox : System.Windows.Forms.RichTextBox
    {
        public event SendMessage SendMessageEvent;
        private bool _AutoLineId = true;
        public bool AutoLineId
        {
            set
            {
                _AutoLineId = value;
            }
            get
            {
                return _AutoLineId;
            }
        }
        private adLineIdTxt _LineId;
        public adLineIdTxt LineId
        {
            get
            {
                return _LineId;
            }
        }
        private bool _IsChecked = false;
        public bool IsChecked
        {
            get
            {
                return _IsChecked;
            }
            set
            {
                _IsChecked = value;
            }
        }
        public adScrollRichTextBox()
        {
            InitializeComponent();
            InitializeLineId();
        }
        private void InitializeLineId()
        {
            _LineId = new adLineIdTxt();
            _LineId.Name = “LineId”;
            _LineId.Text = “1”;
            _LineId.Width = 40;
            _LineId.Dock = DockStyle.Left;
            _LineId.Font = this.Font;
            _LineId.ForeColor = Color.Green;
            _LineId.ReadOnly = true;
            _LineId.BackColor = Color.White;
            _LineId.TabStop = false;
            _LineId.BorderStyle = BorderStyle.None;
            _LineId.ScrollBars = RichTextBoxScrollBars.None;
            _LineId.Cursor = Cursors.Arrow;
            this.SelectionIndent = 40;
            this.Controls.Add(_LineId);
        }
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == (int)ApiVariable.WM_HSCROLL || m.Msg == (int)ApiVariable.WM_VSCROLL
                || m.Msg == (int)ApiVariable.WM_MOUSEWHEEL || m.Msg == (int)ApiVariable.EM_SCROLL
                || m.Msg == (int)ApiVariable.BM_CLICK)
            {
                if (SendMessageEvent != null)
                {
                    SendMessageEvent(m);
                }
            }
            base.WndProc(ref m);
        }
        public void Scroll(Message poMsg)
        {
            if (this.Handle != null)
            {
                poMsg.HWnd = this.Handle;
                WndProc(ref poMsg);
            }
        }
        public void LineIdScroll(Message poMsg)
        {
            if (poMsg.Msg == (int)ApiVariable.WM_VSCROLL)
            {
                _LineId.Scroll(poMsg);
            }
        }
        public void NewLine(string pcLineNo, string pcLine, Color poLineColor)
        {
            NewLine(pcLineNo, pcLine, poLineColor, Color.White);
        }
        public void NewLine(string pcLineNo, string pcLine, Color poLineColor, Color poBackColor)
        {
            this.SelectionIndent = 40;
            this.SelectionColor = poLineColor;
            this.SelectionBackColor = poBackColor;
            this.AppendText(pcLine + “\r\n”);
            this._LineId.NewLineNo(pcLineNo);
        }
        public void SetValue(string pcAllLine)
        {
            this.SelectionIndent = 40;
            this.Text = pcAllLine;
            this._LineId.Clear();
            for (int i = 1; i <= this.Lines.Length; i++)
            {
                this._LineId.NewLineNo(i.ToString());
            }
        }
        protected override void OnSizeChanged(EventArgs e)
        {
            _LineId.Top–;
            base.OnSizeChanged(e);
        }
        protected override void OnTextChanged(EventArgs e)
        {
            this.SelectionIndent = 40;
            this.SelectionHangingIndent = 40;
            if (_AutoLineId)
            {
                this._LineId.Clear();
                for (int i = 0; i < this.Lines.Length; i++)
                {
                    this._LineId.NewLineNo(i.ToString());
                }
            }
            base.OnTextChanged(e);
        }
        #region RichTextBox_ScrollVariable
        private int _VPrePosition = 0;
        private int _HPrePosition = 0;
        // =================================================================== 
        // for NativeWindow and PostMessageA
        private const int WM_HSCROLL = 0x114;
        private const int WM_VSCROLL = 0x115;
        private const int WM_MOUSEWHEEL = 0x20A;
        private const int WM_COMMAND = 0x111;
        // =================================================================== 
        // for GetScroll and PostMessageA
        private const int WM_USER = 0x400;
        private const int SBS_HORZ = 0;
        private const int SBS_VERT = 1;
        // =================================================================== 
        // for SubClassing
        private const int SB_THUMBPOSITION = 4;
        // =================================================================== 
        // API Function: GetScrollPos()
        [DllImport(“user32.dll”)]
        private static extern int GetScrollPos(IntPtr hWnd, int nBar);
        // =================================================================== 
        // API Function: PostMessageA()
        [DllImport(“user32.dll”)]
        private static extern bool PostMessageA(IntPtr hwnd, int wMsg, int wParam, int lParam);
        #endregion
        protected override void OnVScroll(EventArgs e)
        {
            VerticalScrollNew(this, this._LineId);
            base.OnVScroll(e);
        }
        private void VerticalScrollNew(RichTextBox sender, RichTextBox receiver)
        {
            int liPosition = GetScrollPos(sender.Handle, SBS_VERT);
            if (liPosition != _VPrePosition)
            {
                this._VPrePosition = liPosition;
                PostMessageA(receiver.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * liPosition, 0);
            }
        }
    }
}

我们在保持前一篇截取滚动条消息的同时,引用Windows API.用它来控制行号跟随滚轮滚动.

这里需要重写”adScrollRichTextBox” 的 OnVScroll(EventArgs e)方法.有人问,为什么不连带重写OnHScroll方法呢?原因很简单,因为我们的行号只需要操作上下滚动,左右滚动就没必要了.除非你想玩BT点,让你的行好忽隐忽现.

至此,我们已经顺利的为RichTextBox添加上行号了!

我上传了资源,见地址:http://download.csdn.net/source/1078930

详细介绍C++中的虚函数和动态联编

面向对象程序设计的基本观点是用程式来仿真大千世界,这使得它的各种根本特性非常人性化,如封装、继承、多态等等,而虚函数就是c++中实现多态性的主将。为了实现多态性,c++编译器也革命性地提供了动态联编(或叫晚捆绑)这一特征。

虚拟函数亦是mfc编程的关键所在,mfc编程主要有两种方法:一是响应各种消息,进行对应的消息处理。二就是重载并改写虚拟函数,来实现自己的某些要求或改变系统的某些默认处理。
虚函数的地位是如此的重要,对它进行穷根究底,力求能知其然并知其所以然 对我们编程能力的提高大有好处。下面且听我道来。

多态性和动态联编的实现过程分析

一、基础略(限于篇幅,请参阅相应的c++书籍):

1、多态性:使用基础类的指针动态调用其派生类中函数的特性。

2、动态联编:在运行阶段,才将函数的调用与对应的函数体进行连接的方式,又叫运行时联编或晚捆绑。

二、过程描述:

1、编译器发现一个类中有虚函数,编译器会立即为此类生成虚拟函数表 vtable(后面有对vtable的分析)。虚拟函数表的各表项为指向对应虚拟函数的指针。

2、编译器在此类中隐含插入一个指针vptr(对vc编译器来说,它插在类的第一个位置上)。

有一个办法可以让你感知这个隐含指针的存在,虽然你不能在类中直接看到它,但你可以比较一下含有虚拟函数时的类的尺寸和没有虚拟函数时的类的尺寸,你能够发现,这个指针确实存在。

3、在调用此类的构造函数时,在类的构造函数中,编译器会隐含执行vptr与vtable的关联代码,将vptr指向对应的vtable。这就将类与此类的vtable联系了起来。

4、在调用类的构造函数时,指向基础类的指针此时已经变成指向具体的类的this指针,这样依靠此this指针即可得到正确的vtable,从而实现了多态性。在此时才能真正与函数体进行连接,这就是动态联编。

三、vtable 分析

分析1:虚拟函数表包含此类及其父类的所有虚拟函数的地址。如果它没有重载父类的虚拟函数,vtable中对应表项指向其父类的此函数。反之,指向重载后的此函数。

分析2:虚拟函数被继承后仍旧是虚拟函数,虚拟函数非常严格地按出现的顺序在 vtable 中排序,所以确定的虚拟函数对应 vtable 中一个固定的位置n,n是一个在编译时就确定的常量。所以,使用vptr加上对应的n,就可得到对应函数的入口地址。

四、编译器调用虚拟函数的汇编码(参考think in c++):
push funparam ;先将函数参数压栈
push si ;将this指针压栈,以确保在当前类上操作
mov bx,word ptr[si] ;因为vc++编译器将vptr放在类的第一个位置上,所以bx内为vptr
call word ptr[bx+n] ;调用虚拟函数。n = 所调用的虚拟函数在对应 vtable 中的位置
纯虚函数:

一、引入原因:

1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。

2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual returntype function()= 0;),则编译器要求在派生类中必须予以重载以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。

二、纯虚函数实质:

1、类中含有纯虚函数则它的vtable表不完全,有一个空位,所以,不能生成对象(编译器绝对不允许有调用一个不存在函数的可能)。在它的派生类中,除非重载这个函数,否则,此派生类的vtable表亦不完整,亦不能生成对象,即它也成为一个纯虚基类。
虚函数与构造、析构函数:

1、构造函数本身不能是虚拟函数;并且虚机制在构造函数中不起作用(在构造函数中的虚拟函数只会调用它的本地版本)。
想一想,在基类构造函数中使用虚机制,则可能会调用到子类,此时子类尚未生成,有何后果!?。

2、析构函数本身常常要求是虚拟函数;但虚机制在析构函数中不起作用。

若类中使用了虚拟函数,析构函数一定要是虚拟函数,比如使用虚拟机制调用delete,没有虚拟的析构函数,怎能保证delete的是你希望delete的对象。

虚机制也不能在析构函数中生效,因为可能会引起调用已经被delete掉的类的虚拟函数的问题。

对象切片:

向上映射(子类被映射到父类)的时候,会发生子类的vtable 完全变成父类的vtable的情况。这就是对象切片。
原因:向上映射的时候,接口会变窄,而编译器绝对不允许有调用一个不存在函数的可能,所以,子类中新派生的虚拟函数的入口在vtable中会被强行“切”掉,从而出现上述情况。

虚拟函数使用的缺点

优点讲了一大堆,现在谈一下缺点,虚函数最主要的缺点是执行效率较低,看一看虚拟函数引发的多态性的实现过程,你就能体会到其中的原因。

希望通过以上内容的介绍,能够给你带来帮助。