Dec 3

转载自兴百放的BLOG, 对视图的描述非常详细到位, 原网址如下:

http://www.cnblogs.com/xbf321/archive/2009/06/16/view_in_sqlserver.html
http://www.cnblogs.com/xbf321/archive/2009/06/19/view_two_in_sqlserver.html

=====================================================

1,什么是视图?

2,为什么要用视图;

3,视图中的ORDER BY;

4,刷新视图;

5,更新视图;

6,视图选项;

7,索引视图;


1.什么是视图

视图是由一个查询所定义的虚拟表,它与物理表不同的是,视图中的数据没有物理表现形式,除非你为其创建一个索引;如果查询一个没有索引的视图,Sql Server实际访问的是基础表。

如果你要创建一个视图,为其指定一个名称和查询即可。Sql Server只保存视图的元数据,用户描述这个对象,以及它所包含的列,安全,依赖等。当你查询视图时,无论是获取数据还是更新数据,Sql server都用视图的定义来访问基础表;

视图在我们日常操作也扮演着许多重要的角色,比如可以利用视图访问经过筛选和处理的数据,而不是直接访问基础表,以及在一定程度上也保护了基础表。

我们在创建视图的时候,也要遵守三个规则:

    1. 不能在视图定义中指定ORDER BY ,除非定义中包含Top或For Xml 说明;
    2. 所有的列必须有列名;
    3. 这些所有的列名必须唯一;

对于视图表中在没有top或for xml说明的情况下,不能有Order by 语句,这是因为视图被认为是一个表,表是一个逻辑的实体,它的行是没有顺序的。视图中所有列必须有列名,且唯一的情况我想大家都理解;

下面的sql语句表示创建一个简单的视图:

   1:  CREATE VIEW dbo.V1
   2:  AS
   3:  SELECT CustomerID,CompanyName FROM Customers 
   4:  WHERE EXISTS(SELECT * FROM Orders WHERE Customers.CustomerID = Orders.CustomerID)
2.为什么要使用视图(更新)
      SqlServer既然给我们提供这样的对象,就一定有它的作用。而我们在使用视图上,要么用的过多,要么用的不够,所以一部分人建议不要用视图,而一部分人又建议少用。那我们听谁的呢?
      其实我们要是掌握了用视图的目的,就能在正确的地方,用正确的视图;那么视图能给我们解决什么问题呢?
            1,为最终用户减少数据库呈现的复杂性。客户端只要对视图写简单的代码,就能返回我所需要的数据,一些复杂的逻辑操作,放在了视图中来完成;
            2,防止敏感的列被选中,同时仍然提供对其他重要数据的访问;
            3,对视图添加一些额外的索引,来提高查询的效率;
      视图其实没有改变任何事情,只是对访问的数据进行了某种形式的筛选。考虑一下视图的作用,你应该能看到视图的概念如何为缺乏经验的用户简化数据(只显示他们关心的数据),或者不给予用户访问基础表的
权利,但授予他们访问不包含敏感数据视图的权力,从而提前隐藏敏感数据。
      要知道,在默认的情况下,视图没有做什么特殊的事情。视图就好象一个查询那样从命令行运行(这里不存在任何形式的预先优化),这意味着在数据请求和将被交付的数据之间多加了一层开销。这表明视图绝不可能像
只是直接运行底层SELECT语句那样快。不过,视图存在有一个原因--这就是它的安全性或为用户所做的简化,在你的需要和开销之间权衡,找到最适合特定情况的解决方案。
3.视图中的ORDER BY
    视图表示一个逻辑实体,它与表非常类似;
    如果我们在上面的创建的sql语句中加一个Order BY 语句,看看有什么效果:
   1:  ALTER VIEW dbo.V1
   2:  AS 
   3:  SELECT CustomerID,CompanyName FROM Customers 
   4:  WHERE EXISTS(SELECT * FROM Orders WHERE Customers.CustomerID = Orders.CustomerID)  
   5:  ORDER BY CompanyName

 

