RSS
 

Archive for the ‘12.技术’ Category

vim查找替换

03 十一


  1. 行内搜索。

    1. f命令可以进行行内搜索。输入fx可以找到下一个x字符。
    2. F命令可以在反方向进行行内搜索,输入Fx可以找到上一个x字符。
    3. t命令同样是进行行内搜索,但是光标停留在符合条件的字符的前面。输入tx使光标停留在下一个x字符的前面。
    4. T命令可以在反方向进行行内搜索,但是光标停留在符合条件的字符的下一个字符上。输入tx使光标停留在上一个x的后面的字符上。
    5. %可以搜索与之匹配的对应的()[]{}。这个功能对于书写程序特别有用。

  2. 全文的搜索。键入/后,光标进入VIM底部的命令行,这时就可以输入以/为开始的/搜索命令了。

    1. 最简单的查找。输入/string就可以查找string字符串。
    2. 继续查找。在第一次搜索后输入n将会继续进行上一次搜索,如果输入3n那么会找到后面第三个符合的字符串。大写的N会在反方向,也就是向上进行搜索。
    3. 向上搜索。输入?可以向上搜索。随后的n命令将会继续向上搜索,N向下搜索,其他和/一样。
    4. 对大小写的匹配。

      1. 在命令行输入:set ignorecase可以设置搜索以忽略大小写的方式进行。输入:set noignorecase可以设置搜索以匹配大小写的方式进行。默认的,将匹配大小写。这个默认设置可以在.vimrc中修改。
      2. 大小写智能匹配模式。输入命令:set ignorecase smartcase可以设置为智能大小写匹配模式。在这种模式下,如果你输入的字符串中至少包含一个大写字母,那么就会以大小写敏感模式进行搜索,否则以忽略大小写模式进行搜索。
      3. 在搜索命令中指定大小写。在几种大小写搜索模式中进行转换总要输入长长的一串指令,如果需要不断地转换模式,确实有些麻烦。所以你可以直接在搜索命令中指定大小写匹配模式。\c表示忽略大小写,而\C表示对大小写敏感。

    5. 查找当前词。一个简单的方法可以让你查找下一个和当前词一样的词,你不比输入/currentword,你可以直接按下*就可以查找下一个currentword。#可以让你向上查找同一个单词。
    6. 调整查找后的光标位置。

      1. 使用/string/3会使在光标转到找到string字符串的行下面的第3行。第1行从包含string的行算起。同样可以使用/string/-2来使光标停留在包含string的行的前面2行。
      2. 使用/string/e可以使光标停留在string字符串的末尾而不是默认的第一个字符位置。/string/b则表示停留在string第一个字符的位置,不过默认就是如此,我们不比多此一举。但是/string/b+2却可以使光标停留在string字符串的第二个字符的位置,这里是r。在e或者b的后面+或者-数字,可以进一步调整光标的位置。

    7. 特殊字符。在搜索命令中,  .*[]^%/?~$这10个字符有着特殊意义,所以在使用这些字符的时候要在前面加上一个反斜杠/。而\e表示<esc>;\t表示<tab>;\r表示<cr>;\b表示<bs>。
    8. 匹配换行或空格。使用\n可以表示一个换行;而\s表示匹配空白,注意是空白不是空格。\_s表示匹配换行或者空格;\_a表示匹配换行或者一个字母。比如:/the\nword查找以the结束并且下一行以word开始的行的位置。/the\_sword查找the和word之间以空白或者换行分割的位置。/the\_s\+word表示the和word之间可以有多个空白。\+的含义可以在后面找到。
    9. 限定词首或者词尾。\<可以限定找到是以指定字符串开始的单词。比如/\<the可以找到以the开始在字符串,但是会忽略在单词中间包含the的字符串。而\>则是限定必须是以指定字符串结尾的单词。
    10. 行首和行尾。输入/^string查找以string开始的行,并且光标停留在这个string的第一个字符。输入/string$则查找以string结束的行,并将光标停留在这个string的第一个字符。这个模式不会忽略前导或者后置的空格。
    11. 匹配任何一个字符。句号.可以匹配任何一个字符。例如/t.e可以找到the或者tae或者tue。

  3. 更加复杂的匹配模式。实际上,/搜索可以使用非常复杂的匹配模式。下面列出了其中一些复杂的匹配模式。

    1. 可重复的字符。

      1. 使用*表示它前面的字符可以重复多次或者0次。比如/be*可以匹配b,be,bee,beee等等。但是这也会包含b,因为e重复0后是空。如果要匹配诸如be,bebe,bebebe的字符串可以使用\(和\)把他们包括起来,形如:/\(be\)*。
      2. 使用\+来限定为重复至少一次到无数次。如:/be\+可以匹配be,bee,bee等等。
      3. 重复0次或者一次。\=可以指定重复0次或者一次。/strings\=表示匹配string或者strings。这在查找单词的复数形式特别有用.
      4. 指定重复的次数。\{n,m}可以指定重复n到m次。比如:/be\{2,4}将会匹配bee,beee,beeee。同样可以使用\{,4}表示匹配0到4次;\{4,}表示4次到无数次;\{4}表示重复4次。

    2. 匹配列表中的一个。使用一个以\|分割的列表,可以表示匹配列表中任何一项。比如/one\|two表示查找one或者two;/one\|two\|three表示查找one或者two或者three。

      1. 一个复杂的实例:/end\(if\|while\|for\)将会查找endif,endwhile以及endfor。
      2. 字符列表。使用[0-9]可以表示一个0到9范围的字符。比如/string[1-5]表示查找string1,string2,string3,string4,string5。当然也可以使用[a-z]表示字符a到z中间的一个。
      3. 特殊的字符集合。使用\d也可以表示0到9的数字,这个选项可以代替[0-9]。\D表示非数字代替[^0-9];\x表示十六进制数代替[0-9a-fA-F];\X表示非十六进制数代替[^0-9a-fA-F];\s表示空白字符代替[ ] (<Tab> 和<Space>);\S表示非空白字符代替[^ ] (除<Tab> 和<Space>之外);\l表示小写字母代替[a-z];\L表示非小写字母代替[^a-z];\u表示大写字母代替[A-Z];\U表示非大写字母代替[^A-Z]

  4. 搜索并替换。输入:[range]s/from/to/[flags]命令可以搜索匹配的字符串并且替换为指定的字符串。这是一个稍微复杂的命令。其中[range]是一个可选项,用来指定搜索替换的范围。[flags]也是一个可选项,用来指定搜索替换的处理方式。所以这个命令的简单形式是:s/from/to/。

    1. 搜索替换的范围。如果没有指定范围,则只在当前行进行搜索替换。

      1. 在所有行进行搜索替换。范围符号%表示在所有行进行搜索替换。:%s/from/to/就是在全文查找from并替换为to。
      2. 在指定的行上进行搜索替换。:1,50s/from/to/表示在第1行和第50行之间(包括1和50行)进行搜索和替换。:45s/from/to/表示仅仅在第45行进行搜索和替换。而”1,$”行号范围和“%“是等价的。

    2. 处理方式。我现在使用的VIM默认的方式是无须确认。不过好像不同版本的默认的处理方式是不同的。

      1. 无须确认。方式符号g表示直接替换无须进行确认。:%s/from/to/g表示在全文中查找from并且直接全部替换为to。
      2. 列印。处理方式符号p表示在替换时列出每个被改变的行。手册中这么说,但是实际效果却让我很迷惑。
      3. 确认后处理。处理方式符号c表示在进行替换前需要用户进行确认。这时你可以选择(y/n/a/q/1/^E/^Y):y表示同意当前替换;n表示不同意当前替换;a表示替换当前和后面的并且不再确认;q表示立即结束替换操作;1表示把当前的替换后结束替换操作;^E向上滚屏^Y向下滚屏,用来帮助查看前后内容以决定进行操作。

