技术宅改变世界 G∑∑K

回顾

        上一篇,我们介绍了基本控件及控件的重要属性和用法,我们本篇详细介绍WPF中的几种布局容器及每种布局容器的使用场景,当

然这些都是本人在实际项目中的使用经验,可能还存在错误之处,还请大家指出。

本文大纲

1、Grid

2、StackPanel

3、DockPanel

4、WrapPanel

Grid

1、Row和Column

我们下面来介绍Grid的行的用法,及我们在UI设计过程中需要注意的细节。

由于前面我们在第一章中已经介绍了基本的关于Grid的表格行和列的定义及相关属性,为了防止大家遗忘,我们这里再次介绍下:

image

为了加深大家对Grid布局的印象,我们这里加入控件来展示效果。

下面在每个单元格都加入子控件

image

上面指定了控件在Grid表格中的哪一行那一列,如果我们的某个控件跨行或者跨列如何做呢?

image

关于跨行和跨列一样,只不过将Grid.ColumnSpan换成Grid.RowSpan。

下面介绍,在Grid如何将控件设置为自适应宽度和高度,或者是固定宽度或固定高度时,应该注意的细节。

image

1、自适应区域:

image

2、顶部对齐或底部对齐

image

对于顶部对齐和底部对齐,相对来说都一样。

3、左右对齐时:

image

4、下面来举个例子,我们来如何分析,根据原型来使用Grid布局来达到要求和目标:

例如下图:

image

我们以博客园为例,可能例子不太合适,但是如果我们想做一个博客园的桌面版,保持风格一致的情况下,如果我们使用Grid布局如何来布局呢?

A、有Logo图片,上面还有设置等菜单,所以,我们可以吧这块设置为二行,这样比较容易区分页面的布局和设置

B、下面有几个二级菜单,新闻、博问等 一行

C、左侧有网站分类。必须1列

D、右侧有内容区。上面有区分首页、精华、候选、新闻、关注等、1列

E、右侧有找找看、还有最新新闻等 1列。

F、最下面,肯定还有状态栏,如果我们开发桌面系统。1行

 

根据上面的分析,我们的Grid表格至少5行、3列

关于其他的设计,我们通过Grid表格的组合来进行控制。

下面我们就来实现下:

先设置大体布局如下:

image

关于上述布局的具体实现如下:

<Window x:Class=”Samples.MainWindow”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”MainWindow” Height=”600″ Width=”800″>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height=”20″/>
<RowDefinition Height=”50″/>
<RowDefinition Height=”30″/>
<RowDefinition Height=”*”/>
<RowDefinition Height=”30″/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=”150″/>
<ColumnDefinition Width=”*”/>
<ColumnDefinition Width=”200″/>
</Grid.ColumnDefinitions>
<Grid Grid.Column=”1″ Grid.ColumnSpan=”2″>
<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Right”>
<Button Content=”何戈洲” Margin=”5,0,0,0″/>
<Button Content=”我的博客” Margin=”5,0,0,0″/>
<Button Content=”短消息” Margin=”5,0,0,0″/>
<Button Content=”设置” Margin=”5,0,0,0″/>
<Button Content=”退出” Margin=”5,0,0,0″/>
</StackPanel>
</Grid>
<Grid Grid.Column=”0″ Grid.Row=”1″>
<Image Source=”/Samples;Component/Images/logo_small.gif” />
</Grid>
<Grid Grid.Column=”0″ Grid.ColumnSpan=”3″ Grid.Row=”2″>
<StackPanel Orientation=”Horizontal”>
<Button Margin=”5,0,0,0″>园子</Button>
<Button Margin=”5,0,0,0″>新闻</Button>
<Button Margin=”5,0,0,0″>博问</Button>
<Button Margin=”5,0,0,0″>闪存</Button>
<Button Margin=”5,0,0,0″>网摘</Button>
<Button Margin=”5,0,0,0″>招聘</Button>
<Button Margin=”5,0,0,0″>专题</Button>
<Button Margin=”5,0,0,0″>知识</Button>
</StackPanel>
</Grid>
<Grid Grid.Column=”0″ Grid.ColumnSpan=”3″ Grid.Row=”3″>
<Image Source=”/Samples;Component/Images/main.png” />
</Grid>
<Grid Grid.Column=”0″ Grid.ColumnSpan=”3″ Grid.Row=”4″>
<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Center”>
<Button Margin=”5,0,0,0″>关于我们</Button>
<Button Margin=”5,0,0,0″>联系我们</Button>
<Button Margin=”5,0,0,0″>广告服务</Button>
<Button Margin=”5,0,0,0″>人才服务</Button>
<Button Margin=”5,0,0,0″>版权</Button>
</StackPanel>
</Grid>
</Grid>
</Window>

 

从上面的代码可以看出来,非常的简单,Grid特别适合软件系统的整体布局,在实际的项目中通过Grid与其他的布局控件相结合一起完成页面的整体布局。

StackPanel

StackPanel 适合水平或者垂直方向的布局,在上面的例子中我们大量的使用该种布局方式。适合局部区域的布局。比如博客园中的如下区域就可以采用StackPanel进行布局。

image

image

image

对于这类的固定的区域,我们可以不适用Grid来进行布局,使用StackPanel也可以达到目标。

我们来使用StackPanel来进行布局

<StackPanel Orientation=”Vertical” VerticalAlignment=”Stretch”>
<GroupBox Header=”网站分类” Height=”Auto”>
<StackPanel Orientation=”Vertical”>
<Button Content=”.NET技术(16)”/>
<Button Content=”编程语言(13)”/>
<Button Content=”软件设计(3)”/>
<Button Content=”Web前端(16)”/>
<Button Content=”软件工程(26)”/>
</StackPanel>
</GroupBox>
<GroupBox Header=”链接” Height=”Auto”>
<StackPanel Orientation=”Vertical”>
<Button Content=”反馈和建议”/>
<Button Content=”官方博客”/>
<Button Content=”电子期刊” />
<Button Content=”人才服务”/>
<Button Content=”博客模板”/>
</StackPanel>
</GroupBox>
</StackPanel>

运行效果如下:

image

与预期的效果相同,对于其他的模块,我们也可以在局部,对于水平或者垂直方向要求进行布局的,我们都可以采用StackPanel来进行布局。

下面我们来看看横向布局的例子:

image

我们通过表格中的使用对StackPanel的停靠定位,进而通过Stackpanel对内部的子控件的停靠方向设置,我们通过如下代码实现上述效果:

<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Right”>
<Button Content=”何戈洲” Margin=”5,0,0,0″/>
<Button Content=”我的博客” Margin=”5,0,0,0″/>
<Button Content=”短消息” Margin=”5,0,0,0″/>
<Button Content=”设置” Margin=”5,0,0,0″/>
<Button Content=”退出” Margin=”5,0,0,0″/>
</StackPanel>

StackPanel在父容器中是右对齐的。

然后再StackPanel容器中,如果也采用内容右对齐,会有什么效果呢?

<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Right”>
<Button Content=”何戈洲” Margin=”5,0,0,0″ HorizontalAlignment=”Right”/>
<Button Content=”我的博客” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”短消息” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”设置” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”退出” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
</StackPanel>

设置子控件的停靠方式时,不会起到任何作用,默认情况下,Stack的水平布局时,从左至右。

<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Right” FlowDirection=”RightToLeft”>
<Button Content=”何戈洲” Margin=”5,0,0,0″ HorizontalAlignment=”Right”/>
<Button Content=”我的博客” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”短消息” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”设置” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”退出” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
</StackPanel>

修改了FlowDirection设置了StackPanel的方向后,所有的子控件,都是从右向左方向进行绘制和显示,效果如下:

image

所以对于StackPanel我们基本上是用上述的属性和对StackPanel的停靠方式进行设置后,即可满足布局的要求。

DockPanel

DockPanel停靠容器,专门负责自适应窗口的布局,之前我们介绍了DockPanel的布局设置,这里再回顾下:

<DockPanel>
<StackPanel DockPanel.Dock=”Top” Height=”0″>
</StackPanel>
<StackPanel DockPanel.Dock=”Left” Height=”0″>
</StackPanel>
<StackPanel DockPanel.Dock=”Bottom” Height=”0″>
</StackPanel>
<StackPanel DockPanel.Dock=”Right” Orientation=”Vertical” Width=”200″>
<GroupBox Header=”最新新闻” Height=”160″>
<StackPanel Orientation=”Vertical”>
<Button Content=”宅急送近日宣布降价抢”/>
<Button Content=”腾讯联手华为操盘四核手机”/>
<Button Content=”Windows 8各版本区别与售价”/>
<Button Content=”数方程将无线网络带宽提高一个数量级”/>
<Button Content=”中移动:Lumia 920T将于11月上市”/>
<Button Content=”Windows 8下一站:10月25日纽约”/>
</StackPanel>
</GroupBox>
<GroupBox Header=”48小时阅读排行榜” Height=”160″>
<StackPanel Orientation=”Vertical”>
<Button Content=”子用户-角色-权限-菜单 浅谈:子账户设计方案”/>
<Button Content=”网站已恢复正常,让大家久等了”/>
<Button Content=”拿什么拯救你,我的51Job简历?——UBB漏洞”/>
<Button Content=”这些年我们没用过的JS”/>
<Button Content=”多少钱才可让人重拾理想”/>
<Button Content=”准备购买的Dell服务器的硬件配置”/>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel >

<Button Content=” 我铺满”/>

</StackPanel>
</DockPanel>

上面的DockPanel在进行自适应布局时,默认最后的一个区域时默认填充,可以理解为fill。而必须制定其他的区域后,该设置才有效,所以,我们上面设置了top,left,bottom 占用的空间都是0,这样,系统会将最后的一个子区域填充。

上面设置后的效果如下。

image

当然,这个页面的整体,我们也可以采用DockPanel进行布局,布局的效果,完全可以达到上述效果,下面我们来使用DockPanel来对整体进行布局吧。

最终的代码如下:

<DockPanel>
<StackPanel DockPanel.Dock=”Top” Height=”100″>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=”20″/>
<RowDefinition Height=”50″/>
<RowDefinition Height=”30″/>
</Grid.RowDefinitions>
<Grid >
<StackPanel Orientation=”Horizontal” HorizontalAlignment=”Right” FlowDirection=”RightToLeft”>
<Button Content=”何戈洲” Margin=”5,0,0,0″ HorizontalAlignment=”Right”/>
<Button Content=”我的博客” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”短消息” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”设置” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
<Button Content=”退出” Margin=”5,0,0,0″  HorizontalAlignment=”Right”/>
</StackPanel>
</Grid>
<Grid  Grid.Row=”1″>
<Image Source=”/Samples;Component/Images/logo_small.gif” HorizontalAlignment=”Left”/>
</Grid>
<Grid  Grid.Row=”2″>
<StackPanel Orientation=”Horizontal”>
<Button Margin=”5,0,0,0″>园子</Button>
<Button Margin=”5,0,0,0″>新闻</Button>
<Button Margin=”5,0,0,0″>博问</Button>
<Button Margin=”5,0,0,0″>闪存</Button>
<Button Margin=”5,0,0,0″>网摘</Button>
<Button Margin=”5,0,0,0″>招聘</Button>
<Button Margin=”5,0,0,0″>专题</Button>
<Button Margin=”5,0,0,0″>知识</Button>
</StackPanel>
</Grid>
</Grid>
</StackPanel>
<StackPanel DockPanel.Dock=”Bottom” Height=”30″ Orientation=”Horizontal” HorizontalAlignment=”Center”>
<Button Margin=”5,0,0,0″>关于我们</Button>
<Button Margin=”5,0,0,0″>联系我们</Button>
<Button Margin=”5,0,0,0″>广告服务</Button>
<Button Margin=”5,0,0,0″>人才服务</Button>
<Button Margin=”5,0,0,0″>版权</Button>
</StackPanel>
<StackPanel DockPanel.Dock=”Left” Width=”150″>
<StackPanel Orientation=”Vertical” VerticalAlignment=”Stretch”>
<GroupBox Header=”网站分类” Height=”Auto”>
<StackPanel Orientation=”Vertical”>
<Button Content=”.NET技术(16)”/>
<Button Content=”编程语言(13)”/>
<Button Content=”软件设计(3)”/>
<Button Content=”Web前端(16)”/>
<Button Content=”软件工程(26)”/>
</StackPanel>
</GroupBox>
<GroupBox Header=”链接” Height=”Auto”>
<StackPanel Orientation=”Vertical”>
<Button Content=”反馈和建议”/>
<Button Content=”官方博客”/>
<Button Content=”电子期刊” />
<Button Content=”人才服务”/>
<Button Content=”博客模板”/>
</StackPanel>
</GroupBox>
</StackPanel>
</StackPanel>
<StackPanel DockPanel.Dock=”Right” Orientation=”Vertical” Width=”200″>
<GroupBox Header=”最新新闻” Height=”160″>
<StackPanel Orientation=”Vertical”>
<Button Content=”宅急送近日宣布降价抢”/>
<Button Content=”腾讯联手华为操盘四核手机”/>
<Button Content=”Windows 8各版本区别与售价”/>
<Button Content=”数方程将无线网络带宽提高一个数量级”/>
<Button Content=”中移动:Lumia 920T将于11月上市”/>
<Button Content=”Windows 8下一站:10月25日纽约”/>
</StackPanel>
</GroupBox>
<GroupBox Header=”48小时阅读排行榜” Height=”160″>
<StackPanel Orientation=”Vertical”>
<Button Content=”子用户-角色-权限-菜单 浅谈:子账户设计方案”/>
<Button Content=”网站已恢复正常,让大家久等了”/>
<Button Content=”拿什么拯救你,我的51Job简历?——UBB漏洞”/>
<Button Content=”这些年我们没用过的JS”/>
<Button Content=”多少钱才可让人重拾理想”/>
<Button Content=”准备购买的Dell服务器的硬件配置”/>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel >
<Button Content=” 我铺满”/>
</StackPanel>
</DockPanel>

 

运行上述代码效果如下:

image

通过DockPanel完成对整体的布局,然后内部结合一些其他的布局控件,完成局部的细节的布局。

 

 

 

WrapPanel

WrapPanel容器我们也介绍过,该容器可以看做自动换行功能的StackPanel容器。下面我们就来分析下该容器的一般应用场景。

image 我们看到了windows8中的如下页面,如果我们仿制该页面的时候,其实我们可以采用wrappanel来实现自动的换行,下面我们来试试吧

最终代码如下:

<Window x:Class=”Samples.Window8Window”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”Window8Window” Height=”600″ Width=”800″>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=”50″/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<Grid Grid.Row=”0″>
<Label Content=”开始” FontFamily=”微软雅黑” FontSize=”30″/>
</Grid >
<Grid Grid.Row=”1″>
<WrapPanel Orientation=”Horizontal” ItemHeight=”100″ ItemWidth=”190″>
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
<Image Source=”/Samples;Component/Images/logo_small.gif” />
</WrapPanel>
</Grid>
</Grid>
</Window>

 

运行后,效果如下:

image

当然,我们的界面效果,还打不到美感,但是的确是自动换行。我们将水平方向,修改为垂直方向后,运行:

image

运行查看效果。

image

通过上面的简单案例,我们基本上知道了wrapPanel的用法。

 

总结

通过上面的介绍和demo的演示,我们知道了如何在项目中什么情况下,使用什么样的布局容器,通过实际的案例,我们更容易理解和掌握布局的模式。错误之处,还请大家反馈,我及时改正,谢谢!

18  扩展函数
扩展函数允许你调用动态链接库中的函数,调用Windows APIs,或运行另一个应用程序或安装程序脚本。UseDLL 和 UnUseDLL函数允许你装入一个DLL到内存中或卸载它并使用DLL。LaunchApp 和 LaunchAppAndWait函数允许你仍在执行脚本时运行另一个Windows 或DOS应用程序。
CallDLLFx
从一个外部DLL中调用函数。
Delay
延迟安装程序脚本的执行。
LaunchApp
运行另一个程序。
LaunchAppAndWait
运行另一个程序并等待该程序终止。
UnUseDLL
从内存卸载一个DLL。
UseDLL
把一个DLL装入到内存。
18.1  CallDLLFx
语法:CallDLLFx (szDLL, szFunction, lvValue, svValue);
说明:CallDLLFx函数调用一个指定的DLL中的函数。该函数必须使用下列固定定义,hwnd是InstallShield主窗口的主窗口句柄:
LONG APIENTRY YourFunction (HWND hwnd, LPLONG lpIValue, LPSTR lpszValue);
参数:
szDLL
指定包含要执行的函数的DLL的全限定文件名。
szFunction
指定由szDLL指定的DLL中的函数的名称。
IvValue
指定访问一个DLL函数时传递的长整型变量。
svValue
指定传递给DLL函数的字符串变量。
返回值:
CallDLLFx函数从DLL中的函数返回一个长整型数。
18.2  Delay
语法:Delay (nSeconds);
说明:Delay函数使一个安装程序脚本的执行延迟一个指定的秒数。其它和InstallShield同时运行的任务在InstallShield被延迟时仍正常进行。
参数:
nSeconds
指定程序被延迟的秒数。
返回值:
0:表明函数成功延迟了脚本的执行。
< 0:表明函数未能延迟了脚本的执行。
18.3  LaunchApp
语法:LaunchApp (szCommand, szCmdLine);
说明:LaunchApp函数允许你在脚本内运行另一个应用程序。该应用程序和脚本同时运行。InstallShield对运行的应用程序没有控制权并且不能确定运行的应用程序是否成功运行。运行的应用程序和其它在运行InstallShield时你启动的应用程序一样运行。你可以在InstallShield中运行任何你可以在操作系统中正常运行的应用程序。
参数:
szCommand
指定要运行的应用程序的全限定名。如果你指定一个没有路径的文件名,InstallShield在当前文件夹中,Windows 文件夹中,Windows系统文件夹中和列在环境变量PATH中的文件夹中查找。
如果应用程序的全限定名包括长文件夹名和/或一个长文件名,在把szCommand传递给LaunchAppAndWait之前先把它传递给LongPathToQuote。
szCmdLine
指定传递给szCommand标识的应用程序的命令行参数(如果有)。如果没有参数,则迟到一个空字符串。
返回值:
0:LaunchApp成功运行应用程序。
< 0:LaunchApp未能找到或未能运行应用程序。
注解:
·安装进程在应用程序被运行后继续。应用程序即使在安装脚本终止后仍可以运行。
·你也可以使用FindWindow 和 SendMessage函数来控制或发送消息给运行的应用程序。如果你想要在一个Window中运行一个DOS应用程序,你可以提供一个和DOS应用程序同名的PIF(Program Information File)。在PIF文件中,你指定应用程序运行在其下的一个窗口方式。
·运行一个DOS程序时,你不能确定返回结果DOS_ERRORLEVEL。然而你可以把一个DOS应用程序放进一个批处理文件,让批处理文件来识别错误并创建另一个包含返回错误代码的文件。然后你可以读该文件并确定从DOS应用程序返回的错误代码。
·LaunchApp 使用Windows API 的CreateProcess 来运行应用程序。
18.4  LaunchAppAndWait
语法:LaunchAppAndWait (szProgram, szCmdLine, lWait);
说明:LaunchAppAndWait函数运行由szProgram指定的带有szCmdLine指定的命令行参数的应用程序。第三个参数,lWait指示安装在继续前是否要等待直到运行的应用程序终止。
一个安装程序只能监控由szProgram指定的应用程序;如果该应用程序要运行其它应用程序或进程,安装程序不能监控它们。因此,安装程序将在第一个应用程序结束后继续,即使那时由第一个应用程序运行的其它应用程序仍在运行。注意如果运行的应用程序终止失败,则安装程序将无限等待运行的应用程序完成。
参数:
szProgram
指定要被运行的应用程序的文件名。建议要指定应用程序的完整路径和文件名。如果你不包括一个路径,InstallShield将使用被Windows API 函数CreateProcess使用的相同的查找次序来定位文件。如果文件未能在这些位置找到,函数将失败。
如果应用程序的全限定名包括长文件夹名和/或一个长文件名,在把szCommand传递给LaunchAppAndWait之前先把它传递给LongPathToQuote。
szCmdLine
指定传递给运行的应用程序的命令行参数。为运行没有命令行参数的应用程序,传递一个空字符串。
lWait
指定安装程序在继续前是否要等待运行的应用程序终止。在该参数位置传递下列预定义常量之一:
NOWAIT:指定安装程序在运行应用程序后立即继续,应用程序将和安装程序脚本同时运行。注意使用该参数等效于调用函数LaunchApp。
WAIT:指定安装程序必须等待直到由该函数运行的应用程序终止。
返回值:
1:表明应用程序成功运行。
< 0:表明应用程序未能运行。
注解:
·InstallShield 安装程序使用函数CreateProcess。在InstallShield运行应用程序后,它查找装入的应用程序的窗口句柄。如果它找到窗口句柄,则它在继续前等待直到应用程序窗口消失。
·安装程序不能监控一个不创建窗口的应用程序。如果指定的应用程序没有创建一个窗口,安装程序在运行应用程序后立即继续。注意应用程序的窗口不需要可见,但它必须存在,以便让安装程序等待。
·一些应用程序试图装入DLLs并且当那些DLLs不能被定位时不能正确运行。为确保一个应用程序能找到它需要的DLLs,有必要在调用LaunchAppAndWait前改变到包含可执行应用程序的目录。为改变当前目录,调用ChangeDirectory函数。
·如果运行的应用程序终止失败,则安装程序将无限等待运行的应用程序完成。
·LaunchAppAndWait以一个全屏DOS窗口来运行DOS程序。为以一个不同类型的窗口来运行一个DOS程序,你必须直接调用Windows APIs。
18.5  UnUseDLL
语法:UnUseDLL (szDLLName);
说明:UnUseDLL函数从内存卸载一个DLL。UnUseDLL将该DLL的锁计数减少一。当锁计数等于0时,InstallShield 卸载该 DLL。每一个对UseDLL的调用都必须有一个对应的对UnUseDLL的调用,因而DLLs在它们不再需要时不会留在内存中而浪费系统资源。一旦你卸载了一个DLL,你不能再调用该DLL中的函数。
Microsoft Windows系统 DLLs, 如 User.exe, User32.dll, Gdi.exe, Gdi32.dll, Krnl386.exe, Krnl286.exe, 和 Kernel32.dll,自动由Windows 装入和卸载。不要调用UseDLL 和UnUseDLL 来装入和卸载这些DLLs。
参数:
szDLLName
指定DLL的文件名。该参数不要包含路径。
返回值:
0:表明函数成功解锁和从内存卸载DLL。
< 0:表明函数未能解锁和卸载DLL。
注解:
如果脚本在用UnUseDLL正确卸载DLL前退出或终止,DLL将被锁定在内存。如果你试图再次访问DLL,你的脚本可能失败。你必须通过重启Windows来将DLL从内存中删除。
18.6  UseDLL
语法:UseDLL (szDLLName);
说明:UseDLL函数把一个DLL装入内存。在DLL已经被装入内存后,你的安装程序脚本可以调用该DLL中的函数。注意如果由szDLLName指定的DLL需要其它DLL,那些DLL必须位于某个文件夹中,该DLL试图从这个文件夹装入它们。正常时它是当前目录。为确保那些DLL可以被定位,在调用UseDLL前调用ChangeDirectory来改变当前目录到那些DLL所处的位置。若不能做到这些,则可能DLL不能被正确装入。
每次你将一个DLL装入到内存,该DLL的锁计数增加。锁计数计算使用该DLL的应用程序的数目。你不再使用DLL时,你必须马上调用UnUseDLL来卸载它。如果你不卸载不再需要的DLL,则该DLL在没有应用程序需要它时仍会留在内存中,因而浪费系统资源。在脚本中每个对UseDLL的调用必须有一个相对应的UnUseDLL调用。
Microsoft Windows系统 DLLs, 如 User.exe, User32.dll, Gdi.exe, Gdi32.dll, Krnl386.exe, Krnl286.exe, 和 Kernel32.dll,自动由Windows 装入和卸载。不要调用UseDLL 和UnUseDLL 来装入和卸载这些DLLs。
参数:
szDLLName
指定要装入的DLL名。如果你未指定一个扩展名,InstallShield假定文件扩展名为.dll 或.exe。建议该参数包括一个路径,但只是可选。如果该参数没有指定DLL的路径,InstallShield将使用和Windows API 函数LoadLibrary使用的相同的查找次序来查找DLL。
有关查找次序的更多信息可查看Windows API函数的说明。
为在你的安装中包含DLL,把它添加到安装文件窗格中的Language Independent文件夹的合适的子文件夹中。InstallShield将把它压缩到你的安装中。当Setup.exe执行时,它自动解压缩并把你的安装的内容拷贝到由SUPPORTDIR指定的临时目录中。然后你可以添加DLL文件名到SUPPROTDIR以便指向该DLL,如下所示:
szDLLName = SUPPORTDIR^”MYDLL.DLL”;
UseDLL (szDLLName);
如果你不放置你的DLL到你的安装中(通过把它插入到安装文件窗格中的合适文件夹中),你可以把文件和你的应用程序文件一起分散,然后从目标系统装入它。然而,如果你这么做,你必须指定你把DLL安装到的位置使得你的安装程序可以指向它。你也必须确保你的安装程序不会试图在DLL被传输到目标系统之前装入它。
返回值:
0:表明函数成功地把DLL装入内存。
< 0:表明函数未能把DLL装入内存。
注解:
·如果UseDLL失败,最大的可能性是DLL没有找到。如果这样,确保参数szDLLName指定了正确的路径。
·另一个通常的使用DLL的错误原因和DLL的依赖性有关:被你装入的DLL所访问的DLLs。如果你的DLL 访问的DLLs没有被装入或没有找到,你的DLL调用可能失败。如果发生这种情况,确保其它DLL存在于系统上并且它们是可访问的。
·如果脚本在用UnUseDLL正确卸载DLL前退出或终止,DLL将被锁定在内存。如果你试图再次访问DLL,脚本可能失败。你必须通过重启Windows来将DLL从内存中删除。