运行该语句将会失败,回收到以下的提示:

Msg 1033, Level 15, State 1, Procedure V1, Line 5
除非另外还指定了 TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效。

根据提示,ORDER By 也不是不能用,只有指定了Top或for xml语句后,ORDER BY 才能使用,如:
 

   1:  ALTER VIEW dbo.V1
   2:  AS 
   3:  SELECT TOP(10) CustomerID,CompanyName FROM Customers 
   4:  WHERE EXISTS(SELECT * FROM Orders WHERE Customers.CustomerID = Orders.CustomerID)  
   5:  ORDER BY CompanyName

但是,并不建议在视图中使用ORDER BY ,这是因为视图表示一个表,而对于表来说,是不会有排序的;所以建议在查询视图的时候,用ORDER BY;

SQL Server2005联机丛书有一段这样的描述:“在视图、内联函数、派生表或子查询的定义中使用ORDER BY 字句,子句只能用户确定TOP子句返回的行。ORDER BY 不保证在查询这些构造时得到有序结果,除非在查询本省也指定了ORDER BY.”

4.刷新视图

我在上面说过,视图会保存元数据,列,安全,以及依赖等信息,如果我们把基础表的架构更改了,并不会直接反映到视图上来;更改架构后,使用sp_refreshview存储过程刷新视图的元数据是一个好习惯;

比如我们创建了一个表T1和一个T1的视图V1,然后更改T1,再看V1的结果:

首先创建表T1:
 

   1:  IF OBJECT_ID('T1') IS NOT NULL
   2:      DROP TABLE T1
   3:   
   4:  CREATE TABLE T1(col1 INT,col2 INT)
   5:  INSERT INTO T1(col1,col2) VALUES(1,2)
   6:  GO

然后创建T1的视图V1:

   3:  CREATE VIEW V1
   4:  AS 
   5:  SELECT * FROM T1

 

在现实实践中,要避免在视图中的SELECT语句中使用*,在这只是演示。如果你查询视图V1就会出现以下结果:
tmp16

接下来,我们对表T1添加一列col3:
 

   1:  ALTER TABLE T1 ADD col3 INT

然后再次查询视图V1,你想这时的结果是三列呢,还是而列呢?答案是二列。T1架构的改变,并没有影响到视图的元数据中,这时候,如果我们要刷新一下视图V1,我们就可以用:EXEC sp_refreshview V1 命令, 再次查询,V1的结果就是三列了。

5.更新视图

视图是一个虚拟表,我们在查询视图的时候,实际上是对基础表的查询。视图不仅可以作为SELECT查询的目标,也可以作为修改语句的目标。当然,当你修改视图的时候,修改的时候是对基础表的修改,它就好像是一个代理。当然,如果不允许直接修改基础表,只允许修改视图,就可以限制你要公开的数据。这样,就可以对你的数据起到一定的保护作用,不过这种限制的时候很少。

那么在更新视图的时候,有哪些限制条件呢?

1,只要视图有一列不能隐式获取值,你就不能想视图中插入数据,如果列允许NULL、有默认值或者IDETITY属性,则说明它可以隐式获取值;

2,如果视图包含联结,UPDATE或INSERT语句只能影响联结的一端。也就是说,INSERT或UPDATE语句必须定义目标列列表,这些列只能数据联结的一端。你不能从由联结查询定义的视图中删除数据;

3,不能修改作为计算结果的列。如:标量表达式和聚合函数,SqlServer不会尝试改变数据库引擎的计算结果;

4,如果在创建或修改视图时指定了WITH CHECK OPTION选项,与视图的查询筛选器有冲突的INSERT或UPDATE语句将被拒绝;我在“视图选项”一节详细讲解一下。