摘自:http://jnote.cn/blog/read/vim-search.html

 
 

IE自动通过代理服务器访问某些网站

05

Auto Proxy Config 自动通过代理服务器访问某些网站:

在C盘根目录下新建文件proxy.pac,用写字板写入如下内容:
function FindProxyForURL(url, host)
{
if (dnsDomainIs(host, “.acs.org”))
return “PROXY 10.1.1.20:8080; DIRECT”;
else if (dnsDomainIs(host, “.nature.com”))
return “PROXY 10.1.1.2:80; DIRECT”;
else
return “DIRECT”;
}
在浏览器中,菜单”工具”-”Internet选项”-”连接”-”局域网设置”,不勾”自动检测设置”和”为LAN使用代理服务器。。”,勾上”使用自动配置脚本”,在地址后面填上file://c:\proxy.pac

Read the rest of this entry »

 
 

UltraEdit 正则表达式

17

UltraEdit 允许在搜索菜单下面列出了的许多搜索和替换功能中使用正则表达式。正则表达式能让更多的复杂的搜索和替换功能变成简单的操作。(中文版界面上显示为“正规表达式”)
有两个可使用的语法集合。下面的第一表显示出在 UltraEdit 的更早的版本被使用的原来的 UltraEdit 句法。第二表给出了可选的”Unix”类型的正则表达式。这可以从配置单元启用。