有时候当我们有日志文件在安装目录时,InstallShield清除不干净安装目录。那么我们可以自定义

删除安装目录.

InstallShield X:

//如果是卸载最后阶段

Onmoved函数中加入

if REMOVEALLMODE != 0 then
//MessageBox(TARGETDIR,WARNING);
DeleteDir(TARGETDIR,ALLCONTENTS) ;
endif;

另外,(!MAINTENANCE))用来判断是否在维护

InstallShield 6.2

1.定义一个全局变量BOOL bUnInstalled;
2.Before Move Data/onBegin中设为FALSE(安装,卸载,修复,重装都会运行)
3.Before Move Data/Maintenance UI Before/Dlg_ObjDialogs
switch(nType)
case REMOVEALL: ComponentRemoveAll(); bUnInstalled = TRUE;//卸载时运行
4.Afer Move Data/OnEnd(安装,卸载,修复,重装都会运行)

if bUnInstalled then
DeleteDir(TARGETDIR, ALLCONTENTS);
else
//MessageBox(“installed/Modify/Change”,WARNING);
//MessageBox(TARGETDIR,WARNING);
endif;

在InstallShield中调用批处理文件其实是安装打包中很常用的一种手段,通过批处理启动服务,进行数据库初始配置等等。

在实际操作中,针对不同工程类型,对批处理的调用还是有很多疑惑困扰着大家,撰写此文希望能帮助到那些被困扰的打包开发人员。

这里假设我们的需求是要启动安装路径下的批处理文件Sample.bat。

InstallScript工程

这种类型调用批处理相对简单,通过LaunchAppAndWait函数进行调用,下面是简单示例:

    szProgram  =   TARGETDIR  ^   ” sample.bat ” ;
szParam  =   “” ;
LaunchAppAndWait (szProgram, szParam,  LAAW_OPTION_WAIT  |  LAAW_OPTION_HIDDEN );

Basic MSI工程

MSI工程中对于批处理的调用较复杂,我们逐步说明如何操作:

  1. 通过Custom Action Wizard添加一个CA,启动向导
  2. 在Basic Information界面中为CA命名
  3. 在Action Type界面中指定Type为Launch an Executable;Location选择Stored in the Directory table
  4. 在Action Parameters界面中,Source选择INSTALLDIR,Target中输入内容:”[SystemFolder]cmd.exe” /c “[INSTALLDIR]Sample.bat”
  5. Additional Options界面默认
  6. 在Respond Options界面中,In-Script Execution选择Deferred Execution
  7. 在Insert into Sequence界面中,将CA插入到InstallFinalize之前,并设定Install Execute Condition为:Not Installed
  8. 其余默认

 

作者万炳宏 – Kevin Wan

InstallShield高级技术支持工程师

培训与技术咨询:kevin.wan#foxmail.com

技术专栏:http://www.cnblogs.com/installshield

这周工作时曾遇到一个问题。在一个MYSQL的表里做类似下面这一个很简单查询的时候耗时接近1秒钟的时间。

select sum(col5) , sum(col6) from table_name
where col_key_2='value1' and col_key_3 = 'value2'

表定义如下:

CREATE TABLE `table_name` (
  `col_key_1` date NOT NULL default '0000-00-00',
  `col_key_3` varchar(32) NOT NULL default '',
  `col_key_2` varchar(32) NOT NULL default '',
  `col5` bigint(20) unsigned default NULL,
  `col6` bigint(20) unsigned default NULL,
  `col7` bigint(20) unsigned default NULL,
  `col8` bigint(20) unsigned default NULL,
  `col_key_4` varchar(32) NOT NULL default '',
  PRIMARY KEY  (`col_key_1`,`col_key_2`,`col_key_3`,`col_key_4`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 |

整个表里大概只有200多万条数据。但查询的速度居然会慢到1秒钟才能查询出来,完全不可以忍受。

然后我给这张加上了另一个索引:KEY `class` (`col_key_2`,`col_key_3`)

查询的速度立马提高到0.00秒。

于是认真的查看了一下mysql 手册的8.3小节。

MySQL索引的种类和作用

mysql的索引分成:primary key, unique, index, fulltext index。 primary key是主键, unique是唯一索引, index是普通的索引。fulltext index是全文索引。 索引的作用就像C语言里的指针那样,直接指向表的一行。

可以对用col_name(N) 对符串的前N个字节做索引。 text类型和blob类型则必须要对前N个字节做索引。MYISAM最多支持1000个字节的索引, INNODB最多支持767字节的索引。

索引有下列作用:

1 帮助where语句快速查询。

2 进行多表连接

3 找到最大值和最小值(应该只有B-tree索引有这个功能,hash索引没有这个功能)

4 sort(应该只有B-tree索引有这个功能,hash索引没有这个功能)和group

多列索引

多列索引在对多个列同时进行查询的时候特别有用。多列索引最多支持16列。可以这样理解多列索引:

把多个列concat在一起,然后再对这个concat的值做一个索引。

比较神奇的一点是,比如你有一个索引针对col1 col2 col3这3个列时, 只查询col1和只查询col1 col2时也能用到这个索引。

比如有这个表:

CREATE TABLE test (
    id         INT NOT NULL,
    last_name  CHAR(30) NOT NULL,
    first_name CHAR(30) NOT NULL,
    PRIMARY KEY (id),
    INDEX name (last_name,first_name)
);

下面这些查询都可以用到多列索引:

SELECT * FROM test WHERE last_name='Widenius';

SELECT * FROM test
  WHERE last_name='Widenius' AND first_name='Michael';

SELECT * FROM test
  WHERE last_name='Widenius'
  AND (first_name='Michael' OR first_name='Monty');

SELECT * FROM test
  WHERE last_name='Widenius'
  AND first_name >='M' AND first_name < 'N';

下面这些查询不能用到多列索引:

SELECT * FROM test WHERE first_name='Michael';

SELECT * FROM test
  WHERE last_name='Widenius' OR first_name='Michael';

你可以在sql语句前使用explain语句来确定是否用到了索引。

比如下面这个查询就可以用到class这个索引

mysql> explain select sum(col5) , sum(col6) from table_name 
where col_key_2='value1' and col_key_3 = 'value2' \G 
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: table_name
         type: ref
possible_keys: class
          key: class
      key_len: 68
          ref: const,const
         rows: 1
        Extra: Using where
1 row in set (0.00 sec)

而下面这个查询则不能使用到索引:

mysql> explain select sum(col5) , sum(col6) from table_name
 where col5='value1' and col_key_3 = 'value2' \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: table_name
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 2357455
        Extra: Using where
1 row in set (0.00 sec)

索引的好坏

MySQL使用一个指标value group size来衡量索引的好坏。什么是value group呢? 就是具有相同索引key值的行数。这个指标显然是越小越好。最理想的情况就是每一个key值只对应1行, 这样的话我们的每次搜索一个key值都只返回一行,显然速度非常快。

可以用mysql提供的工具查看一个表的索引的好坏。可以先用analyze table语句更新统计,然后用show index来查看统计:

mysql> analyze table table_name;
+-----------------+---------+----------+----------+
| Table           | Op      | Msg_type | Msg_text |
+-----------------+---------+----------+----------+
| stat.table_name | analyze | status   | OK       |
+-----------------+---------+----------+----------+
1 row in set (3.13 sec)

mysql> show index in table_name;

table_name这张表有两个索引PRIMARY和class,PRIMARY这个索引是一个包含4列的多列索引。

Cardinality这个值表示索引值的不同的行数。

例如:

col_key_1值有18行。

col_key_1+col_key_2 值有392909行。

col_key_1 + col_key_2 + col_key_3 值有235745行。

col_key_1 + col_key_2 + col_key_3 + col_key_4值有235745行。

通过索引值的行数,我们就可以看出来索引好还是不好了。索引值不同的行数越多索引就越好。当索引值不同的行数=表的总行数就达到最理想的情况 value group size = 1了。

B-tree索引和Hash索引的比较

默认情况下MySQL都是使用B-tree索引。来谈一下Hash索引的缺陷:

1 只能处理’=‘ 这种where 子句,而对于< >是无能为力的。 这和B-tree索引是有序的,Hash无序的有关。

2 无法处理order by。 原因同上。

3 无法得知两行之间的距离。 原因同上。

4 只能搜完整的字段,不能只搜字段的一部分。 而对于B-tree索引, 支持搜索字符串最左边的一部分。例如”police%” 。

WPF在系统设计的时候少不了登陆,登出,退出系统!

那么注销系统和退出系统究竟该怎样做呢!

首先行不通的事情就是直接关闭登陆页面,也就是起始页面,这样的话系统真的就退出,因此采用的方法应该是在登陆成功之后,隐藏掉登陆页面。

那么我们又怎样来进行登出操作呢?退出系统操作呢?

WPF注销系统与退出系统实现

 

在关闭的时候,我们可能在MainWindow中直接有退出系统按钮,那么我们在点击的时候直接调用Application.Current.ShutDownOK,关闭了,没有问题,但是如果我们不是点击的退出系统按钮,而是直接点击Window自带的关闭呢,结果是什么?可想而知,我们只是退出了当前的主页,而没有退出系统,因为还有登陆页面在隐藏。

因此我们用上面的关闭方法是行不通的,那么到底该怎样做呢,其实很简单,我们知道每个窗体都有Closing的事件,只要我们捕捉到加以利用,一切就OK了,因此我们在MianWindow窗体初始化的时候就监听它的Closing事件,这样无论我们点击了什么,只要当前的窗体关闭了,就相当于关闭了整个系统。

问题还是存在,那么如果我们又想注销系统怎么办呢?当然我们得关闭MainWindow,但是我们不是真想关闭系统而是想显示Login窗体,这样我们以上的做法不是不合理了?难道要改套路不成?

不是的,我们可以设置在MianWindow中设置一个bool的全局变量,默认值为trueClosing事件中,我们只需要判断一下这个变量就可以了,如果为true,那么就关闭系统,如果为false,则显示Login窗体,(当然,在那个页面进行控制,则在哪个页面就应该传递过来Login窗体的对象,我认为比较好用的方法是定义一个属性,通过传递来进行,例如在Login打开MianWidow的时候,我们就将MainWindow.Login=this),清空该用户的信息,OK,那么我们什么时候设置那个全局变量呢?当然是在我们点击注销按钮的时候了,在点击注销按钮的时候,就两个动作,一个是将全局的变量设置成False,另一重要,就是关闭自身,this.close()OK 这样就完成了。

这是一个Basic MSI基础知识,有不少网友问,所以我再啰嗦一遍。

A. 首先在Installation Designer/Behavior and Logic/InstallScript视图中,选中Files点击鼠标右键选择“New Script File”,默认会生成一个setup.rul的文件,并有一个MyFunction的函数声明和。

B. 之后我们在MyFunction函数内添加一行代码:MessageBox(“MyFunction”, INFORMATION);

当然,你也可以修改函数名,用更有实际意义字符串代替。

C. 在Installation Designer/Behavior and Logic/Custom Actions and Sequences视图中,选中顶端的Custom Actions点击鼠标右键选择“Custom Action Wizard”(这里的Custom Action我们经常也简称为CA),下面按着向导逐步说明。

  1.  第一个界面是Custom Action Wizard欢迎界面。
  2. 在第二个Basic Information界面中,选择CA的Name,而Comment可忽略。
  3. 在第三个Action Type界面中,在Type的下拉菜单中选择Run InstallScript Code。(这里你会发现你可以通过CA调用托管代码,其他可执行程序,以及JScript,VBScript等)
  4. 在第四个Action Parameters界面中,在Source的下拉菜单选择的MyFunction。(如果脚本Setup.rul中你有多个函数声明,你会发现都会显示到下拉菜单中)
  5. 之后的界面都按照默认设置,直到点击Finish结束。

D. 在同一树状结构中,Sequences/Installation/Execute,选中Execute点击鼠标右键选择Insert,你会看到在C中你添加的CA,选中你的CA,然后设置Condition。(注意如果是想在安装时执行:Not Installed;如果是卸载执行:Installed)

E. 有关我们的CA在Sequence中的位置, 可根据情况调整位置。

CA可以在Sequence中被调用,也可以在各个Dialog中被调用执行。

怒了啊。。。 刚才编辑了那么多,结果一个误点,就把所有的东西都弄没了。。。我勒个。。。。。

好吧好吧好吧,,我重新写,我重新写。。。

以前没有做过 installscript工程的项目,只做过Basic MSI工程的,所以好多难点都要自己重新来找解决方案。下面就一一分享一下我的成果。

1. installscript 如何判断当前为哪种安装语言?

主要是没有找,用正宗的方法怎么去判断,而是自己想出来的一个方法,不过还是很管用的,首先,a.在string editor里面添加一个string  ID_STRING_LANGUAGE, b.然后在英文的里面输入 1033,中文里面输入2052,c.最后在installscript 中引用 @ID_STRING_LANGUAGE字符串,然后做什么操作就看你的需求了。

2. installscript 双语安装包如何在中英文安装下出现不同的license内容。

我们第一想到的就是加载不同的文件喽~~ 嗯,你猜对了,确实应该加载不同的文件,其实我首先想到的是去 dialog里面寻找license的对话框,然后在相应控件上加载相应的 rtf文件,结果验证是出现乱码的。。。。 然后我就想,应该在代码里面写,然后又去代码里面OnFirstUIBefore 下的 Dlg_SdLicense2 里面的 szLicenseFile 这个的值根据语言变化而变化,加载的是rtf文件,但是发现还是有问题。 后来发现有rtf和txt两种文件。我尝试着把rtf改成了txt文件,结果真的可以了,表示不太懂这是为什么?有懂的可否解释一下???

3. installscript 快捷方式的创建???

创建普通的快捷方式,跟basic msi 一样,去System configuration下面的shortcuts进行创建,但是此快捷方式的icon和target只能从 安装目录下选择,Icon File 我是这样写的:<TARGETDIR>\XDict.exe 。 Target: <TARGETDIR>\XDict.exe。如果还有其他的需求,可以

对其他的设置进行修改。

现在说一下卸载的快捷方式,着实难了一下呢。

a. 可以 将UNINSTALL_STRING 里面的内容的前半部分的 setup.exe路径写到 Target下面。然后参数写 -unist。

b.  也可以自己制作卸载程序,但是其实自己制作卸载程序也是调用的上面的那个路径。制作卸载程序的方法,我接下来会放下载链接。(其实制作程序是借鉴的别人的,不是我自己写的。)

4. 如何修改安装程序的任务栏图标呢?

默认都是installshield的,这个真的很讨厌。找了好多资料,都没找到,最后是问的一个大侠。这个不能通过installshield来修改,只能自己写外部代码来改变,这个大侠用的c++的代码来修改的。在installscript里面进行修改。外部代码源文件以及实例文件,一会儿我会全部奉上。

5. 如何判断当前为卸载过程??

这个也着实难了一下,怎么判断呢?百度出来一个判断 UNINSTAL_STRING是否为空 ,结果试了一下,不管用。

后来我就测试script脚本里面的代码,貌似卸载的时候会走 OnMaintUIBefore 函数,里面有判断REMOVE的地方 —— Dlg_Start 下面。 大家可自行查找。

其实却是有标识当前状态为卸载状态的 :REMOVEALL。可以这样判断: if( REMOVEALL){}    嗯嗯,就是这样。

6. 如何使一个feature 必选呢?不能去掉??

这样来操作,创建一个空的feature,然后将其设置为 No visiable,然后将必选的那个feature的必须feature为空的那个feature,就这样就可以了。

7. 比如我安装过程中,又调用了其他的安装包,而且是静默安装,这时候肯定会卡死好长时间?怎么办??? 怎么让它不卡死,而且把进度条显示出来。

我的需求是,选择了某个feature,然后安装其中相对应的安装包,哦,有人说,你这个需求是怎么设置的?可以在feature设置里面的 feature event下面 有一个 OnInstalling,在其里面进行加载相应的函数。(自己可把安装安装包写在一个函数里面)。

然后在函数里面加载如下代码:SetStatusWindow( 20, @ID_STRING16 ); 前面的那个参数是显示进度条显示多少,设置20表示已经安装20%.   Enable(STATUSEX);    StatusUpdate( ON, 100 );   我的理解是 那个100应该是更新走到 100%。

示例代码下载处:  http://url.cn/RiRdX0

还记得GOT第七季的剧透吗?

截止目前西班牙泄露的第6集,是不是跟之前的剧透都一一对应了?

还是同样的黑客泄露,第八季大结局简直惨不忍睹。



 

前方高能

第八季简单剧情概要:

异鬼大军攻破临冬城继续南下,最终在鹰巢城被人类战胜。另一方面瑟曦和攸伦结婚后被杀,攸伦坐上铁王座,琼恩和詹姆等人不服率兵攻打君临。

—主要人物最终命运:

死亡角色:

布兰·史塔克:在异鬼大军攻占临冬城的战役中被异鬼杀死在神木林。

雷哥:在异鬼大军攻占临冬城的战役中雷哥由布兰控制作战,在布兰被杀后雷哥坠落被尸鬼所杀,死后尸体被卓耿火化因此没变成冰龙。

梅拉·黎德:在异鬼大军攻占临冬城的战役中为保护布兰而死。

霍兰·黎德:告知琼恩·雪诺他的真实身份之后在与异鬼的战斗中战死。

波德瑞克:在异鬼大军攻占临冬城的战役中战死。

托蒙德:在异鬼大军攻占临冬城的战役战死。

灰虫子:在异鬼大军攻占临冬城的战役战死。

莱安娜·莫尔蒙:在向熊岛转移的途中遇异鬼军队袭击死亡。

席恩·葛雷乔伊:在攸伦攻打派克岛的战斗中被攸伦杀死。

布蕾妮:在异鬼大军攻占鹰巢城的战役中被异鬼杀死。

冰龙韦塞里昂:在异鬼大军攻占鹰巢城的战役中被卓耿杀死并火化。

夜王:在异鬼大军攻占鹰巢城的战役中被卓耿杀死并火化。

瑟曦·兰尼斯特:和攸伦结婚后,攸伦买通千面神殿刺杀瑟曦,最终被假扮成科本艾莉亚·史塔克杀死。

科本:被艾莉亚·史塔克杀死

格雷果·克里冈:被桑铎·克里冈重伤等死。

丹妮莉丝·坦格利安:生下与琼恩·雪诺的女儿莱安娜·坦格利安后失血过多,恳求瓦里斯结束了她的生命,死后尸体被黄金团带到君临示众。

瓦里斯:在攸伦的黄金团攻陷龙石岛后被黄金团杀死。

梅丽珊卓:回到维斯特洛,想烧琼恩·雪诺和丹妮莉丝的孩子,琼恩·雪诺判死刑,经过自己请求后被龙焰烧死。

卓耿:在最终君临战斗中战斗中受重伤,最后喷出龙焰点君临地下的野火,被炸死,同时被炸死的有:攸伦·葛雷乔伊、詹姆·兰尼斯特、乔拉·莫尔蒙、桑铎·克里冈、琼恩·雪诺。

幸存角色:

莱安娜·坦格利安:丹妮莉丝和雪诺之女,七国女王。

提利昂·兰尼斯特:首相,辅佐莱安娜·坦格利安,同时掌管凯岩城。

弥桑黛:莱安娜·坦格利安监护人,与提利昂·兰尼斯特成了一对(是否结婚未知)。

艾莉亚·史塔克:雪诺死后心灰意冷和娜梅莉亚一起乘船离开维斯特洛再无音讯。

珊莎·史塔克:临冬城领主和谷地领主,与詹德利·拜拉席恩结婚。

詹德利·拜拉席恩:与珊莎·史塔克结婚,风息堡领主。

艾德慕·徒利:河间领主,他的其中一个儿子捡到了艾莉亚·史塔克的缝衣针,艾莉亚之前把剑丢掉了。

山姆威尔·塔利:河湾领主,和山姆的妈妈、小山姆以及吉莉住在一起,同时吉莉怀了山姆的孩子。

雅拉·葛雷乔伊:铁群领主。

波隆:娶了多恩领主,奥伯伦的某个女儿。

戴佛斯·席渥斯:得知雪诺死后和山姆说想回家找老婆去了之后就再无音讯。

—剧终彩蛋:

在极寒之地,布兰被一个异鬼复活成为新的夜王。


看了以上的剧透是不是感觉到炸裂???

下面是分剧情介绍哦!


来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

权力的游戏?S08 E05?泄露剧本

HBO LEAK

NOTE:这同样是个预印本,很多情节未必会和剧集播出时相同。

NOTE:时长100 min上下

第一个场景只有?R.Djawadi?的配乐,前?5 min?没有对话。

第一个镜头是谷地骑士在为大战做必要的准备,由詹姆、提利昂、艾德慕、波隆领军的兰尼斯特家族、徒利家族和史塔克家族的军队正向谷地进发。黄金团登上攸伦·葛雷乔伊的船只(起航前往龙石岛)。暴风雪侵来,约恩·罗伊斯命令谷地骑士备好龙晶长枪迎敌,罗宾·艾林在其侧共同作战。罗伊斯爵士对小罗宾指挥他的将士颇感不满,你几乎可以从他的脸色上看出这一点。在罗伊斯督促谷地备战时,尸鬼大军偏便已然攻入血门,罗宾·艾林试图对弓箭手下令,但弓箭手们没有听他的。在鹰巢城内我们会看见珊莎、詹德利和塔斯的布蕾妮,詹德利向珊莎告别随后就加入战斗,珊莎则告诉詹德利自己希望他能平安归来,詹德利则觉得自己很可能会战死沙场。布蕾妮也希望加入战斗而珊莎对此表示异议。,珊莎希望布蕾妮能待在她身边,但布蕾妮说尸鬼攻破鹰巢城之时,她将谁也保护不了。她曾发誓誓死保护珊莎,但此时最好的方法就是去守卫谷地的门户以阻止夜王的军队进攻鹰巢城。珊莎抱了抱布蕾妮,感谢她在一路上为史塔克家族的所作所为,随后布蕾妮便离开鹰巢城加入了战斗。下一个镜头是龙石岛,黄金团渡海来擒丹妮莉丝女王的消息传到了这里。丹妮命令她手下仅存的无垢者和多斯拉克骑兵抵御入侵,瓦里斯建议丹妮立刻逃离龙石岛,但他的建议未被采纳。丹妮察觉到自己的羊水已破,随时可能临盆,弥桑黛和山姆威尔则帮丹妮作必要的休息。山姆威尔忧心忡忡,因为丹妮的孩子随时都可能出生。三名异鬼在远处静静地看着亡者军团击溃谷地骑士,夜王骑御韦赛里昂高高在上俯视着这壮观的景象。韦赛里昂(在先前的战斗中)失去了一只眼并有一只翅膀受了重伤。布蕾妮拼死一战,罗宾·艾林被几只尸鬼围攻但他成功逃入鹰巢城。珊莎对罗宾说他曾经发誓要为子民而战,罗宾则说自己毫不关心他们的死活。布蕾妮、罗伊斯爵士和詹德利仍然在奋战,但胜利的天平正逐渐倾向亡灵那方。异鬼最终攻入鹰巢城,不死之龙的怒号响彻八方。独眼韦赛里昂飞临罗伊斯爵士和谷地骑士上空,喷出烈火将其化为灰烬。此时一声龙吟响起,琼恩·雪诺身骑卓耿驾临战场。琼恩协助谷地骑士撤出,身下是混乱中的兰尼斯特家族、徒利家族和北境的军士。许多尸鬼(在与维斯特洛的军队作战时)葬身卓耿的烈焰之下,但龙焰似乎并不能对异鬼造成伤害。波隆用两根龙晶长枪杀死了一名异鬼,布蕾妮独战一名异鬼落于下风并最终被冰刃击中下腹,目睹此景的詹姆变得疯狂并从背后摧毁了异鬼。詹姆抱着正在死去的布蕾妮悲伤得不能自已,布蕾妮希望詹姆能护珊莎和艾莉亚周全,一如自己向凯特琳夫人保证的那样。詹姆承诺说自己将会为她去保护那两个女孩,布蕾妮的最后遗言是她认为詹姆是个有荣誉感的人,说罢布蕾妮便在詹姆的怀中死去了。艾莉亚、猎狗和娜梅莉亚进入大雪纷飞的君临,猎狗说自己一点也不想念这臭烘烘的城市,艾莉亚同意他的看法。艾莉亚路过被炸毁的贝勒大圣堂时注意到君临相比她离开时早已截然不同,在这里她想起了父亲奈德·史塔克,她告诉猎狗是时候让兰尼斯特血债血偿了。丹妮莉丝开始分娩,山姆威尔和弥桑黛为她助产。瓦里斯目睹小股黄金团登陆,无垢者们则竭力阻止他们登上通往龙石岛宫殿的阶梯,多斯拉克人与黄金团在海岸上正面冲突。乔拉和部分北境军队也加入了保卫战,乔拉试图进入城堡去保护丹妮。由于艾德慕和提利昂的指挥若定,在鹰巢城的尸鬼大军随着时间流逝而逐渐减少,夜王不再骑在韦赛里昂背上而是试图与另一名异鬼进入鹰巢城,韦赛里昂则尝试攻击詹德利并向他喷吐火焰,詹德利将一柄龙晶长枪插入韦赛里昂口中。冰龙从血门的岩石上坠落在地,卓耿给了夜王和韦塞里昂最后一击并用龙焰将韦塞里昂的尸体点燃。乔拉撤入宫殿时发现丹妮正在生产,情况对丹妮似乎并不乐观,悲伤的山姆说母子不能同时幸存,他只能救一个。乔拉希望丹妮能活下来但丹妮不愿放弃孩子,之后丹妮成功诞下婴儿。山姆威尔告诉丹妮她生下了一个女孩并让丹妮为她取个名字,虚弱的丹妮给孩子取名为莱安娜,一如琼恩所愿。弥桑黛、瓦里斯和乔拉意识到丹妮的生命正在流失,黄金团则在战斗中取得上风并攻至宫殿外的阶梯,丹妮向乔拉告别并感谢他能一直陪伴在身边,乔拉则向丹妮表达爱意,丹妮请求乔拉能继续他的誓言并和弥桑黛一起保护自己的女儿。丹妮恳求乔拉能杀了自己结束痛苦,乔拉则说自己永远不会做那样的事。最后瓦里斯举起山姆的族剑碎心,一剑洞穿了丹妮的心脏。瑟曦和魔山在王座厅会见科本,科本将丹妮莉丝战败的消息告知瑟曦。瑟曦狂笑并为其胜利而痛饮,片刻之后瑟曦开始感到眩晕(这时观众应该都意识到了科本是艾莉亚假扮的,她用贾坤的毒药毒杀瑟曦),科本直视着瑟曦,瑟曦也意识到事情有怪。艾莉亚摘下面具、亮出身份,她呵斥瑟曦说即使是雄狮也不应与群狼为敌。瑟曦命令魔山杀死艾莉亚但艾莉亚告诉她那是无用之举,因为她带了娜梅莉亚和猎狗来解决魔山。娜梅莉亚将瑟曦咬成重伤而猎狗则与魔山战作一团,随后艾莉亚切开了瑟曦的喉咙,娜梅莉亚与猎狗则击败了魔山。魔山似乎不会死,他的头盔在战斗中掉下,猎狗抓住机会用火将点燃魔山的脸。猎狗、艾莉亚和娜梅莉亚离开红堡,留下魔山在那里孤独的等死。黄金团的团长迈进宫殿,他命令瓦里斯交出丹妮,瓦里斯告诉他丹妮已死。黄金团的团长将瓦里斯推开并捅死了他,随后他们找到了丹妮的尸体并将她带回君临献给瑟曦。山姆威尔则和丹妮的孩子、弥桑黛躲藏起来。黄金团撤出龙石岛,返航回到君临。攸伦回到君临发现瑟曦已死,但他假装为瑟曦之死而悲伤。琼恩·雪诺和珊莎·史塔克回到龙石岛却发现所有人都死了,山姆威尔在宫殿的阶梯上见到琼恩并将孩子交还给他。琼恩开始啜泣并问丹妮是否还活着,山姆将那悲伤的事实告诉了琼恩,他还告诉他丹妮为他们的女儿取名为莱安娜·坦格利安。此集最后一个镜头是葛雷乔伊家族的攸伦一世登基,是为七国国王。阵亡:罗伊斯爵士、塔斯的布蕾妮、罗宾·艾林、冰龙韦塞里昂、丹妮莉丝·坦格利安女王、夜王、科本、瑟曦·兰尼斯特女王、魔山和瓦里斯。

第5集集终。


权力的游戏?S08 E06?泄露剧本

HBO LEAK

NOTE:对于那些关心戴佛斯·席渥斯在第五集境遇的人而言——他在那一集中并不是关键角色,但他的确出现了而且和琼恩、詹姆以及詹德利等人并肩作战。他并未对主要的事件产生影响,但他确实在那一集中参与了战斗。

FINAL EPISODE:(最终集)注意这是预印本,很多情节可能会在实拍时改变。

时长:110 min?上下

第一场景发生在龙石岛,詹姆、波隆、詹德利、提利昂和戴佛斯爵士得胜归来,艾德慕应该是返回了奔流城。琼恩接待了众人并将丹妮的死讯告知他们,提利昂悲痛欲绝,随后他与琼恩在龙石岛的悬崖上有一段对话。提利昂告诉琼恩自己曾是那么信任丹妮,坚信她能将这该死的世界变得更好而现在他却只能放弃这个不切实际的梦想。提利昂问琼恩丹妮死于谁手。琼恩说丹妮因难产而死,尸体被黄金团带走献给瑟曦。然后提利昂问及孩子的情况,琼恩则回答说孩子一切安好。提利昂苦笑一番但他随后又看到一条葛雷乔伊的船靠上岛屿,琼恩和提利昂走向那条船,原来是雅拉·葛雷乔伊抵达了龙石岛。雅拉将自己逃出攸伦魔爪但失去了所有军队的事情告知两人,琼恩问她席恩的下落,雅拉回答说席恩在亡者军团袭来之前就已战死。下一个镜头是悬在君临城墙上的丹妮的尸体,君临的民众向丹妮的尸体扔掷粪便、嘲笑羞辱她的遗骸,猎狗和艾莉亚在附近的一个啤酒店里得知攸伦称王、君临即将遭到围攻的消息。艾莉亚想帮助琼恩、与他并肩而战但猎狗认为自己的事情已经解决了(想尽早离开),最终猎狗同意与艾莉亚留在君临助琼恩一臂之力。之后我们会看见攸伦·葛雷乔伊与黄金团的团长在王座厅交谈,他感谢黄金团将龙女王的尸首带来但他认为黄金团没能活捉她是他们的耻辱,攸伦说如果他们将她活着带来自己本会操她的(fucked her),但他们令他失望了。攸伦期望着自己对北境伪王琼恩·雪诺的审判早日到来,因为北方佬们将首先对君临发起进攻。攸伦命令新学士放出所有信鸦并要求各大王国的领主对其宣誓效忠,否则他威胁将用自己在君临的佣兵团一个一个将其摧毁。琼恩、詹姆、波隆、提利昂、乔拉、珊莎、詹德利、戴佛斯、弥桑黛、山姆威尔以及雅拉齐聚龙石岛的议事厅讨论如何应对来自攸伦的威胁,就是瑟曦惨死之后自立为王的攸伦。詹姆认为攸伦是杀死瑟曦的凶手并因此暴怒,雅拉同意詹姆的看法,戴佛斯则认为攸伦这个疯子不应统治七大王国,而他们应该聚集所有力量回击他。詹德利表达了自己对攸伦以及他夺走自己已遭灭门的拜拉席恩家族王位(或权位)的仇恨,提利昂和詹姆则想用兰尼斯特家族亲兵来攻击攸伦。提利昂同时认为他们应该说服效忠丹妮的人们为琼恩和丹妮的孩子而战,因而我们有北境军队、兰尼斯特军队、残存的多斯拉克人和无垢者以及卓耿为琼恩而战。山姆威尔再度提起琼恩的身世,但琼恩似乎不愿他人知道此事。琼恩再次清楚地声明自己的追求并非铁王座。戴佛斯爵士为琼恩担保,他告诉其他人琼恩是他们从攸伦的魔掌中救出黎民百姓最后的机会而且他相信琼恩将会成为一个真正的好国王。琼恩是他们唯一的希望。其他人同意戴佛斯的说法,琼恩最终接受了黄袍加身。龙石岛议事厅之后,琼恩与珊莎有过一段对话。珊莎对琼恩的身世感到疑惑并且她很难相信父亲自始至终都在欺骗整个家族,无论是对母亲凯特琳还是对琼恩。琼恩请求珊莎统治好北境和临冬城,因为她是临冬城的继承人。她答应了琼恩并告诉他自己早已准备好离开龙石岛了,两人拥抱彼此,之后分道扬镳。弥桑黛将红袍女再次来到龙石岛的消息告诉了琼恩,梅丽珊卓告诉琼恩自己已经准备好为自己的罪孽赎罪并打算向他揭露自己的真实身份。她说自己已经完成了使命,预言也已都应验。梅丽珊卓指出自己曾劝说丹妮将孩子献祭给光之王,戴佛斯则说自己曾发誓要处死她,詹德利也是如此想法。琼恩本打算绞死梅丽珊卓但红袍女则希望自己能死于烈火,因为那是最纯粹的死亡。红袍女被带往龙石岛外处决,最终如她所愿死于卓耿的龙焰之下。舰队从龙石岛出发前往黑水湾,提利昂、弥桑黛、戴佛斯、山姆威尔和小莱安娜则留守龙石岛。我们会看到乔拉、詹德利以及波隆的登舰镜头。琼恩恳请提利昂和弥桑黛照料好小莱安娜以防自己身遭不测,琼恩与山姆告别,感谢他能始终忠诚的陪在自己身边。攸伦伫立在君临的城墙上目迎坦格利安舰队而来,丹妮腐烂的尸体摇曳在下方。我们会发现琼恩身着坦格利安-史塔克双色战甲驾驭卓耿飞翔在蒙冲斗舰之上,攸伦则带来了按照他的命令重新设计的射龙毒蝎。兰尼斯特军队首先登陆并开始进攻,黄金团则守卫着君临的城墙。两名黄金团的团长骑着大象碾碎兰尼斯特士兵,北境的军队加入战斗协助兰尼斯特军,多斯拉克骑兵则从侧翼杀出。琼恩和卓耿加入战斗,卓耿连人带象一起焚化了黄金团的一名团长。垂死之象在火焰中挣扎并在死前杀死了几个不知名的士兵。雅拉挥舞着巨斧砍杀,詹德利甩动着重锤击碎敌军的头颅。攸伦命令手下人用毒蝎射杀巨龙,他共有三架巨弩,卓耿躲开了一箭但很快就被另外两箭击中双翼,卓耿迅速摔落地面。兰尼斯特军、史塔克军、多斯拉克人和无垢者最终突破了城门,琼恩则试图带着卓耿逃到龙穴(因为他注意到卓耿身受重伤、很难继续飞行)。艾莉亚看到卓耿飞向龙穴的同时猎狗加入詹姆、波隆、乔拉的战斗,詹德利和雅拉以为攸伦将会藏身红堡,所以他们准备一路拼杀到那儿。但实际上攸伦正在命令他的军队前往龙穴去处死琼恩·雪诺和他的那只野兽,波隆和詹姆发觉攸伦与黄金团一并前往龙穴并尾随他们。詹姆意识到琼恩正在极度危险之中,因为除他之外只有提利昂知道龙穴之下还埋藏着大量野火,如果卓耿在那里喷出龙焰,野火将摧毁整个龙穴和在那里的所有人和物。艾莉亚和娜梅莉亚进到龙穴时琼恩正在照看卓耿的伤势,琼恩试图将插在龙翼上的长箭拔出(但失败了)。琼恩尽管对见到艾莉亚非常惊奇但他随后要求艾莉亚立刻离开君临,艾莉亚一开始并不想离开,暗示琼恩自己想与他一起战斗。艾莉亚拍了拍卓耿并说自己小时候总是梦想着像传说中的坦格利安勇士那样驭龙飞翔,琼恩微微一笑但他随后强烈地劝阻艾莉亚让她离开,因为他们正身处险境。艾莉亚最终听取了琼恩的劝告,两人拥抱告别。艾莉亚对离开琼恩很是失望沮丧。雅拉和詹德利杀入红堡却发现攸伦早已逃往龙穴,雅拉为此非常恼怒,因为她迫切想亲手结果了叔叔。攸伦和黄金团冲进龙穴,攸伦和琼恩一对一单挑。詹姆、猎狗、乔拉以及一小股兰尼斯特士兵冲入龙穴企图救出琼恩,波隆不愿冒险并待在原地。詹姆从背后捅伤了攸伦,当时攸伦几乎杀死琼恩·雪诺,攸伦受了致命伤。詹姆尝试将琼恩带离龙穴,乔拉和猎狗则与剩余的佣兵战斗为琼恩平安离开扫清障碍。黄金团的佣兵们向卓耿投掷长枪,卓耿垂死挣扎。突然,卓耿毫无预兆地释放烈焰,龙穴开始震动,詹姆意识到卓耿的火焰点燃了深埋地下的野火,他喝令琼恩快跑逃生。受了致死伤的攸伦狂笑,告诉所有人今日他们都将惨死于此。野火在琼恩、猎狗、詹姆和乔拉逃离之前炸飞了龙穴,无人幸存。雅拉和詹德利在远处惊恐地望着野火的爆炸,民众则在更多野火被点燃之前加速逃离君临。下一个镜头是龙石岛,提利昂看见几艘坦格利安战船逃回到这里。雅拉和詹德利将君临的野火爆炸,琼恩、哥哥詹姆以及乔拉未能生还的消息告诉了提利昂、戴佛斯、山姆威尔和弥桑黛。提利昂听到消息时彻底崩溃,而戴佛斯和山姆听闻琼恩的死讯后同样如此。山姆威尔和戴佛斯进行了短暂的交谈,山姆询问戴佛斯日后的打算,戴佛斯则说自己可能会回到自己离开太久的妻子身边。山姆笑了笑,说自己同样如此。我们接下来会看到艾莉亚和娜梅莉亚准备登船的镜头。艾莉亚告诉船长自己已与维斯特洛毫无瓜葛,当船长问一个女孩和一匹狼离开平和的维斯特洛到厄斯索斯做什么时,艾莉亚答道:“Valar Morghulis.”我们目送艾莉亚乘船东去,我们再看到众人已经是三年以后了。下一个镜头是山姆正在教小山姆读书识字,吉莉·塔利女士,这就是她现在的名字,现在是河湾王国的女主。山姆的母亲、妹妹塔拉和吉莉交谈着分享彼此的爱,山姆来到女士们之间询问她们在说些什么,但山姆的母亲笑说这是女生之间的私密话。最后一个镜头是山姆和吉莉,吉莉告诉山姆一个好消息——她怀了他的孩子,山姆得知自己与吉莉有了一个孩子开心得不知所以,他告诉吉莉自己将永远视小山姆为长子,而他也将有一天会从自己手中继承河湾王国。再下一个镜头则带我们来到了临冬城,我们看到珊莎夫人和詹德利公爵在一起。珊莎与詹德利、艾德慕和萝斯琳看着自己的孩子们彼此嬉戏打闹、训练拳脚。艾德慕仍为奔流城公爵。珊莎注意到艾德慕的一个孩子腰上带着艾莉亚的佩剑缝衣针,她问艾德慕是如何找回那柄剑的。艾德慕答说是自己的儿子威廉在打猎途中从丛林中找到的,他问珊莎艾莉亚是否还活着,珊莎回答说她大概还活着吧。毕竟,艾莉亚总能绝处逢生。珊莎和詹德利下到临冬城的地窖里,琼恩的雕像矗立于斯。詹德利问珊莎琼恩是否属于这里,珊莎说这就是他真正的归宿,埋葬于他真正的父亲奈德和兄弟身边。詹德利开玩笑说他实际上甚至都不叫“琼恩”,珊莎则答说对她而言他永远是琼恩·雪诺。珊莎说自己同时身为临冬城夫人和谷地女主有很多事要做并问詹德利将打算怎么处置已成空城的风息堡,詹德利则说自己很快就会着手处理这件事了,因为他现在已被摄政王合法化为一个真正的拜拉席恩。之后我们将会看到摄政王、莱安娜·坦格利安女王的女王之手提利昂·兰尼斯特,提利昂正看着弥桑黛为莱安娜读睡前故事,那是蕾妮斯和维桑尼亚·坦格利安的传说。莱安娜问弥桑黛自己的妈妈是否也曾驾驭巨龙,弥桑黛回答说丹妮莉丝就是那么一个无畏的勇士。下一个镜头是提利昂和弥桑黛两人共枕而眠,他们说着有关莱安娜的事情。莱安娜并不是一个好相处的孩子,但提利昂开玩笑说她将很好地传递坦格利安家族的香火。(这里理解成莱安娜生育能力极佳似较为妥当)弥桑黛大笑说作为琼恩和丹妮莉丝的孩子,提利昂就不要指望莱安娜这个了。提利昂夸赞弥桑黛在莱安娜的教育中发挥了极好的作用,之后两人便鱼水缠绵、共享云雨之乐。最后一个场景是提利昂和莱安娜站在巨幅的维斯特洛地图前,提利昂告诉莱安娜终有一日她将真正的君临七国。他向莱安娜解释七国的情况,北境和谷地正在珊莎·史塔克夫人的治下,她是史塔克家族仅存的后人而且她嫁给了风暴地的领主詹德利·拜拉席恩公爵;河间地由奔流城公爵艾德慕·塔利统治,而空荡荡的凯岩城则掌握在提利昂手中;蓝道·塔利之子山姆威尔·塔利公爵统领着河湾王国,雅拉·葛雷乔伊则手握铁群岛大权。莱安娜问及多恩,提利昂则回答说多恩领的领袖是红毒蛇奥柏伦·马泰尔的一个私生女,而她则嫁给了一个总是梦想着能有一个庄严华美的宫殿作屋、一位高贵美丽的女士为妻的男人,现在这个男人美梦成真了。提利昂和莱安娜并肩走向铁王座,提利昂警告莱安娜权力的游戏不是小孩子玩的把戏,涉身其中总要付出代价。当然,提利昂向莱安娜保证她将永远不会在权力的游戏中孤军奋战,因为自己会永远在她身边。最后,莱安娜和提利昂一起凝望着铁王座,目光久久不移。结束的镜头将我们拉往极北的永冬之地,一个异鬼骑马而过带着布兰·史塔克的尸体前往祭坛。异鬼按照特定的仪式将一片龙晶刺入布兰的胸膛,布兰睁开了眼睛,双眼闪着幽蓝的光芒。异鬼将一顶王冠戴在布兰头上,祭坛渐渐被冰霜覆盖。凛冬将至……


这个剧透是知乎上翻译的黑客泄露剧本,也就是跟泄露的第七季剧透是一样的出处,相信看过第七季剧透的都知道准到什么程度吧。

现在的悬念就是本来第八季的后几集剧本也不是最终定稿,再加上泄露,可能会有怎样的调整(但结局走向应该是跟作者靠好了的)。

如果维持这个结局的话真tm的太虐了!!!!

原文地址
C#开发者都知道,在Winform开发中,SendKeys类提供的方法是很实用的。但是可惜的是,在WPF中不能使用这个方法了。
我们知道,在WPF中非UI线程刷新UI线程,需要使用Dispatcher.Invoke((Action)delegate { /* Your
code is put here */ });
方法。这里调用System.Windows.Forms.SendKeys.Send()方法会报错。

下面这个代码文件做了一个很好的包装,可以下载后参考:
Simulation.zip

如何使用呢?
很简单, 要敲一个键, 比如回车:
Keyboard.Press(Key.Enter);
Keyboard.Release(Key.Enter);

要敲一个组合键:比如Alt+F4:
Keyboard.Press(Key.LeftAlt);
Keyboard.Press(Key.F4);
Keyboard.Release(Key.LeftAlt);
Keyboard.Release(Key.F4);

要敲一段文字:
Keyboard.Type(“notepad”);

鼠标与之类似,比如:
Mouse.MoveTo(new System.Drawing.Point(x, y));
Mouse.Click(MouseButton.Right);

Simulation类的定义如下:

//定义是这样的:

[DllImport("user32.dll", SetLastError = true)]
internal static extern int SendInput(int nInputs, ref INPUT mi, int cbSize);

//其中的INPUT结构表示一个键盘或鼠标操作:

[StructLayout(LayoutKind.Sequential)]
internal struct INPUT
{
internal int type;
internal INPUTUNION union;
};
[StructLayout(LayoutKind.Explicit)]
internal struct INPUTUNION
{
[FieldOffset(0)]
internal MOUSEINPUT mouseInput;
[FieldOffset(0)]
internal KEYBDINPUT keyboardInput;
};
[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEINPUT
{
internal int dx;
internal int dy;
internal int mouseData;
internal int dwFlags;
internal int time;
internal IntPtr dwExtraInfo;
};
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBDINPUT
{
internal short wVk;
internal short wScan;
internal int dwFlags;
internal int time;
internal IntPtr dwExtraInfo;
};
[Flags]
internal enum SendMouseInputFlags
{
Move = 0x0001,
LeftDown = 0x0002,
LeftUp = 0x0004,
RightDown = 0x0008,
RightUp = 0x0010,
MiddleDown = 0x0020,
MiddleUp = 0x0040,
XDown = 0x0080,
XUp = 0x0100,
Wheel = 0x0800,
Absolute = 0x8000,
};

<Style x:Key=”{x:Type DocumentViewer}” TargetType=”{x:Type DocumentViewer}”>

<Setter Property=”Foreground” Value=”{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}” />

<Setter Property=”Background” Value=”{DynamicResource {x:Static SystemColors.ControlBrushKey}}” />

<Setter Property=”FocusVisualStyle” Value=”{x:Null}” />

<Setter Property=”Template”>

<Setter.Value>

<ControlTemplate TargetType=”{x:Type DocumentViewer}”>

<Border BorderThickness=”{TemplateBinding BorderThickness}” BorderBrush=”{TemplateBinding BorderBrush}” Focusable=”False”>

<Grid KeyboardNavigation.TabNavigation=”Local”>

<Grid.Background>

<SolidColorBrush Color=”{DynamicResource ControlLightColor}” />

</Grid.Background>

<Grid.RowDefinitions>

<RowDefinition Height=”Auto” />

<RowDefinition Height=”*” />

<RowDefinition Height=”Auto” />

</Grid.RowDefinitions>

<ScrollViewer Grid.Row=”1″ CanContentScroll=”true” HorizontalScrollBarVisibility=”Auto” x:Name=”PART_ContentHost” IsTabStop=”true”>

<ScrollViewer.Background>

<LinearGradientBrush EndPoint=”0.5,1″ StartPoint=”0.5,0″>

<GradientStop Color=”{DynamicResource ControlLightColor}” Offset=”0″ />

<GradientStop Color=”{DynamicResource ControlMediumColor}” Offset=”1″ />

</LinearGradientBrush>

</ScrollViewer.Background>

</ScrollViewer>

</Grid>

</Border>

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

一、写入注册表

1. 打开project assistant –> Project Registry

可以像注册表里一样操作,其中[INSTALLDIR]是指的安装路径

 

二、 运行bat文件

网上很多介绍如何运行bat的方法,但我这个是limted 版本,不适用。

1. 打开 Define Setup Requirements and Actions –> Custom Actions

2. 右健 After Register Product –> New Exe

如何执行批处理

 

我们可以在事件中使用方法 来执行  installshield提供了该方法LaunchAppAndWait 来调用应用程序

 

 

//执行卸载脚本
function ExecuteUnstall()
string cmdline;
begin
if(AskYesNo(“您确定要卸载吗”,YES)=NO)  then
abort;
endif;
cmdline=”cmd /c /””+TARGETDIR+”//tools//uninstall.bat/””;
if (LaunchAppAndWait (“”,cmdline, WAIT) < 0) then
MessageBox (“Unable to launch cmd “^cmdline^”.”,SEVERE);
endif;

end;

 

 

设计环境变量  环境变量 可以从注册表中设置

用户变量的位置是 :HKEY_CURRENT_USER/Environment

系统变量的位置是:HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager/Environment

 

function SetEnvironment()
begin
RegDBSetDefaultRoot (HKEY_CURRENT_USER);
RegDBSetKeyValueEx (“Environment” , “JAVA_HOME” , REGDB_STRING_EXPAND , TARGETDIR+”//Java” , -1);
RegDBSetKeyValueEx (“Environment” , “CATALINA_HOME” , REGDB_STRING_EXPAND ,TARGETDIR+”//tomcat” , -1);
RegDBSetKeyValueEx (“Environment” ,”JRE_HOME” , REGDB_STRING_EXPAND ,TARGETDIR+”//Java//jre1.5.0_18″ , -1);

//这个地方nzType千万不要直接用 REGDB_STRING    否则会编译出错 日
//if (RegDBGetKeyValueEx (szKey, “Path”, nzType, svOld, nsize) < 0) then
//        MessageBox (“RegDBGetKeyValueEx failed.”, SEVERE);
//        abort;
//endif;
// svNew=svOld+”;”+TARGETDIR+”//Java//bin”;
//RegDBSetKeyValueEx (“Environment” , “Path” , REGDB_STRING ,svNew , -1);

end;

Microsoft.NET的应用程序的代码文件,与Java生成的文件类似,它们都没有本地代码,而是一种类似于汇编的代码。这样,只要有合适的工具,就可以完整的把别人写出来的程序反编译成自己需要的程序文件。

我所知道的.Net下的反汇编程序是Salamander 和 Refelector两个工具,他们都可以对.Net的程序集反编译成你需要的语言。

那么,我们写的程序,做的项目,如何进行正版的许可证管理,有许多方法。

最好的方案,是几个方法的综合。下面我说一下单独的许可验证方法。

最简单的方法,就是使用许可存储。方法是用户输入正版的注册码,通过程序中专门的算法程序进行验算,得出的结果与事先保存在程序中的结果比对,比对一致表示输入正确。然后把结果保存在存储中,如注册表或者专门的许可文件中,程序许可通过。

这个方法使用的人/公司最多,但是缺点也是最多的,只要使用上面的工具把验算注册码的算法给弄清楚,就可以自己写一个生成序列号的注册机,这个注册方法就形同虚设了。

还有一个比较好的方法,就是仿照WindowsXP的激活机制,客户的程序自动访问互联网的一个专门设定的服务器,通过Tcp/Ip或者WebService远程访问服务器上的许可程序,许可后把结果保存在客户端计算机上。这个方法的好处是许可验证代码保存在开发者控制的计算机上,客户端无法获取验证算法,而且可以通过数据库管理用户,非常方便。

但是这个方法也有缺点,首先是可靠的Internet连接。如果要防止用户使用盗版,则必须在客户端的程序中添加一个随机访问远程许可服务器验证的功能,这样不但需要一个24小时的Internet连接,而且经常进行验证也会干扰程序的正常运行。还有就是如果有人通过研究客户端的接收返回信息的代码,弄一个虚拟的验证服务器,这个功能也会完蛋。

那么,所有的焦点都聚集在客户端的验证算法上,只要这个客户端的验证算法被人弄清楚了,整个程序的许可可以说就不存在了,所以许多开发者/开发公司费好大的力气,弄一个足够复杂的验证算法出来,用算法的复杂度来抵抗破解。但是再复杂的算法,只要有人写得出来,就有人能破解得出来,这个道理我想大家都明白。

那是否有加密算法与解密算法不同的办法呢?有。而且.Net自带的类库里面就有这个算法。
这个算法的原理是不对称加密的原理。不对称加密原理大家基本上都了解。加密的密码(密钥)分为两个部分,公钥和私钥。通过私钥加密的密文只能通过公钥解密。根据这个特性,我们可以发现只要开发者保存好私钥,即使算法代码被客户端破解,因客户端不知道保存在开发者处的私钥,也无法生成注册码。

这个算法就是 System.Security.Cryptography 名称空间的RSAPKCS1SignatureFormatter 类(用来生成注册码)和 RSAPKCS1SignatureDeformatter类(用来在客户端验证注册码)。验证过程如下:
首先,需要生成一个公钥和私钥对,当然,依靠人是无法生成的,我们可以通过 System.Security.Cryptography名称空间的RSACryptoServiceProvider 类来生成公钥/私钥对。

using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
// 公钥
stringpubkey =rsa.ToXmlString(false);
// 私钥
stringprikey =rsa.ToXmlString(true);
}

获取私钥以后,可以用 RSAPKCS1SignatureFormatter 类来生成注册码,代码如下(引用名称空间略)

using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(prikey);;
//加密对象
RSAPKCS1SignatureFormatter f = new RSAPKCS1SignatureFormatter(rsa);
  f.SetHashAlgorithm("SHA1");
byte[] source =System.Text.ASCIIEncoding.ASCII.GetBytes(txtIn.Text);
SHA1Managed sha= new SHA1Managed();
byte[] result =sha.ComputeHash(source);
byte[] b =f.CreateSignature(result);
11 msg.Text =Convert.ToBase64String(b);
}

上面的代码是一个示例aspx页面的代码,页面包括一个id为msg的Label控件,一个ID为txtIn的TextBox控件,一个ID为btnOK的Button控件,上面的代码就是btnOK的事件处理程序的内容。大家可以非常清楚的看出处理流程,生成一个RsaCryptoServiceProvider类实例,然后把这个类实例的加密密钥指定为包含私钥的prikey字符串因为加密解密的公钥/私钥必须是对应的。然后获取txtIn输入的内容,生成密钥后在msg控件上显示。

下面是使用 RSAPKCS1SignatureDeformatter 类来验证输入: 

using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
  rsa.FromXmlString(pubkey);
  RSAPKCS1SignatureDeformatter f = new RSAPKCS1SignatureDeformatter(rsa);
  f.SetHashAlgorithm("SHA1");
  byte[] key =Convert.FromBase64String(txtKey.Text);
   SHA1Managed sha= new SHA1Managed();
  byte[] name =sha.ComputeHash(ASCIIEncoding.ASCII.GetBytes(txtIn.Text));
  if(f.VerifySignature(name,key))
   msg.Text ="验证成功";
  else
   msg.Text ="不成功";
}

上面的代码也很好理解,就是多了一个ID为txtKey的TextBox控件,他通过同时获取用户名/加密密钥来进行验证。重点是RSA类的FromXmlString()方法,注意上面的这个方法获取的是公钥,表示这段验证代码是保存在客户端的,客户端代码是没有私钥的,即使有人把程序集的代码反编译了也没有用。

上面两段代码需要注意的就是生成的公钥/私钥必须匹配,我使用RSA对象生成密钥对后保存成为字符串常量,就可以解决这个问题。

上面这个方法仍然无法解决客户使用ildasm反编译后暴力修改IL代码,只有靠可靠的强名称以及数字证书来保证程序集不被修改了。

    • 问题如下:

      我通过WPF进行打印,WPF中PrintDialog默认纸张大小应该是A4,宽度是700多左右,高度是1000多。当我想要在X轴1000的位置打印文字时,总是打印不出来。因此我想通过设置打印纸张的大小来让X轴1000处的文字打印出来。代码如下。设置PageMediaSize为 ISOA0,应该是3000*4000多。但是我还是无法将X轴1000处的文字打印出来。网上搜了很久无果。因此在这里提问。希望能解决这个问题。代码如下。

      private void button_SinglePointPrint_Click(object sender, RoutedEventArgs e)
      {
      PrintDialog pDialog = new PrintDialog();
      pDialog.PrintTicket = pt;
      pDialog.PrintQueue = pq;
      pDialog.PrintTicket.PageMediaSize = new PageMediaSize(PageMediaSizeName.ISOA0);
      if (pDialog.ShowDialog() == true)
      {
      pt = pDialog.PrintTicket;
      pq = pDialog.PrintQueue;

      DrawingVisual vis = new DrawingVisual();
      DrawingContext dc = vis.RenderOpen();

      dc.DrawText(pLib.GetFormattedText(textBox_SinglePointText.Text, TextAlignment.Center, new Typeface(fontName), fontSize), new Point(1000,100));
      dc.Close();

      pDialog.PrintVisual(vis, “TestSinglePoint”);
      }
      }

      2014年4月21日 15:38
      kouxuelong 的头像

      0 分数

答案

  • 你好,

    你可以通过设置PrintTicket.PageMediaSizeSize,然后Transform你的窗体大小。请查看下面代码:

     private void _print()
     {
          PrintDialog printDlg = new System.Windows.Controls.PrintDialog();
    
           PrintTicket pt = printDlg.PrintTicket;
           Double printableWidth = pt.PageMediaSize.Width.Value;
           Double printableHeight = pt.PageMediaSize.Height.Value;
    
           Double xScale = (printableWidth - xMargin * 2) / printableWidth;
           Double yScale = (printableHeight - yMargin * 2) / printableHeight;
    
           this.Transform = new MatrixTransform(xScale, 0, 0, yScale, xMargin, yMargin);
    
        //now print the visual to printer to fit on the one page.
         printDlg.PrintVisual(this, "Print Page");
     }

    谢谢!