如果视图上定义了INSERT OF触发器,则违反这些限制的数据修改语句可以被执行。在INSERT OF触发器中你可以用自己的代码替换原始修改;

当你允许对有联结查询定义的视图执行修改的时候,一定要谨慎,比如一对多的关系,如果你根据“多”的某一索引值修改对应“一”端某列值的记录,那么结果就可想而知;

6.视图选项

当你创建或修改视图时,可以指定一些选项,这些选项用户控制视图的行为和功能。

ENCRYPTION、SCHEMABINDING和VIEW_METADATA选项在视图头指定,CHECK OPTION选项则在查询之后指定;

如:

   1:  CREATE VIEW v2
   2:  WITH ENCRYPTION,SCHEMABINDING,VIEW_METADATA
   3:  AS
   4:  SELECT OrderID FROM dbo.Orders
   5:  WITH CHECK OPTION

1),ENCRYPTION

如果你在构建任何类型的商业软件的时候,需要对视图进行加密的时候,这是一个不错的选项。

如果未指定ENCRYPTION选项,SQLSERVEr则以纯文本的形式保存用户定义的语句,如果指定了ENCRYPTION选项,对象的文本则会被混淆。

SQLSERVER提供了一个系统函数sp_helptext查看视图的文本,如果应用的ENCRYPTION选项,则会得到“The text for object ‘xx’ is encrypted”语句;

注:在加密之前一定要先备份你所要加密的视图,一旦加密,就不能回头。

2),SCHEMABINDING

如果你使用SCHEMABINDING选项创建视图,SQLSERVER将不允许删除基础表或修改被引用的列,防止在对底层对象修改时,使视图变得“孤立”,如果某人没有注意到你的视图,执行了DROP,删除视图引用的列或其他一些操作,那就很糟糕。如果使用SCHEMABINDING选项,则就可以避免这种情况。

如果想在视图上创建索引,则必须使用SCHMABINDING选项;

如果应用这个选项,则定义视图的时候要注意两点:

1,所有对象必须由两部分构成的名称,如:应该使用dbo.Orders 而不能是Orders

2,不能在SELECT列表使用*,所有的列名必须指定一个名称;

3),CHECK OPTION

使用WITH CHECK OPTION 创建的视图能防止与视图查询筛选器有冲突的INSERT或UPDATE语句。没有该选项,视图可以接受不符合查询筛选器的修改。比如:

我们在Northwind数据库中创建一个CustomWithOrder的视图,现在还没有添加WITH CHECK OPTION选项
 

   1:  CREATE VIEW CustomerWithOrder
   2:  WITH VIEW_METADATA
   3:  AS
   4:   
   5:  SELECT Customers.CustomerID,Customers.CompanyName FROM Customers 
   6:  WHERE EXISTS(SELECT 1 FROM Orders WHERE Orders.CustomerID = Customers.CustomerID)
   7:   
 
 该视图的作用是查询所有有订单的客户的id和公司名,接下来我们向视图中插入一条不存在的用户id,和公司名:
   1:  INSERT INTO CustomerWithOrder(CustomerID,CompanyName) VALUES('MYSQL','MyReed')
执行成功,然后在查询这个CustomerWithOrder视图,很明显,查询不到CustomerID为’MySQL’的用户,因为视图只包含发生过订单的用户;如果你直接查询Customers表,就会发现这个新增的用户信息了。
接下来对CustomerWithOrder视图添加WITH CHECK OPTION 选项
 
   1:  ALTER VIEW CustomerWithOrder
   2:  WITH VIEW_METADATA
   3:  AS
   4:   
   5:  SELECT Customers.CustomerID,Customers.CompanyName FROM Customers 
   6:  WHERE EXISTS(SELECT 1 FROM Orders WHERE Orders.CustomerID = Customers.CustomerID)
   7:   
   8:  WITH CHECK OPTION

然后再执行下面的语句:

 
   1:  INSERT INTO CustomerWithOrder(CustomerID,CompanyName) VALUES('ILSQL','MyReed')