符号  功能
%     匹配行的开始 – 显示搜索字符串必须在行的开始,但是在所选择的结果字符串中不包括任何行终止字符。
$     匹配行尾 – 显示搜索字符串必须在行尾,但是在所选择的结果字符串中不包括任何行终止字符。
?     除了换行符以外匹配任何单个的字符
*     除了换行符匹配任何数量的字符和数字
+     前一字符匹配一个或多个,但至少要出现一个
++    前一字符匹配零个或多个,但至少要出现一个
^b    匹配一个分页
^p    匹配一个换行符(CR/LF)(段)(DOS文件)
^r    匹配一个换行符(CR 仅仅)(段)(MAC 文件)
^n    匹配一个换行符 ( LF 仅仅 )( 段 )( UNIX 文件 )
^t    匹配一个标签字符TAB
[]    匹配任何单个的字符,或在方括号中的范围
^{A^}^{ B^} 匹配表达式A或 B
^     重载其后的正规表达式字符
^(^)  括或标注为用于替换命令的表达式。

一个正则表达式最多可以有9个标注表达式, 按正规表达式的需要而定。
相应的替换表达式是 ^x , 替换范围x是1-9。例如:
If ^(h*o^) ^(f*s^) matches “hello folks”,
^2 ^1 would replace it with “folks hello”.

(hello folks 将被替换成 folks hello。)

注: ^ 是实际字符 ^不是Ctl + 键值。

例如:
m?n 匹配 “man”,”men”,”min” 但不匹配 “moon”.
t*t 匹配 “test”,”tonight” 和 “tea time” (the “tea t” portion) 但不匹配 “tea
time” (newline between “tea ” and “time”).
Te+st 匹配 “test”,”teest”,” teeeest “等等。但是不匹配 “tst”。
[aeiou]  匹配每个小写元音。
[,.?]  匹配一文字的 “,”,”.”或 “?”。
[0-9, a-z] 匹配任何数位,或小写字母。
[~0-9] 除了数字以外匹配任何字符 (~ 意味着”不”)

你按如下方式可以查找一个表达式A或 B :

“^{John^}^{Tom^}”

这将在找John或Tom的出现。应该在 2 个表达式之间没有任何东西。

你可以在同一搜索中按如下方式组合A or B and C or D:

“^{John^}^{Tom^}^{Smith^}^{Jones^}”

这将在John or Tom 后面找 Smith or Jones。


下表为”Unix”句法类型的正则表达式。

正则表达式 (Unix句法):

符号        功能
          标记下一个字符作为一个特殊的字符。
“n”         匹配字符”n”。”n” 一个换行符或换行符字符。
^           匹配/定位行的开始。
$           匹配/定位行的尾。
*           匹配前面的字符零次或多次。例
+           匹配前面的字符一次或多次。例
.           匹配除了一个换行符字符匹配任何单个的字符。
(expression)标注用于替换命令的表达式。一个正则表达式根据需要,最多可以有9个标注表达式。相应的代替表达式是 x , x的范围是 1-9 。


例如:

If (h.*o) (f.*s) matches “hello folks”,
2 1 would replace it with “folks hello”.
(hello folks 将被替换成 folks hello。)