你会收到以下错误:
 

Msg 550, Level 16, State 1, Line 2
试图进行的插入或更新已失败,原因是目标视图或者目标视图所跨越的某一视图指定了 WITH CHECK OPTION,而该操作的一个或多个结果行又不符合 CHECK OPTION 约束。
语句已终止。

4),VIEW_METADATA

该选项的作用是,让视图看起来更像一个真正的表。不使用该选项,返回给客户端的api的元数据将是视图所依赖的基础表的数据;

如果客户端希望SqlServer发送视图的元数据信息,而不是基础表的元数据时,可以在创建或修改视图时指定此选项;是不是听的很费劲,听我慢慢说;

假设用户拥有对视图的操作权限,而没有对基础表操作的权限,那么用户对视图执行一些操作,如果指定了VIEW_METADATA选项,那么该语句将会违背安全而失败,因为只要指定了VIEW_METADATA那么返回给客户端就是视图的元数据,而不是基础表的元数据。另一方面,如果用户尝试通过视图修改数据,而该操作又与视图上定义的CHECK OPTION有冲突,这种操作只有直接提交到基础表,才有可能成功。

SqlServer中就有这样的工具,在SqlServer2000中,企业管理器就是,如果我们向视图中插入一条记录,比如向在有WITH CHECK OPTION选项的CustomerWithOrder视图中插入一个任意的消费者无论存在与否,并打开跟踪企业管理器提交到Sql Server中的操作,你会发现操作实际把基础表作为目标提交的,及时他违背CHECK OPTION,也会成功。而在Sql Server2005中的SSMS中,就会不同了,如果在“Modify”视图中,手动插入一条记录,就可以成功,说明虽然指定了VIEW_METADATA和CHECK OPTION选项,它还是插入到了基础表中了,可以跟踪一下提交到Sqlserver的操作(用Sql server Profiler)。但如果在由“Open View”产生的面板中进行操作,将会失败,提示:
tmpF2DE

可以再次跟踪提交到Sql server的操作,就能看到,他提交到目标对象是视图;

还是那句话:如果客户端希望SqlServer发送视图的元数据信息,而不是基础表的元数据时,可以在创建或修改视图时指定此选项

这次明白了吗?

 

我个人总结,只要有VIEW_METADATA选项就有必要加上CHECK OPTION选项,而SCHEMABINDING选项,最好也要加上,防止你的视图“孤立”,而在索引视图中SCHEMABINDING选项是必须加上的。

7.索引视图

如果没有索引,视图中的数据不会有任何物理表现形似,如果加上索引,则就把视图中的数据物理化了,SqlServer会在修改基础表时同步索引视图。但你不能直接同步视图内容。

我们知道在表上创建索引,能提高性能,相同,在视图也是一样,在视图上创建的第一个索引必须是唯一聚集索引,之后才可以创建其他的非聚集索引。

索引视图必须使用SCHEMABINDING选项,并且不能引用其他视图,只能引用基础表和UDF,而基础表和UDF必须使用两部分命名约定来引用(参见5.视图选项中的SCHEMABINDING选项)。

除了性能,你可能还会因为其他原因使用索引视图,比如在一张基础表中有一列我们要强制该列中已知值的唯一性,但是允许出现多次的NULL值,我们怎么办呢,我们首先想到的可能是用UNIQUE约束,但是UNIQUE会认为两个NULL值相等,那么这个不得不放弃了,那还有什么办法呢?

其实我们可以利用一个索引视图来完成这个任务,利用索引视图筛选所有非NULL的数据,那么这种索引将防止重复的已知值进入基础表,但允许多个NULL,因为NULL不是唯一索引的一部分,我们在向基础表中插入数据的时候,就利用索引视图的UNIQUE来限制我们的数据,来达到某列中强制已知值的唯一性的目的;

我们可以演示一下,首先创建一个基础表T2和一个索引视图V2:
 

   1:  CREATE TABLE T2(col1 INT,col2 NVARCHAR(50))
   2:   
   3:  CREATE VIEW V2
   4:  WITH SCHEMABINDING
   5:  AS
   6:  SELECT col1 FROM dbo.T2 WHERE col1 IS NOT NULL;
   7:  CREATE UNIQUE CLUSTERED INDEX idx_col1 ON dbo.V2(col1);
 
然后我们向T2表中插入以下数据:

 
   1:  INSERT INTO t2(col1,col2) VALUES(1,'2')
   2:  INSERT INTO t2(col1,col2) VALUES(1,'3')
   3:  INSERT INTO t2(col1,col2) VALUES(null,'4')
   4:  INSERT INTO t2(col1,col2) VALUES(null,'5')

那么以上4条INSERT哪条会失败呢?答案是2。最后让我们SELECT 一下基础表T2,看实现我们开始那个要求了吗?

 
   1:  SELECT * FROM t2

 

 

执行:
tmpD43

哈哈实现了吧!

Dec 2

这也得记上一笔, 学习70-431时, 在我本机上运行 CREATE PARTITION FUNCTION 命令总是出错, 报告 "Incorrect syntax near 'PARTITION'".

找了半天才发现原来这语句只能在SQL SERVER ENTERPRISE 版本上使用. 这里放个使用的例子:

HiddenCopy Code To Clipboard
  1. CREATE PARTITION FUNCTION emps_partition (int)
  2. AS RANGE LEFT
  3. FOR VALUES (1, 100, 1000)
  4.  
  5. CREATE PARTITION SCHEME emps_partition_schema
  6. AS PARTITION emps_partition ALL TO ([PRIMARY])
  7.  
  8. CREATE TABLE empid_partition (Empid int, name char(25))
  9. ON emps_partition_schema (empid)

 

再记一下如何查看SQL SERVER的版本, 只要在SQL查询中运行一句就可以:

SELECT @@version

就会有非常详细的信息了, 比如这个:

Microsoft SQL Server 2000 - 8.00.2039 (Intel X86) May 3 2005 23:18:38 Copyright (c) 1988-2003 Microsoft Corporation Standard Edition on Windows NT 5.0 (Build 2195: Service Pack 4)

或者:

Microsoft SQL Server 2005 - 9.00.1399.06 (Intel X86) Oct 14 2005 00:33:37 Copyright (c) 1988-2005 Microsoft Corporation Developer Edition on Windows NT 5.1 (Build 2600: Service Pack 3)

服务器上的:

Microsoft SQL Server 2005 - 9.00.3282.00 (Intel X86) Aug 5 2008 01:01:05 Copyright (c) 1988-2005 Microsoft Corporation Standard Edition on Windows NT 5.2 (Build 3790: Service Pack 2)

 

Tags: ,
Nov 11

C++使用 void extern __declspec(dllexport) 函数名()定义的输出函数, 在C#中调用时, 如前文所述, 使用
[DllImport("D:\VS2005Projects\Dev_PetroSim2010b\Dev_AMDBR\Debug\Dev_DR_AMDBR.dll", EntryPoint = "#1")]
public static extern void amDBRSurfaceTensionEX(int compCount, int[] components, int modelID, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] double[] Z, ref int errorID);
来调用.

这里注意的是,定义函数入口点的参数EntryPoint = "#1", C#中有2种方式可以定义入口点, 一种是以序号加前缀"#",这是我一直用的,另一种是用EntryPoint="函数名",这种方法我试了很多次却无法实现,C#编译器总会说在DLL中没有找到函数.

如何找到函数所对应的EntryPoint编号呢?我用的是一个叫DLLExportViewer的软件,可以告诉你各个函数的Ordinal,即编号,照此调用即可,但是问题是,如果原DLL进行编辑之后,增加或减少输出函数之后,各个函数的编号会重新打乱,使原程序无法进行.所以对这种情况,即DLL尚在编辑之中时,还是以函数名作为EntryPoint比较好.