[xyz]       一个字符集。匹配在方括号之间的任何字符。
[^xyz]      一个否定的字符集。不匹配在方括号之间的任何字符。
d          匹配一个数字字符。等价于[0-9]。
D          匹配一个非数字字符。等价于[^0-9]。
f          匹配一个换页字符。
n          匹配一个换行字符。
r          匹配一个回车符字符。
s          匹配任何空白的空格, 标签, 换页, 包括空格等等,但不匹配换行符。
S          匹配任何非空白的字符,但不匹配换行符。
t          匹配一个标签TAB字符。
v          匹配一个垂直的标签字符。
w          匹配任何词语字符包括下划线。
W          匹配任何非词语字符字符。

注: ^ 是实际字符 ^不是Ctl + 键值。


例如:
m.n       匹配 “man”,”men”,”min” 但不匹配 “moon”.
t+t       匹配 “test”,”tonight” 和 “tea time” (the “tea t” portion) 但不匹配 “tea
time” (newline between “tea ” and “time”).
Te*st     匹配 “test”,”teest”,” teeeest “等等。但是不匹配 “tst”。
[aeiou]   匹配每个小写元音。
[,.?]     匹配一文字的 “,”,”.”或 “?”。
[0-9,a-z] 匹配任何数位,或小写字母。
[^0-9]    除了数字以外匹配任何字符 (~ 意味着”不”)


你按如下方式可以查找一个表达式A或 B :

“(John)|(Tom)”

这将在找John或Tom的出现。应该在 2 个表达式之间没有任何东西。

你可以在同一搜索中按如下方式组合A or B and C or D:

“(John|Tom) (Smith|Jones)”

这将在John or Tom 后面找 Smith or Jones。


另外:

p        匹配 CR/LF ( 作为 rn 的一样 ) 作为DOS行结束符匹配

如果查找/替换功能中正则表达式没有选用,则替换字段中下列字符也是有效的:

符号   功能

^^          匹配一个 “^” 字符
^s          替换为被选择 ( 加亮 ) 活跃的文件窗口的文章。
^c          替换为剪贴板的内容
^b          匹配一个页裂缝
^p          匹配一个换行符 ( CR/LF )( 段 )( DOS 文件)
^r          匹配一个换行符 ( CR 仅仅 )( 段 )( MAC 文件)
^n          匹配一个换行符 ( LF 仅仅 )( 段 )( UNIX 文件)
^t          匹配一个标签TAB字符

 
 

DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0//EN”

14

有DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0//EN”和无指定时的css差异。


一、width=100%


未指定DOCTYPE时:


块的总宽度是上级块的内容宽度,
块的内容宽度在指定 margin-left 与 margin-right 的中间。


指定了DOCTYPE时:


块的总宽度是 margin-left + margin-right + width(width指定的是 margin-left 与 margin-right 中间的内容区;
如果想实现未指定DOCTYPE时的width=100%的效果需要 left=0; right=0。


二、SCOROLLBAR


未指定DOCTYPE时,关于 SCROLLBAR 的相关 CSS 可以写在 body 或 html 里。


指定了DOCTYPE时,必须要写在 html 里:


 SCROLLBAR-FACE-COLOR: #F0F0F0;
 SCROLLBAR-HIGHLIGHT-COLOR: #FFFFFF;
 SCROLLBAR-SHADOW-COLOR: #E0E0E0;
 SCROLLBAR-3DLIGHT-COLOR: #000000;
 SCROLLBAR-ARROW-COLOR: #000000;
 SCROLLBAR-TRACK-COLOR: #cccccc;
(只对IE有效)


三、display:inline-block 和 display:block 是有区别的

 
 

【技术】C++Builder, Use dynamic RTL

26

这几天在弄一个调用了WMSDK和引用了Windows Media Player 9/10 组件的ActiveX组件。生成了一个 .ocx,将要发布时在一台 Windows 2000 with SP4 + WMP9 的“干净”机器上测试时,发现这个 ocx 文件不能通过 regsvr32 注册(缺少dll那种错误)。


找了好久的原因……,主要呐闷的是:同样条件编译的.exe应用程序在同样的环境中运行没有问题,但这个ocx就是不能注册。


通过 UltraEdit 查看 .ocx 的16进制内容,及用 tdump -em. 发现在这个 .ocx 中有两个不常见的 dll:wmvcore.dll、vcltest3.dll 和 stlpmt45.dll。


其中,wmvcore.dll 在安装 WMP9 后就出现的 dll,应该没有问题。后两个文件缺是 C++Builder 自己的 bin 目录中的。为了测试,将这两个文件复制到 ocx 的目录中、system32 目录中,ocx还是不能注册(ocx换方案编译也不行)。


一气之下,将 C++Builder bin 下的所有内容都复制到 ocx 目录中……,竟然注册成功了——确定了问题就在这些库中,一点一点的查,最终发现 ocx 的注册需要 cc3260mt.dll、stlpmt45.dll,那个 vcltest3.dll 却不是必须。


在网上查资料:cc3260mt 似乎是关于 Use dynamic RTL 选择打开后,使用 MultiThread 编译的程序所需的,stlpmt45.dll 到是没有查到什么结果(我一直以为它是 STL 的什么库,后来确认程序通过他来调用 cc3260mt.dll)。


关于 Use dynamic RTL:


默认情况下使用 C++Builder 开发编译的应用程序,是需要一些 Borland 的运行时库来支持的。像我这样从Delphi“转型”过来的人应该知道:将 Project->Options->Packages 里的 Build with runtime packages 取消选中,可以脱离 Borland Package Library 运行时库。


但在 C++Builder 里光去掉上面这项还不能完全解脱。Project->Options->Linker->Use dynamic RTL 选项也去掉(印象中如果不去掉它会需要 BorlandMM.dll)。


看到网上的资料说 cc3260mt 也是用于对 dynamic RTL 支持的,便在 DLL项目中左看右看,也没看到那个 Use dynamic RTL 打着勾。


这个 DLL 项目使用了两个自己开发的 .lib,这两个 .lib 也用于 EXE 项目的开发,所以一直没有想问题会不会出在 .lib 上(尽管也更改过一些选项重新编译过这两个 .lib)。


翻来覆去的看,突然发现:原来 LIB 项目的 Project->Options->TLib 页里,也有一个 Use dynamic RTL 项,把这两个 LIB 项目的这个选项去掉,再编译生成 .ocx ——一切正常啦……


还是奇怪为什么 .exe 的应用就不受 .lib 中的这个选项的影响。是已经影响了我没发现?或是 .exe 或 .ocx 编译时哪个编译器有 BUG?呵呵,又是说人家的错,还是别没事就把错归结到人家头上。有错先找自己原因,反正现在的问题解决了,先睡觉再说。


写这篇文章地目的是:


多写一篇凑数(好长时间没写了),希望提交时别报错。

 
 

【技术】Indy 10 的问题

30

用 Indy10.0.52 写 FTP Server


错误发现:


在 OnChangeDirectory 事件中,raise 自定义异常,在客户端看到错误消息,但命令反馈代码为 200,相当时客户端认为此命令正常执行。


错误研究:


原有程序 IdCommandHandlers.pas 第441行:
          if ExceptionReply.Code = ” then begin
            Reply.Assign(Self.ExceptionReply);
          end;
          // If still no go, from server
          // Can be nil though. Typically only servers pass it in
          if (ExceptionReply.Code = ”) and (TIdCommandHandlers(Collection).FExceptionReply <> nil)
           then begin
            ExceptionReply.Assign(TIdCommandHandlers(Collection).FExceptionReply);
          end;
          if ExceptionReply.Code <> ” then
          begin
            Reply.Text.Add(E.Message);
            SendReply;
          end else begin
            raise;
          end;


当异常抛出后,ExceptionReply.Code 常为 550,而在此部分的运行中,SendReply必将一个没有定义过反馈代码的 Reply 发向客户端。


经分析,当 ExceptionReply.Code 为空字符串时,向 Reply 赋予 ExceptionReply 的值,显然是不何理的。而且异常发生时 ExceptionReply.Code 的值又常是 ’550′。


怀疑是 Indy 的错误,所以将441行:
          if ExceptionReply.Code = ” then begin
改为:
          if ExceptionReply.Code <> ” then begin


可以解决发现的问题。(注释代码:{F34C2328-8505-4FFE-9A94-9D7F8E67E46D})


问题延伸:


这似乎是 Indy10.0.52 IdFTPServer 的一个bug(也不排除我对它的功能理解错误的情况),IdFTPServer.ExceptionReply 属性似乎在使用中没有用处。


先记录如上,暂时解决问题,以后有时间再深入研究。

 
 

【学技术】看 SQL Server 2005 新功能是看到有关树结构处理的东东

10 十一

WITH EmpCTE(empid, empname, mgrid, lvl, sortcol)
AS
(
  SELECT empid, empname, mgrid, 0,
    CAST(empid AS VARBINARY(900))
  FROM Employees
  WHERE empid = 1
  UNION ALL
  SELECT E.empid, E.empname, E.mgrid, M.lvl+1,
    CAST(sortcol + CAST(E.empid AS BINARY(4)) AS VARBINARY(900))
  FROM Employees AS E
    JOIN EmpCTE AS M
      ON E.mgrid = M.empid
)
SELECT
  REPLICATE(‘ | ‘, lvl)
    + ‘(‘ + (CAST(empid AS VARCHAR(10))) + ‘) ‘
    + empname AS empname
FROM EmpCTE
ORDER BY sortcol
(1) Nancy
 | (2) Andrew
 |  | (5) Steven
 |  | (6) Michael
 | (3) Janet
 |  | (7) Robert
 |  |  | (11) David
 |  |  |  | (14) James
 |  |  | (12) Ron
 |  |  | (13) Dan
 |  | (8) Laura
 |  | (9) Ann
 | (4) Margaret
 |  | (10) Ina


吓了一跳,呵呵,不知真正用起来效率如何。反正试了一起想用的 通过 ROW_NUMBER 来分页,效率太低,挺失望的,带没找到提高效率的办法。

 
 

【技术】.NET 2.0 在 IHttpHandlerFactory 中得到的

25

IHttpHandler GetHandler( HttpContext context, string requestType, string url, string path )


下面是在实现 IHttpHandlerFactory 的类中 GetHandler 函数中得到的值(Web.Config 中已经在 HttpHandlers 设置中,将 path=* 映射到了这个HttpHandlerFactory 上:


访问地址:http://localhost/myweb/
path: d:\vhosts\myweb\
url: /myweb/
context.Request.Path: /myweb/


访问地址:http://localhost/myweb/a
path: d:\vhosts\myweb\a
url: /myweb/a
context.Request.Path: /myweb/a


访问地址:http://localhost/myweb/a/
path: d:\vhosts\myweb\a
url: /myweb/a/
context.Request.Path: /myweb/a/


访问地址:http://localhost/myweb/a/
path: d:\vhosts\myweb\a\
url: /myweb/a/
context.Request.Path: /myweb/a/


访问地址:http://localhost/myweb/a.aspx
path: d:\vhosts\myweb\a.aspx
url: /myweb/a.aspx
context.Request.Path: /myweb/a.aspx


访问地址:http://localhost/myweb/a.aspx?b=c
path: d:\vhosts\myweb\a.aspx
url: /myweb/a.aspx
context.Request.Path: /myweb/a.aspx


访问地址:http://localhost/myweb/a.aspx/b
path: d:\vhosts\myweb\a.aspx
url: /myweb/a.aspx
context.Request.Path: /myweb/a.aspx/b


访问地址:http://localhost/myweb/a.aspx/b.aspx/c.aspx
path: d:\vhosts\myweb\a.aspx
url: /myweb/a.aspx
context.Request.Path: /myweb/a.aspx/b.aspx/c.aspx

 
 

【技术】.NET C# 动态调用动态链接库中的函数。

12

C# 调用传统的 API 动态链接库,是.NET开发经常被讨论的问题。


比如有这么一个动态链接库(delphi 语言):


library DelphiDLL;

uses
  SysUtils,
  Classes,
  Windows;

{$R *.res}

function TestMyFunction( a: Integer ): Integer; stdcall;
begin
  Result := a * a;
end;

exports
  TestMyFunction;

begin
end.


在C#中调用这个动态链接库的一般方法是:


class DllStatic
{
  [DllImport( "DelphiDLL.dll" )]
  public static extern int TestMyFunction( int a );
}


但这种方法类似传统语言对动态链接库的“静态调用”(如 delphi 中的 external )。这种静态调用,有的时候并不是我们想要的。


在传统语言中,我们可以用 LoadLibrary、GetProcAddress 来替代上面的方法。经过思考的试验,在C#中,也可以使用这种方法来实现。


class DllDynamic
{
  [DllImport( "Kernel32.dll" )]
  public static extern int LoadLibrary( string lpFileName );

  [DllImport( "Kernel32.dll" )]
  public static extern bool FreeLibrary( int hModule );

  [DllImport( "Kernel32.dll" )]
  public static extern IntPtr GetProcAddress( int hModule, string lpProcName );
}

class Program
{
  public delegate int TestMyFunction( int a );


  static void Main( string[] args )
  {   
    int hLib = DllDynamic.LoadLibrary( “DelphiDLL.dll” );
    try
    {
      IntPtr ptr = DllDynamic.GetProcAddress( hLib, “TestMyFunction” );
     
TestMyFunction myfunc = (TestMyFunction) Marshal.GetDelegateForFunctionPointer( ptr, typeof( TestMyFunction ) );

      
for (int i = 0; i < 10; i++ )
      {
        Console.WriteLine( myfunc( i ) );
      }
    }
    finally
    {
      DllDynamic.FreeLibrary( hLib );
    }
  }
}


其中,对 GetProcAddress 的结果做何处理,是尝试多次的焦点问题。查找若干资料,终于发现 Marshal 类中的静态方法可以进行内存地址到函数托管的封存转换。

 
4 Comments

Posted in 12.技术

 

.Net 2.0 Beta 2 今天发现的怪问题

03

在 web.config 中如下设定的 HttpModule 在 Web 应用启动时,偶尔会出现多个实例。在这些实例中,其中一个会保持下来,其它几个将在 n 分钟后自动 Dispose 掉。


        <httpModules>
            <add name=”MyHttpModule” type= “My.Framework.MyHttpModule, My.Framework” />
        </httpModules>


这个问题影响到在 HttpModule 中,不能无顾忌的使用静态变量。


比如,在 Dottext 0.96 中的 EventHttpModule 下面这样的写法是有问题的:


namespace Dottext.Framework.ScheduledEvents
{
    /// <summary>
    /// Summary description for EventModule.
    /// </summary>
    public class EventHttpModule: System.Web.IHttpModule
    {
        static Timer eventTimer;

        public EventHttpModule()
        {
        }

       
#region IHttpModule Members

       
public void Init( System.Web.HttpApplication application )
        {
            if (eventTimer == null)
            {
                eventTimer = new Timer( new TimerCallback( ScheduledEventWorkCallback ), 
                    application.Context, 60000, EventManager.TimerMinutesInterval * 60000 );
            }
        }

       
private void ScheduledEventWorkCallback( object sender )
        {
            
try
            {
                EventManager.Execute();
            }
            catch (Exception ex)
            {
                LogManager.CreateExceptionLog( ex, “Failed ScheduledEventCallBack” );
            }
        }

        
public void Dispose()
        {
            eventTimer = null;
       
}


        #endregion
    }
}


看到这个代码,首先的问题是 eventTimer 没有被显性的 Dispose,查阅MSDN及试验证明,Timer 类型(无论是 System.Threading.Timer 还是 System.Timers.Timer)不做显性的 Dispose,其计时器事件还是会被响应的。


于是,在 eventTimer = null; 前面加上了 eventTimer.Dispose();


然后经过一些方法测试,就发现了本篇开头发现的问题。EventHttpModule 出现了两个实例,其中一个实例创建了一个 eventTimer 实例,另外一个实例中,eventTimer 没有再次创建。过了一段时间,其中一个 EventHttpModule 实例(应该是没有被正式引用的 EventHttpModule 实例)被 Dispose,eventTimer 被终止,另一个 EventHttpModule 中,eventTimer 当然也就失去了计时器作用。