在C:Program FilesMicrosoft Visual Studio 8VCbin目录中,有一个文件dumpbin.exe文件和DLLExportViewer一样可以查看DLL的输出函数,但是在运行时,居然跳出一个错误窗口
查找后发现,mspdb80.dll文件是在C:Program FilesCommon FilesMicrosoft SharedVSA8.0VsaEnv中,将此文件复制到C:Program FilesMicrosoft Visual Studio 8VCbin目录,再运行时错误消失.

查阅dumpbin的输出,发现输出函数说明及如下

ordinal hint RVA name
7 6 0002D591 ?amDBRSurfaceTensionEX@@YAXHPAHHPANAAH@Z = @ILT+1420(?amDBRSurfaceTensionEX@@YAXHPAHHPANAAH@Z)
 

前面的ordinal 7即为函数的输出序号, name列即为可用的函数名,在C#中,改函数声明为如下即可用函数名作为EntryPoint

[DllImport("D:VS2005ProjectsDev_PetroSim2010bDev_AMDBRDebugDev_DR_AMDBR.dll", EntryPoint = "?amDBRSurfaceTensionEX@@YAXHPAHHPANAAH@Z")]
 

============================================

照上述做法,导出函数可以成功,但是在C#中调用却是相当麻烦,因为函数的入口名称太过繁杂且不可理解,因此,还是要解决在C++中的函数输出,以使在C#中能直接以函数名作为入口.

上述问题中,实际上是在C++中函数输出时,默认输出名是使用C++的mangled name, 要使用C的命名方式方可直接使用其输出名,这时,函数输出声明应改为如下:
extern "C" __declspec(dllexport) void amDBREnthalpyEX(int CompCount, int* streamComp, int modelID, double* streamInfo, int& errorID)

这样,可以看到DLLExportViewer的查看结果了,以此方式声明的amDBREnthalpyEX函数和以原方式声明的其他函数的名称的不同如下图所示.

这时,在C#中,就可以以函数名称作为入口点了:
[DllImport("D:VS2005ProjectsDev_PetroSim2010bDev_AMDBRDebugDev_DR_AMDBR.dll", EntryPoint = "amDBREnthalpyEX")]
public static extern void amDBREnthalpyEX(int compCount, int[] components, int modelID, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] double[] Z, ref int errorID);
 

Tags: , ,
Oct 26

1. 因为不可直接在网页上添加文字(UTF8编码问题), 把所有要显示的文字都放到law_plus表中.

  • 在law_plus表中,添加记录,PLUSLABLE为文字标识,以"STR"打头,BODY为文字内容,支持HTML
  • 在MODULE目录中,增加STRINFO.PHP文件,用于显示数据库记录,内容为:
    PHP Code Copy Code To Clipboard
    1. function getInfo($strName)
    2. {
    3. global $fsql;
    4. global $charset;
    5. global $tbl_plus;
    6. global $MenuInfo;
    7. $scl = " pluslable='str".$strName."' ";
    8. $fsql->query( "select * from {$tbl_plus} where {$scl} " );
    9. if ( $fsql->next_record( ) )
    10. {
    11. $body = $fsql->f( "body" );
    12. }
    13. return $body;
    14. }
  • 修改includes/func/common.inc.php文件,showtemp ($EditCon) 函数,增加
    PHP Code Copy Code To Clipboard
    1. else if ( substr( $arrayx[0], 0, 3 ) == "str" )
    2. {
    3. include_once( ROOTPATH."module/strInfo.php");
    4. $CodeString = getInfo(substr( $arrayx[0],3 ) );
    5. }
  • 在任意的template文件中,增加{#strXXXXXX#}变量即可

2. 添加文章类别后在首页上显示

  • 先在后台添加文章类别
  • 进入phpmyadmin, 打开数据库, 查看law_plus表,
  • 添加记录, 其中,
    • ID依次编入,不能重复;
    • MENUID=1
    • pluslable = modNewsList
    • plustype 和 pluslocat 都是 INDEX
    • catid 可以查看其他的, 依次下来加1
    • showmenuid = 84
    • showmore = 更多
    • modno 这个很重要, 不能有重复,一般取比最后一个modNewsList的modno值大1即可
  • 以上添加完成后,在template的HTML文件中添加引用:

{#modNewsList-18#}

其中的数字18即以上的modno数字.

 

Tags: , , ,
Oct 19

帮老婆的弟媳做一个网站,下载了一份现成的PHP代码,不过都是加了密的.有些改动需要对PHP文件进行,可是发现这份东西是用了ZEND加密的,网上现成的DEZENDER的软件不少,绝大多数是基于黑刀改装的,效果不错,可是都不能对目录结构进行原样复制.找到一份梅花三弄版 DEZENDER据说可以保持原目录结构,可是不知是什么原因,居然在我的计算机上运行不起来.于是我就想自己写一个程序来做这事,顺便学习一下C#.

Form上的几个基本控件:

  • txtFolder: PHP文件所在的根目录, 新目录将自动取名为目录名_new
  • button1: txtFolder的browse按钮
  • CheckBox1: With Subfolders 标记, 即操作是否包括各级子目录
  • CheckBox2: Process PHP files only 标记, 即操作是否只针对PHP文件,如果要针对所有文件,则非PHP文件将被复制到指定目录
  • txtLog: 日志框, 输出所有信息
  • stsInfo: StatusBar, 显示一些提示信息
  • btnList: 显示所有文件到txtLog
  • btnbProcess: 对文件进行操作

几个private变量:

 

  • private bool WithSub = true; //对应checkbox1
  • private bool PhpOnly = false; //对应checkbox2
  • private string RootDir; //对应txtFolder

检查运行环境, PHP.EXE是黑刀的执行文件, 必须在当前目录下存在.

C# 代码复制内容到剪贴板
  1. private void Form1_Load(object sender, EventArgs e)    
  2. {    
  3. if (!File.Exists(".php.exe"))    
  4. {    
  5. MessageBox.Show("No php.exe found, the program may not run properly!");    
  6. Application.Exit();    
  7. }    
  8. dlgOpenFld.SelectedPath = "D:temp";    
  9. }    

 

另外, 在PHP.INI文件中, 要确认以下文件的路径名正确, 个人感觉用相对路径会好一些

PHP 代码复制内容到剪贴板
  1. [Zend]    
  2. zend_extension_manager.optimizer_ts="libOptimizer-3.3.0"    
  3. zend_extension_ts="libZendExtensionManager.dll"   

下面, BROWSE文件夹操作.

C# 代码复制内容到剪贴板
  1. private void button1_Click(object sender, EventArgs e)    
  2. {    
  3. if ( dlgOpenFld.ShowDialog() == DialogResult.OK )    
  4. {    
  5. txtFolder.Text = dlgOpenFld.SelectedPath;    
  6. }    
  7. }    

最重要的操作之一, 用递归取得文件列表, 在这一步里,用到了List<>这样的数据类型, 这是用于未知长度数组的, 可以看到, C#对于文件,目录的操作都与DELPHI有了非常大的不同, 这就是FRAMEWORK:

C# 代码复制内容到剪贴板
  1. private List<FileInfo> getFiles(string tDir, bool SubFolder)    
  2. {    
  3. List<FileInfo> FileList = new List<FileInfo>();    
  4. DirectoryInfo fDir = new DirectoryInfo(tDir);    
  5. DirectoryInfo[] subDir = fDir.GetDirectories();    
  6. FileInfo[] sFile = fDir.GetFiles();    
  7. if (SubFolder)    
  8. {    
  9. foreach (DirectoryInfo d in subDir)    
  10. {    
  11. FileList.AddRange(getFiles(tDir + "" + d.Name, SubFolder));    
  12. }    
  13. }    
  14. foreach (FileInfo f in sFile)    
  15. {    
  16. FileList.Add(f);    
  17. }    
  18. return FileList;    
  19. }    

下一步, 就是对取得的文件列表进行操作了,新文件夹自动取名为XXX_NEW, 并且对文件列表按规则进行操作, 即是只解密PHP文件, 还是要同时复制其他非PHP文件:

C# 代码复制内容到剪贴板
  1. private void button1_Click_1(object sender, EventArgs e)    
  2. {    
  3. if (!Directory.Exists(txtFolder.Text)||(txtFolder.Text.IndexOf(" ")>0))    
  4. {    
  5. MessageBox.Show("Directory "" + txtFolder.Text + "" is invalid! (No space allowed)""Invalid folder selection", MessageBoxButtons.OK,MessageBoxIcon.Error);    
  6. return;    
  7. }    
  8. RootDir = txtFolder.Text;    
  9. string newDir=RootDir + "_new";    
  10. List<FileInfo> FileList = new List<FileInfo>();    
  11. FileList = getFiles(txtFolder.Text, WithSub);    
  12.   
  13. for (int i = 0; i < FileList.Count; i++)    
  14. {    
  15. string tNewDir = FileList[i].DirectoryName.Replace(RootDir, newDir);    
  16. if (!Directory.Exists(tNewDir))    
  17. {    
  18. Directory.CreateDirectory(tNewDir);    
  19. }    
  20. if (FileList[i].Extension != ".php")//&&(FileList[i].    
  21. {    
  22. File.Copy(FileList[i].DirectoryName + "" + FileList[i].Name, tNewDir + "" + FileList[i].Name);    
  23. txtLog.AppendText("COPIED! " + i.ToString() + " - " + FileList[i].DirectoryName + "" + FileList[i].Name + "nn");    
  24. }    
  25. else    
  26. {    
  27. if (runphp(FileList[i].DirectoryName + "" + FileList[i].Name, tNewDir))    
  28. {    
  29. txtLog.AppendText("DECRYPTED! " + i.ToString() + " - " + FileList[i].DirectoryName + "" + FileList[i].Name + "nn");    
  30. }    
  31. else    
  32. {    
  33. txtLog.AppendText("FAILED TO DECRYPT! " + i.ToString() + " - " + FileList[i].DirectoryName + "" + FileList[i].Name + "nn");    
  34. }    
  35. }    
  36. }    
  37. }   

这是解码PHP文件, 通过建立新进程来调用外部EXE文件:

C# 代码复制内容到剪贴板
  1. private bool runphp(string filename, string NewFolder)    
  2. {    
  3. try    
  4. {    
  5. string EXE_PATH = @".";    
  6. System.Diagnostics.Process process = new System.Diagnostics.Process();    
  7. process.StartInfo.FileName = "php.exe";    
  8. process.StartInfo.WorkingDirectory = EXE_PATH;    
  9. process.StartInfo.CreateNoWindow = true;    
  10. process.StartInfo.Arguments = filename + " /tab /indent:1 /path:" + NewFolder + " /ext:* /noexpire";    
  11. process.Start();    
  12. if (process.HasExited)    
  13. {    
  14. //txtLog.AppendText("finished!nn");    
  15. //return true;    
  16. }    
  17. return true;    
  18. }    
  19. catch (Exception e)    
  20. {    
  21. throw e;    
  22. //return false;    
  23. }    
  24.   
  25. }    

其他的代码就不一一罗列了. 只是临时使用的代码, 功能还非常简单.

 

Tags: , ,
分页: 1/2 第一页 1 2 下页 最后页 [ 显示模式: 摘要 | 列表 ]