C#实现软件注册码算法

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 PrintDialog 设置打印纸张高度与宽度问题。

    • 问题如下:

      我通过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");
     }

    谢谢!

WPF的DataGrid单元格回车往右拐的方法

最后一个可编辑的单元格按下回车时增加一行个人感觉太繁琐了点  最好是直接给Grid加一行 而不是给对应的集合加一行 寻求更好的解决方案

/// <summary>
/// Grid最后一列加行和往右拐
/// </summary>
/// <typeparam name=”Tresult”></typeparam>
/// <param name=”dataGrid”></param>
/// <param name=”sender”></param>
/// <param name=”e”></param>
/// <param name=”li”></param>
public static void Grid_Right<Tresult>(this DataGrid dataGrid, object sender, KeyEventArgs e) where Tresult : class, new()
{
var uie = e.OriginalSource as UIElement;
if (e.Key == Key.Enter)
{
e.Handled = true;
uie.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
dataGrid.BeginEdit();
if (dataGrid.CurrentColumn != null)
{
if (dataGrid.CurrentColumn.IsReadOnly == true)
{

for (int b = dataGrid.CurrentColumn.DisplayIndex; b < dataGrid.Columns.Count; b++)
{
for (int k = 0; k < dataGrid.Columns.Count; k++)
{
if (dataGrid.Columns[k].DisplayIndex == b)
{
if (dataGrid.Columns[k].IsReadOnly == false && dataGrid.Columns[k].Visibility == Visibility.Visible)
{
dataGrid.CurrentColumn = dataGrid.Columns[k];
dataGrid.SelectedItem = dataGrid.CurrentItem;
dataGrid.BeginEdit();
return;
}
else if (k == dataGrid.Columns.Count – 1 && (dataGrid.Columns[k].IsReadOnly == true || dataGrid.Columns[k].Visibility == Visibility.Hidden))
{
if (dataGrid.SelectedIndex != dataGrid.Items.Count – 1)
{
dataGrid.SelectedIndex = dataGrid.SelectedIndex + 1;
dataGrid.CurrentColumn = dataGrid.Columns[0];
dataGrid.CurrentItem = dataGrid.SelectedItem;
b = 0;
}
else
{
//PropertyInfo p = dataGrid.CurrentItem.GetType();
ModelObservableCollection<Tresult> li = dataGrid.ItemsSource as ModelObservableCollection<Tresult>;
li.Insert(dataGrid.SelectedIndex + 1, new Tresult());
dataGrid.SelectedIndex = dataGrid.SelectedIndex + 1;
dataGrid.CurrentColumn = dataGrid.Columns[0];
dataGrid.CurrentItem = dataGrid.SelectedItem;
b = 0;
}

}
}
}

}
}
}
}
}

WPF小知识,MessageBox的多种用法

我们在程序中经常会用到MessageBox。

现将其常见用法总结如下:

1.MessageBox.Show(“Hello~~~~”);

最简单的,只显示提示信息。

2.MessageBox.Show(“There are something wrong!”,”ERROR”);

可以给消息框加上标题。

3.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel) == DialogResult.OK)

{

//delete

}

询问是否删除时会用到这个。

4.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel,MessageBoxIcon.Question) == DialogResult.OK)

{

//delete

}

可以给MessageBox加上一个Icon,.net提供常见的Icon共选择。

5.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question,MessageBoxDefaultButton.Button2) == DialogResult.OK)

{

//delete

}

可以改变MessageBox的默认焦点,如下:

6.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question,MessageBoxDefaultButton.Button2,MessageBoxOptions.RtlReading) == DialogResult.OK)

{

//delete

}

反向显示:

7.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2, MessageBoxOptions.RightAlign,true) == DialogResult.OK)

{

//delete

}

添加Help按钮:

8.if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1, MessageBoxOptions.RtlReading, @”/folder/file.htm”) == DialogResult.OK)

{

//delete

}

指定帮助文件的路径,点击即可打开该路径下的帮助文件。

9.//HelpNavigator指定常数来指示要显示的帮助文件元素。Find 帮助文件将打开到搜索页。

if (MessageBox.Show(“Delete this user?”, “Confirm Message”, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1, MessageBoxOptions.RtlReading, @”/folder/file.htm”, HelpNavigator.Find) == DialogResult.OK)

{

//delete

}

还有一些用法,不是太实用这里就不一一介绍了,有兴趣的朋友可以参考下这里:MSDN的MessageBox类。

========================================================================
【函数】 <整型> MessageBox(<字符串> Text, <字符串> Title, <整型> nType,MessageBoxIcon);
【函数说明】 弹出一个消息框。
【语法】
参数:
Text <字符串>,消息框的正文;
Title <字符串>,消息框的标题;
nType <整型>,消息框的类型。
返回值:<整型>,用户在消息框上点击关闭时的选择的按钮。 MessageBoxIcon:对话框上显示的图标样式。

【说明】
MessageBox(“消息内容”, “返回值 确定1”,MessageBoxButtons.OK,MessageBoxIcon.Question);
MessageBox(“消息内容”,, “返回值 确定1 取消2”,MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk);
MessageBox(“消息内容”, “返回值 终止3 重试4 忽略5”,MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error);
MessageBox(“消息内容”, “返回值 是6 否7 取消2”,MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation);
MessageBox(“消息内容”, “返回值 是6 否7”,MessageBoxButtons.YesNo, MessageBoxIcon.Hand);
MessageBox(“消息内容”, “返回值 重试4 取消2”,MessageBoxButtons.RetryCancel, MessageBoxIcon.Information);

MessageBoxIcon: 所有图标样式

MessageBoxIcon.Question MessageBoxIcon.Asterisk MessageBoxIcon.Information MessageBoxIcon.Error MessageBoxIcon.Stop MessageBoxIcon.Hand MessageBoxIcon.Exclamation MessageBoxIcon.Warning MessageBoxIcon.None

MessageBox函数MessageBox()函数MessageBox是标准的windows Api函数只能在CWnd类的继承类中使用,在C#中使用时,通常用MessageBox的show方法来实现对话框的弹出,命名空间System.Windows.Forms

应用实例:

DialogResult 是枚举类可以用枚举值直接比较MessageBox的返回值也可以转换为整型后再比较。如下:DialogResult r1 = MessageBox.Show ( “是否确定?” , “垃圾处理!” , MessageBoxButtons.AbortRetryIgnore , MessageBoxIcon.Question ) ;
int ss1=(int)r1 ;
if ( ss1==3 ){ }
if ( ss1==4 ){ }
if ( ss1==5){ }

或者是

if (DialogResult.Yes == MessageBox.Show(“232”, “”, MessageBoxButtons.YesNo, MessageBoxIcon.Information,MessageBoxDefaultButton.Button1))
{
MessageBox.Show(“122”);
}

WPF有一个 button, button 的背景是一张图片, 当我运行程序后, 点击button的时候, button会有按下的效果,但同时 背景图片也消失了, 等松开鼠标的时候,背景图片又回来了,

回复次数:4

WPF自定义控件与样式(1) – 图标字体

一、图标字体

阿里巴巴开源字体网址:http://www.iconfont.cn/

使用方法:

       1、新浪微博账号登录

       2、点击图标 -> “添加入库”

       3、库->下载代码

界面如下:

      

4、下载的代码中的 *.ttf 文件,在WPF中用

文件如下:

 

二、WPF 中使用图标字体

1、iconfont.ttf 添加到Resources文件夹

iconfont.ttf属性如下:

2、XAML代码

<Window x:Class=”WpfApplication2.MainWindow”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”MainWindow” Height=”350″ Width=”525″>
<Window.Resources>
<Style x:Key=”FIcon” TargetType=”TextBlock”>
<Setter Property=”FontFamily” Value=”/程序集名称;component/Resources/#iconfont”></Setter>
<Setter Property=”Foreground” Value=”Green”></Setter>
<Setter Property=”TextAlignment” Value=”Center”/>
<Setter Property=”HorizontalAlignment” Value=”Center”/>
<Setter Property=”VerticalAlignment” Value=”Center”/>
<Setter Property=”FontSize” Value=”20″/>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text=”&#xe607;” Style=”{StaticResource FIcon}” FontSize=”30″ Margin=”3″ ></TextBlock>
<TextBlock Text=”&#xe612;” Style=”{StaticResource FIcon}” FontSize=”30″ Margin=”3″ ></TextBlock>
</StackPanel>
</Window>

效果:

异常处理之ThreadException、unhandledException及多线程异常处理

一:ThreadException和unhandledException的区别

处理未捕获的异常是每个应用程序起码有的功能,C#在AppDomain提供了UnhandledException 事件来接收未捕获到的异常的通知。常见的应用如下:

复制代码

代码

staticvoid Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException +=new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}

staticvoid CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception error = (Exception)e.ExceptionObject;
Console.WriteLine(“MyHandler caught : “+ error.Message);
}

复制代码

未捕获的异常,通常就是运行时期的BUG,于是我们可以在UnhandledException 的注册事件方法CurrentDomain_UnhandledException中将未捕获异常的信息记录在日志中。值得注意的是,UnhandledException提供的机制并不能阻止应用程序终止,也就是说,CurrentDomain_UnhandledException方法执行后,应用程序就会被终止。

上面我们举的例子来自于控制台程序,UnhandledException可以在任何应用程序域中使用,在某些应用程序模型,如windows窗体程序,还存在ThreadException来处理 Windows 窗体线程中所发生的其未经处理的异常。即,在windows窗体程序中,使用 ThreadException 事件来处理 UI 线程异常,使用 UnhandledException 事件来处理非 UI 线程异常。ThreadException可以阻止应用程序终止。具体使用方法如下:

复制代码

代码

[STAThread]
staticvoid Main()
{
Application.ThreadException +=new ThreadExceptionEventHandler(UIThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(new ErrorHandlerForm());
}

privatestaticvoid UIThreadException(object sender, ThreadExceptionEventArgs t)
{
try
{
string errorMsg =”Windows窗体线程异常 : \n\n”;
MessageBox.Show(errorMsg + t.Exception.Message + Environment.NewLine + t.Exception.StackTrace);
}
catch
{
MessageBox.Show(“不可恢复的Windows窗体异常,应用程序将退出!”);
}
}

privatestaticvoid CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
try
{
Exception ex = (Exception)e.ExceptionObject;
string errorMsg =”非窗体线程异常 : \n\n”;
MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace);
}
catch
{
MessageBox.Show(“不可恢复的非Windows窗体线程异常,应用程序将退出!”);
}
}

复制代码

除了Windows窗体程序,再来说一下WPF程序。WPF的UI线程和Windows的UI线程有点不一样。WPF的UI线程是交给一个叫做调度器的类:Dispatcher。代码如下:

复制代码

代码

public App()
{
this.DispatcherUnhandledException +=new DispatcherUnhandledExceptionEventHandler(Application_DispatcherUnhandledException);
AppDomain.CurrentDomain.UnhandledException +=new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}

void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
try
{
Exception ex = e.ExceptionObject as Exception;
string errorMsg =”非WPF窗体线程异常 : \n\n”;
MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace);
}
catch
{
MessageBox.Show(“不可恢复的WPF窗体线程异常,应用程序将退出!”);
}
}

privatevoid Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
try
{
Exception ex = e.Exception;
string errorMsg =”WPF窗体线程异常 : \n\n”;
MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace);
}
catch
{
MessageBox.Show(“不可恢复的WPF窗体线程异常,应用程序将退出!”);
}
}

复制代码

无论是Windows窗体程序还是WPF程序,我们都看到捕获的异常当中分为”窗体线程异常”和”非窗体线程异常”。如在Windows窗体程序中,如果在窗体线程中,

thrownew Exception(“窗体线程异常”);

将会触发ThreadException事件。

Thread t =new Thread((ThreadStart)delegate
{
thrownew Exception(“非窗体线程异常”);
});
t.Start();

将会触发UnhandledException事件,然后整个应用程序会被终止。

 

二:多线程异常处理

 

多线程的异常处理,要采用特殊的做法。以下的处理方式会存在问题:

复制代码

代码

try
{
Thread t =new Thread((ThreadStart)delegate
{
thrownew Exception(“多线程异常”);
});
t.Start();
}
catch (Exception error)
{
MessageBox.Show(error.Message + Environment.NewLine + error.StackTrace);
}
复制代码

应用程序并不会在这里捕获线程t中的异常,而是会直接退出。从.NET2.0开始,任何线程上未处理的异常,都会导致应用程序的退出(先会触发AppDomain的UnhandledException)。上面代码中的try-catch实际上捕获的还是当前线程的异常,而t是属于新起的异常,所以,正确的做法应该是:

复制代码

代码

Thread t =new Thread((ThreadStart)delegate
{
try
{
thrownew Exception(“多线程异常”);
}
catch (Exception error)
{
MessageBox.Show(“工作线程异常:”+ error.Message + Environment.NewLine + error.StackTrace);
}
});
t.Start();
复制代码

也就是说,新起的线程中异常的捕获,可以将线程内部代码全部try起来。原则上来说,每个线程自己的异常应该在自己的内部处理完毕,不过仍旧有一个办法,可以将线程内部的异常传递到主线程。

在Windows窗体程序中,可以使用窗体的BeginInvoke方法来将异常传递给主窗体线程:

复制代码

代码

Thread t =new Thread((ThreadStart)delegate
{
try
{
thrownew Exception(“非窗体线程异常”);
}
catch (Exception ex)
{
this.BeginInvoke((Action)delegate
{
throw ex;
});
}
});
t.Start();
复制代码

上文的代码将最终引发主线程的Application.ThreadException。最终的结果看起来有点像:

 

在WPF窗体程序中,你可以采用如下的方法将工作线程的异常传递到主线程:

代码

Thread t =new Thread((ThreadStart)delegate
{
try
{
thrownew Exception(“非窗体线程异常”);
}
catch (Exception ex)
{
this.Dispatcher.Invoke((Action)delegate
{
throw ex;
});
}
});
t.Start();

WPF窗体程序的处理方式与Windows窗体程序比较,有两个很有意思的地方:

第一个是,在Windows窗体中,我们采用的是BeginInvoke方法。你会发现使用Invoke方法,并不能引发主线程的Application.ThreadException。而在WPF窗体程序中,无论是调度器的Invoke还是BeginInvoke方法都能将异常传递给主线程。

第二个地方就是InnerException。WPF的工作线程异常将会抛到主线程,变成主线程异常的InnerException,而Windows窗体程序的工作线程异常,将会被吃掉,直接变为null,只是在异常的Message信息中保存工作线程异常的Message。

 

三:ASP.NET异常处理

我们都知道ASP.NET的全局异常处理方法是Global中的Application_Error方法。我曾经查过ASP.NET的Appdomain.CurrentDomain.unhandledException,结果用反射得到的结果,unhandledException所注册的事件方法根本不是这个方法。联想到ASP.NET页面,包括这个全局处理类,都是交给aspnet_isapi.dll处理的,而aspnet_isapi.dll不是一个托管程序集。所以,应该理解为,ASP.NET的未捕获异常的处理,不同于托管异常(即CLR异常),而是交给aspnet_isapi.dll这个非托管DLL处理的。

mysql连接字符串—远程连接用IP地址 而非只是localhost时

r on ‘192.168.1.101’ (10061)

最新的可解决方法如下:(最重要的步骤–>红色字体标识出了)

解决MYSQL数据库无法使用IP访问本地的方法
MYSQL数据库缺省安装后,其默认用户名ROOT如果只能以<localhost>或<127.0.0.1>方式访问主机,即通过外部IP地址访问返回如下内容:
ERROR 1130 (): #HY000Host ‘XXXXXX’ is not allowed to connect to this MySQL server
可以增加一个用户,给其权限,允许由局域网或互联网进行外部访问,方法如下:
1。在运行中输入CMD,确定,进入文本方式。
2。输入mysql -h localhost -u root -p 回车,使用ROOT用户登录。
3。输入use mysql; 显示Database changed,选择MYSQL系统库。
4。假定我们现在增加一个’goldeye2000’用户,密码为’1234567’,让其能够从外部访问MYSQL。输入
grant all on * to ‘goldeye2000’ identified by ‘1234567’;
ALL代表所有权限。
5。现在看看用户表内容。输入select user,host from user ; 可以看到”goldeye2000″用户已经加进去了,并且其权限为’% ‘,’grande’,’localhost ‘。
6。退出MYSQL,输入QUIT;回车
7。我们现在可以用goldeye2000用户在局域网或互联网中以IP方式访问了。
mysql -h 192.168.0.115 -u goldeye2000 -p

二、bind-address = 127.0.0.1 注释掉即可

InstallShield 2010集成.net Framework 4的安装包制作

InstallShield 2010中制作安装包时,对于集成.net Framework 4以前的版本,如3.5 sp1/3.5/3.0/2.0 sp2/2.0sp1/2.0等提供了现成的prq文件模板,可以直接使用。也可以参考前一篇文章的方法自己下载并修改。

http://www.cnblogs.com/downmoon/archive/2010/02/27/1674634.html 

前天下了最新的vs2010,一些程序升级到 .net Framework 4,在制作安装包时可以用vs自带的打包程序,也可以方便的制作。问题是以前制作的installshield模板弃之不用,有些可惜了,但Installshileld 2010下载的最新版sp1 with hotifx 52410并不包含对.net Framework 4对应的prq文件,于是照着前篇文章的方法制作了一个,文件名为Microsoft .NET Framework 4.0.Full.prq,内容如下:

复制代码
<?xml version=”1.0″ encoding=”UTF-8″?>
<SetupPrereq>
<conditions>
<condition Type=”2″ Comparison=”32″ Path=”HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full” FileName=”Install” ReturnValue=”1″></condition>
</conditions>
<operatingsystemconditions>
<operatingsystemcondition MajorVersion=”5″ MinorVersion=”1″ PlatformId=”2″ CSDVersion=”” Bits=”1″ ProductType=”1″ ServicePackMajorMin=”2″></operatingsystemcondition>
<operatingsystemcondition MajorVersion=”5″ MinorVersion=”2″ PlatformId=”2″ CSDVersion=”” Bits=”1″ ProductType=”2|3″ ServicePackMajorMin=”1″></operatingsystemcondition>
</operatingsystemconditions>
<files>
<file LocalFile=”&lt;ISProductFolder&gt;\SetupPrerequisites\Microsoft .net\4.0\dotNetFx40_Full_x86_x64.exe” URL=”http://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/dotNetFx40_Full_x86_x64.exe” CheckSum=”251743DFD3FDA414570524BAC9E55381″ FileSize=”0,50449456″></file>
</files>
<execute file=”dotNetFx40_Full_x86_x64.exe” cmdline=”/q /norestart” cmdlinesilent=”/q /norestart” returncodetoreboot=”1641,3010″ requiresmsiengine=”1″></execute>
<properties Id=”{0a391abd-25c1-4fc0-919f-b21f31ab88b7}” Description=”This prerequisite installs the .net 4.0 framework for 32-bit (x86) systems.” AltPrqURL=”http://saturn.installshield.com/is/prerequisites/microsoft .net framework 4.0.prq”></properties>
</SetupPrereq>
复制代码

说明:

1、注册表中的检测位置:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full

2、文件的直接下载地址:http://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/dotNetFx40_Full_x86_x64.exe

3、exe文件位置:&lt;ISProductFolder&gt;\SetupPrerequisites\Microsoft .net\4.0\dotNetFx40_Full_x86_x64.exe

4、MD5值:251743DFD3FDA414570524BAC9E55381(可以通过对该安装文件查询得知)

5、产品在安装系统中的GUID:(0a391abd-25c1-4fc0-919f-b21f31ab88b7)这个在微软的官方网站下载页面的地址栏可以得知,我随便生成了一个GUID,只要保证在安装系统中不重复就可以了。

另外,.net Framework 4 和.net Framework 3.5类似,都采取可以完全部署和Client Profile两种方式。大小差不多,40多M左右。建议采用完全部署。

具体步骤:

第一步、如果不想在线下载.net Framework 4 ,可以用vs2010先做一个简单的部署安装程序, 按照是上图选项,则会在可执行程序的对应路径下生成一个dotNetFx40_Full_x86_x64.exe,当然也可以直接下载

http://download.microsoft.com/download/9/5/A/95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE/dotNetFx40_Full_x86_x64.exe 

然后将这个文件复制到installshield2010的安装路径下\SetupPrerequisites\Microsoft .net\4.0\,如下图:

第二步:在installshield中选取Framework 4 即可。然后就可以生成包含Framework 4 的安装包了!

InstallShield 2011 自定义语言选择框

在使用InstallShield 2011制作安装包的过程中遇到了两个BT问题。第一个问题是InstallShield 2011的语言框的默认值不再匹配”Region and Language Settings”中设置的”Current Language for non-Unicode”。而InstallShield 12是可以的。第二个问题是如果系统没有安装用户所选择的语言会弹出一个错误框,点确定后安装终止。而InstallShield 12在这种情况下可以正常安装,不过界面上的语言显示为乱码。

我们之前的安装包是用InstallShield 12制作的。现在项目的Dot Net Framework升级到了3.5 SP1,所以得使用支持3.5的InstallShield 2011. 不幸的是客户希望我们的安装包的行为还是和以前一样。

看了N篇帖子后无果。发邮件给InstallShield的公司,隔了两个礼拜才回信,信的大致意思是2011就是这样的。。。(PS.问他们问题是要收费的。。。)幸亏我早就找到了解决方法。

关于第一个问题,我们可以自己写一个语言选择框,然后读取系统的Locale语言,设置为默认值。Install Shield的语言选择框是Setup.exe提供的。可以注意一下安装过程,先弹出语言选择框,然后才是解压msi。而我们的代码是打包在msi里面的。所以我们得新建另一个setup.exe,将自定义语言选择框放置在里面,当用户选择了语言后,首先判断系统是否安装了这个语言,如果没有就又弹出语言框让用户选,这就解决了第二个问题。然后就再调用第二个Setup.exe,当然要带上命令行参数 “-l0x0809″。”-l”是命令,后面加上的是用户选择的语言的16位编码。我们的产品有9种语言,为了精简示例代码,我只保留了英语和中文。如果你希望查找语言代码可以打开下面的链接。

http://msdn.microsoft.com/en-us/library/ms776294(VS.85).aspx

首先理清这两个安装包。第一个安装包只需要一种语言,我们把自定义的语言选择框放在里面。第二个安装包才是我们真正用来安装的,它是多语言的。

下面一步步来建立我们的语言框。

首先在”Dialogs”界面点击右键,选择”New Dialog”。会出现一个Wizard,在Dialog Template界面选择类型”NewScriptBasedDialog”。(千万不要选”Blank Dialog”。这个类型做出来的对话框有个Bug,就是点击窗口上的关闭按钮(右上角那个红叉叉)没有反应。估计是InstallShield的开发人员忘了给它加事件了。)

将Dialog改名为SelectLanguage。把界面上已有的控件通通删掉,然后加上自己的控件。我是照着InstallShield的语言框做的,因为客户要求一模一样。

只需要一个空界面。多语言是在后台代码里实现的。

在InstallScript界面新增文件SelectLanguage.rul。下面是代码

 

 

复制代码
prototype number SdSelectLanguage();
prototype InitDialog(STRING);
#define RES_DIALOG_ID     22011   // “Control identifier” of custom dialog 这是控件的ID,可以到Dialog里自己设置
#define RES_CMB         1301   //   “Control identifier” of combobox
#define RES_SAVE      1302   //  “Control identifier” of “OK” Button
#define RES_CANCEL    1303   //  “Control identifier” of “Cancel” Button
#define RES_Desc      1304   // “Control identifier” of Title
string selectedText,tempStr;
NUMBER languageIndex;
LIST listDrives;

function number SdSelectLanguage()
STRING szDialogName, svName;
NUMBER nResult, nCmdValue;
BOOL    bDone;
HWND    hwndDlg;
number nSize;
string svDrive,languageIndexStr,firstString;
NUMBER localeLanguageID,i;

begin
szDialogName = “SelectLanguage”;
nResult = EzDefineDialog (szDialogName, “”, “”, RES_DIALOG_ID);
if (nResult < 0) then
abort;
endif;
bDone = FALSE;
// Loop until done.
repeat
// Display the dialog and return the next dialog event.
nCmdValue = WaitOnDialog (szDialogName);
switch (nCmdValue)
case 2: //返回值为2 表示点击了关闭按钮
// The user clicked the window’s Close button.
Do(EXIT);
case DLG_ERR:
abort;
case DLG_INIT:
InitDialog(szDialogName);
case RES_CANCEL:
Do (EXIT);
case RES_SAVE:
CtrlGetCurSel(szDialogName, RES_CMB,selectedText); //获取下拉列表的值
ListGetFirstString(listDrives,firstString); //将listDrives的索引置0
ListFindString(listDrives,selectedText); //查找,并选中该值
ListGetIndex(listDrives,languageIndex); //获取listDrives当前索引
bDone = TRUE;
endswitch;
until bDone;
// Close the dialog.
EndDialog (szDialogName);
// Remove the dialog from memory.
ReleaseDialog (szDialogName);
// If dialog is closed with Next button, display name and company.
return 0;
end;

//构造界面
function InitDialog(szDialogName)
number localeLanguageID;
HWND hwndDlg;
string s_Save,s_Cancel,s_desc,s_Title;
begin
localeLanguageID=SYSINFO.nSystemLangID;//系统的locale语言
listDrives = ListCreate(STRINGLIST);
hwndDlg = CmdGetHwndDlg( szDialogName );
switch(localeLanguageID)
case 1033://English(United States)  这里是十进制的语言代码
s_Title=”Choose Setup Language”;
s_desc=”Select the language for the installation from the choices below.”;
s_Save=”OK”;
s_Cancel=”Cancel”;
ListAddString (listDrives, “English(US)”, AFTER);
ListAddString (listDrives, “Chinese”, AFTER);
CtrlSetList(szDialogName, RES_CMB, listDrives);
CtrlSetCurSel(szDialogName, RES_CMB, “English(US)”);
case 2052://Chineses(Simplified)
s_Title=”选择安装的语言”;
s_desc=”从下面的列表中选择安装的语言”;
s_Save=”确定”;
s_Cancel=”取消”;
ListAddString (listDrives, “英语(US)”, AFTER);
ListAddString (listDrives, “中文”, AFTER);
CtrlSetList(szDialogName, RES_CMB, listDrives);
CtrlSetCurSel(szDialogName, RES_CMB, “中文”);
default:
s_Title=”Choose Setup Language”;
s_desc=”Select the language for the installation from the choices below.”;
s_Save=”OK”;
s_Cancel=”Cancel”;
ListAddString (listDrives, “English(US)”, AFTER);
ListAddString (listDrives, “Chinese”, AFTER);
CtrlSetList(szDialogName, RES_CMB, listDrives);
CtrlSetCurSel(szDialogName, RES_CMB, “English(US)”);
endswitch;
SdSetDlgTitle(szDialogName, hwndDlg, s_Title);//dialog title
CtrlSetText(szDialogName, RES_Desc,s_desc );//
CtrlSetText(szDialogName, RES_SAVE, s_Save);
CtrlSetText(szDialogName,RES_CANCEL,s_Cancel);
return 0;
end;

复制代码

 

从上面的代码中我们可以看到用户选择的语言存储在全局变量languageIndex中。

接着我们在Setup.Rul的OnFirstUIBefore事件中添加如下代码。

 

Dlg_SelectLanguage:
if !bIsMaintenance then
SdSelectLanguage();
endif;

 

这段代码用来弹出语言框。我们只需要在安装的时候弹出语言框,修复的时候不需要弹出。所以这里我加了一个判断。但是我并没有用系统的静态变量MAINTENANCE进行判断。因为在这里MAINTENANCE始终为False。这是因为我们有两个安装包,实际上安装的是第二个安装包。所以在第一个安装包里是无法判断MAINTENANCE的。这就需要自己写方法判断是否为修复状态。当然你可以在安装的时候在注册表里写个键值,卸载的时候就把它删掉。这就可以把这个键值作为判断条件。

我这里提供一个简单的方法,也是用注册表,但不需要自己写额外的键。

 

复制代码
prototype BOOL IsMaintenance();
function BOOL IsMaintenance()
string regKey;
BOOL bIsMaintenance;
begin
if SYSINFO.bIsWow64 then
regKey=”SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{CD9FTT7B-38C3-4984-9BW5-05DB4E602Y89}”;
else
regKey=”SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{CD9FTT7B-38C3-4984-9BW5-05DB4E602Y89}”;
endif;
RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
if(RegDBKeyExist(regKey)<0) then
bIsMaintenance=FALSE;
else
bIsMaintenance=TRUE;
endif;
return bIsMaintenance;
end;
复制代码

 

CD9FTT7B-38C3-4984-9BW5-05DB4E602Y89“这个值是在第二个安装包的工程里面设置的。在”General Information”这个界面的”Product Code”属性里填入这个值。当然这个值是任意的,你不要填和我一样的值哦,免得我们的产品打架。填了这个值后,InstallShield会为我们自动创建代码中的键值,在卸载的时候会自动删除它。可以看到这个键值在64位和32位的操作系统中路径不同。还有控制面板用的这个键值。

接下来,在OnEnd事件中,我们通过languageIndex变量得知用户所选的语言,判断系统是否安装了这个语言,如果没有则重新弹出语言框,如果有就调用第二个包。OnEnd中的代码如下:

 

复制代码
bIsMaintenance=IsMaintenance();//是否为修复
if !bIsMaintenance then
bSupport=FALSE;
repeat
bSupport= IsSystemSupportLanguage();//是否是系统支持的语言
if !bSupport then //系统不支持
MessageBox(“The current operating system does not have the selected language installed. Please select another language to continue.”,INFORMATION);
SdSelectLanguage();//重新弹出语言选择框
endif;
until bSupport;
endif;

RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
szCommand   =  “Software\\setup.exe”; //第二个包的相对路径
szCmdLine=””;
if !bIsMaintenance then
switch(languageIndex)
case 0://English(United States) 英语
szCmdLine=”-l0x0409″;
case 1://Chineses(Simplified)   中文
szCmdLine=”-l0x0804″;
default:
szCmdLine = “-l0x0409”;
endswitch;
endif;

if   (LaunchApp(szCommand,   szCmdLine)   <   0)   then //用命令行调用第二个包
MessageBox   (“Error”,   SEVERE);
abort;
endif;

复制代码

 

接下来我要贴出让我饱经磨难的一段代码:IsSystemSupportLanguage–是否是系统支持的语言。我在网上没找到解决方法,最后找了台缺少中文语言包的机器,通过手工查找注册表,看安装语言包前后有哪个键值不同。当然找哪些键值是有规则的,拿语言的代码去搜索,不是一条条去翻。。。虽然找到这个键值并没发多少时间,但是为了一个键值而翻注册表的那种心情,是很沉重的。。。

最后我发现 HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Control\\Nls\\Locale 下面列出了系统可以支持的所有语言,当然都是编码。如果没有安装某语言,这个语言的键值就为空。代码如下:

 

复制代码
prototype BOOL IsSystemSupportLanguage();
function BOOL IsSystemSupportLanguage()
BOOL bResult;
STRING szKey,szName,svValue;
NUMBER nvSize,nvType;
begin
RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
szKey=”SYSTEM\\ControlSet001\\Control\\Nls\\Locale”;
switch(languageIndex)
case 0://English(United States) 英文
szName=”00000409″;
case 1://Chineses(Simplified) 中文
szName=”00000804″;
default:
szName=”00000409″;
endswitch;
nvType=REGDB_STRING;
if RegDBGetKeyValueEx(szKey,szName,nvType,svValue,nvSize)<0 then
bResult=FALSE;
else
if svValue==”” then //如果这个键值为空,就表示没有安装
bResult=FALSE;
else
bResult=TRUE;// 如果这个键值有值,就表示已安装
endif;
endif;
return bResult;
end;
复制代码

下面放出工程文件。/Files/linjiao0125/LanguageDialogProject.rar

如果有疑问可以加我的QQ:252945226。 如果你有更好的解决方法请留言,或者加QQ,或者发邮件 linjiao0125@qq.com

WPF后台动态调用样式文件

应用场合:如果您的WPF应用程序设置WPF运行一个实例代码后,App.xaml文件中对样式资源字典文件的引用将失效.

解决办法1:在App.xaml.cs文件中用反射动态调用另外一个DLL项目中的样式文件即可

详细操作介绍如下:

1、WPF设置只运行一个实例代码:

App.xaml文件代码如下:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source=”ButtonStyle.xaml”/>
</ResourceDictionary>
</Application.Resources>
</Application>

App.xaml.cs文件代码如下:

//添加引用
using System.Diagnostics;
using System.IO;
using System.Reflection;

namespace WpfUI
{

/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{

public App()
{
}

/// <summary>
/// 要设置App.xaml的文件属性中生成操作=无
/// </summary>
[STAThread]
public static void Main()
{
App myApp = new App();
myApp.ShutdownMode = ShutdownMode.OnExplicitShutdown;
myApp.Run();
}

private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
if (e.Exception is InvalidOperationException)
e.Handled = true;
}

protected override void OnStartup(StartupEventArgs e)
{
//获取当前运行WPF程序的进程实例
Process process = Process.GetCurrentProcess();
//遍历WPF程序的同名进程组
foreach (Process p in Process.GetProcessesByName(process.ProcessName))
{
if (p.Id != process.Id && (p.StartTime – process.StartTime).TotalMilliseconds <= 0)
{
p.Kill();//关闭进程
return;
}
}
base.OnStartup(e);
//启动登陆窗体,

MainWindow myWindow = new MainWindow();

myWindow.Show();
}

}

}

2、ButtonStyle.xaml样式文件内容如下:

<ResourceDictionary xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml“>
<!–按钮样式–>
<Style x:Key=”RedButtonStyle” TargetType=”{x:Type Button}”>
<Setter Property=”Foreground” Value=”Red”/>
<Setter Property=”Background” Value=”Silver”/>
<Setter Property=”Height” Value=”23″/>
</Style>

</ResourceDictionary>

3、MainWindow.xaml文件内容如下:

<Window x:Class=”WpfUI.MainWindow”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml
Title=”MainWindow” Height=”350″ Width=”525″>
<Grid>
<Button Content=”Button” Style=”{StaticResource RedButtonStyle}” Height=”23″ HorizontalAlignment=”Left” Margin=”102,66,0,0″ Name=”button1″ VerticalAlignment=”Top” Width=”75″ />
</Grid>
</Window>
4、运行程序后发现按钮样式RedButtonStyle总提示找不到

5、 解决办法如下:

第一步: 新建一个Windows–类库项目WpfThems,将ButtonStyle.xaml拷贝过去, 设置ButtonStyle.xaml文件的属性生成操作为 “Page”,之后生成WpfThems.dll文件

第二步:在当前项目WpfUI中添加WpfThems项目引用

第三步:修改App.xaml.cs 文件代码:添加一个函数LoadStyleResource并修改OnStartup函数内容。修改后的App.xaml.cs文件内容如下:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
//添加引用
using System.Diagnostics;
using System.IO;
using System.Reflection;

namespace WpfUI
{
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{

public App()
{
}

/// <summary>
/// 设置WpfUI项目中的App.xaml的文件属性中”生成操作”为”无”
/// 设置WpfThemes项目中的ButtonStyle.xaml的文件属性中”生成操作”为”Page”
/// </summary>
[STAThread]
public static void Main()
{
App myApp = new App();
myApp.ShutdownMode = ShutdownMode.OnExplicitShutdown;
myApp.Run();
}

/// <summary>
/// 重载应用程序启动函数
/// </summary>
/// <param name=”e”></param>
protected override void OnStartup(StartupEventArgs e)
{
//获取当前运行WPF程序的进程实例
Process process = Process.GetCurrentProcess();
//遍历WPF程序的同名进程组
foreach (Process p in Process.GetProcessesByName(process.ProcessName))
{
if (p.Id != process.Id && (p.StartTime – process.StartTime).TotalMilliseconds <= 0)
{
p.Kill();//关闭进程
return;
}
}
base.OnStartup(e);
//动态调用样式文件
LoadStyleResource();

//启动窗体
MainWindow myWindow = new MainWindow();
myWindow.Show();
}
private void LoadStyleResource()
{
Assembly assembly = Assembly.LoadFrom(“WpfThemes.dll”);
string packUri = @”/WpfThemes;component/ButtonStyle.xaml”;
ResourceDictionary myResourceDictionary = Application.LoadComponent(new Uri(packUri, UriKind.Relative)) as ResourceDictionary;
this.Resources.MergedDictionaries.Add(myResourceDictionary);
}
}
}

 

解决办法2:在每个窗体的xaml文件中添加对指定样式文件的引用

<Window.Resources>

<ResourceDictionary>

<ResourceDictionary.MergedDictionaries>

<ResourceDictionary Source=”ButtonStyle.xaml”/>

</ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

</Window.Resources>

详细工程项目请到我的下载资源中下载: http://download.csdn.net/detail/xqf222/5582575

WPF 登录窗口关闭时打开主窗口

在WPF中设计登录窗口关闭时打开主窗口,自动生成的App.xaml不能满足要求,

1、把App.xaml的属性窗口中的生成操作设定为 无

2、添加Program类

复制代码
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            LoginWindow loginForm = new LoginWindow();
            loginForm.Init();
            bool? rt = loginForm.ShowDialog();
            loginForm.Close();
            if (rt == true)
            {
                Application App = new Application();
                App.ShutdownMode = ShutdownMode.OnMainWindowClose;
                MainWindow m_MianWindow = new MainWindow();
                App.MainWindow = m_MianWindow;
                App.Run(m_MianWindow);
            }
        }
    }
复制代码

这样就可以满足要求了

参考:http://www.mysjtu.com/page/M0/S613/613036.html

 

方法二:

最近看一方法,不用添加Program方法即可,代码如下:

复制代码
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            Application.Current.ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown;
            LoginWindow window = new LoginWindow();
            bool? dialogResult = window.ShowDialog();
            if ((dialogResult.HasValue == true) &&
                (dialogResult.Value == true))
            {
                base.OnStartup(e);
                Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
            }
            else
            {
                this.Shutdown();
            }
        }
    }
复制代码

通过Application的ShutdownMode控制进程的运行时间。

.net安装包自动安装Mysql数据库

在制作.Net安装包的时候,如果项目有用到数据库,怎么能够把数据库打包安装呢?网上已经有很多自动安装Sql Server数据库的例子,但是自动安装mysql的例子似乎不多。本文就介绍一下如何在.Net安装包中自动安装Mysql数据库。

最终我们要实现的效果是,部署.Net桌面应用程序时,能够一键自动安装应用程序以及附带的MySql数据库,并初始化数据库。

实现步骤如下:

 

1.准备一个干净的MySql安装包

mysql本身是开源的,安装完mysql数据库后,其实只是在系统里面安装了一个Windows服务(相对于Windows系统来说)

可以从网上下载一个mysql版本,比如我用的是Mysql5.5,下载地址:http://dev.mysql.com/downloads/mysql/5.5.html

下载安装包,按照正常安装流程安装完后,直接将安装后的目录拷贝一份为我们后面制作安装项目所用。

一般的mysql目录如:net,自动,安装,数据库,安装包0

 

2.修改My.ini配置文件

拷贝一份Mysql目录之后,需要根据项目的需要,需改一下mysql的设置,如设置缓存大小、存储类型等参数。

需要特别设置的是:

端口需要做特别设置,一般默认是3306端口,我们为了防止冲突,将端口改为3307

[mysqld]

# The TCP/IP Port the MySQL Server will listen on port=3307

basedir需要修改,此目录就是MySql文件夹的物理位置,这里显然需要动态配置,我们暂时用一个自定义的占位符来代替,后面在程序中修改。

#Path to installation directory. All paths are usually resolved relative to this.
basedir=”%BaseDir%/MySQL Server 5.5/”

datadir需要修改,此目录是Mysql数据的存放路径,也需要动态配置,暂时用占位符代替,后面用程序修改。

#Path to the database root
datadir=”%BaseDir%/MySQL Server 5.5/data/”

 

3.在安装项目中包含mysql文件

为了测试,我建了如下三个项目:

net,自动,安装,数据库,安装包1

setup1项目就是.Net的安装项目

MySqlAutoInstall是模拟的一个需要使用mysql数据库的桌面程序。

InserterDb项目是一个DLL类库项目,功能是安装mysql数据库。我们将在setup1项目中调用此类库实现mysq数据库自动安装。

 

右键Setup1项目,选择“视图”–“文件系统”,将第一步准备的干净Mysql数据库文件夹拖入“应用程序文件夹”下。

再建一个“你的程序”文件夹(可自定义名称),下面放你的桌面程序,本例中是MySqlAutoInstall项目。

 

4.创建安装MySql数据库的自定义操作

建InserterDb的类库项目,添加一个“安装程序类”Installer1.cs

代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;

using MySql.Data.MySqlClient;
using System.IO;
using System.Threading;

namespace inserterDb
{
    [RunInstaller(true)]
    public partial class Installer1 : System.Configuration.Install.Installer
    {
        public Installer1()
        {
            InitializeComponent();
        }

        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);

            InsertMySql();
            CreatDataBase();
            Log("安装成功!");
        }

       //安装mysql
        protected void InsertMySql()
        {
            string physicalRoot = this.Context.Parameters["targetdir"]; // 安装物理路径 C:\program\microp
            string appPath = physicalRoot + "\\MySQL Server 5.5\\";

            //1.修改my.ini配置  为防止本机已装mysql,特修改my.ini中端口号为3307
            string iniFile = File.ReadAllText(appPath + "my.ini");
            iniFile = iniFile.Replace("%BaseDir%", physicalRoot.Replace("\\", "/")); //%BaseDir%为my.ini中自定义的目录参数
            File.WriteAllText(appPath + "my.ini", iniFile);

            Log("创建win服务……");
            //2.创建win服务
            string info1 = CommandHelper.Execute(appPath + "bin\\mysqld.exe", " install MySQL2 --defaults-file=\"" + appPath + "my.ini\"", 0);
            Log(info1);
            Thread.Sleep(3000);
            Log("使用net start启动服务");
            //3.启动服务
            string info2 = CommandHelper.Execute("net start MySQL2", 0);
            Log(info2);

            Log("启动服务完成!");
            Thread.Sleep(5000);
            MySqlConnection con = new MySqlConnection("Data Source='localhost';Port='3307';Database='';User Id='root';Password='';");
            try
            {
                con.Open();
                con.Close();
                Log("连接成功!");
            }
            catch (Exception ex)
            {
                Log("连接失败!" + ex.Message);
            }
        }
        //创建数据库并初始化表
        protected void CreatDataBase()
        {
            string physicalRoot = this.Context.Parameters["targetdir"]; // 安装物理路径 C:\program\microp
            string mysqlcon = "Data Source='localhost';Port='3307';Database='{0}';User Id='root';Password='';";

            MySqlConnection conn = new MySqlConnection(string.Format(mysqlcon, ""));
            FileInfo file = new FileInfo(physicalRoot + "\\DBInit\\yourDB.sql");  //filename是sql脚本文件路径。
            string sql = file.OpenText().ReadToEnd();

            try
            {
                MySqlScript script = new MySqlScript(conn);
                script.Query = sql;
                int count = script.Execute();
                Log("数据库初始化完成!");

                MySqlConnection con2 = new MySqlConnection(string.Format(mysqlcon, "yourDB"));
                con2.Open();
                MySqlCommand dbcom = new MySqlCommand("select count(*) from t_image", con2);
                dbcom.ExecuteScalar();
                con2.Close();
                Log("数据库创建OK!");

                //修改config.xml中的数据库链接地址
                
            }
            catch (Exception ex2)
            {
                Log("数据库创建失败!" + ex2.Message);
            }


        }

        //写日志
        protected void Log(string line)
        {
            string physicalRoot = this.Context.Parameters["targetdir"]; // 安装物理路径 C:\program\microp
            string filePath = physicalRoot + "Install_log.txt";
            if (File.Exists(filePath))
            {
                File.AppendAllLines(filePath, new string[] { DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ") + line });
            }
            else
            {
                File.WriteAllLines(filePath, new string[] { DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ") + line });
            }
        }
    }
}

 

此类 override void Install方法,当程序安装完后,立即启动数据库的安装。

用代码安装MySql数据库安装步骤是:

1)修改my.ini配置  为防止本机已装mysql,特修改my.ini中端口号为3307

my.ini中的路径应该设置为安装程序的安装目录,这个目录是用户自定义的,可以通过设置Setup的customActionData属性来传递参数,详见后文介绍。

2)调用“mysqld.exe -install ”命令安装mysql数据库服务

3)使用net start启动Mysql服务

4)验证数据库安装结果

5)执行初始化脚本,初始化数据库

 

5.在Setup中关联安装MySql数据库的自定义操作

右键单击“setup1”项目,选择“视图”–“自定义操作” 在“安装”文件夹下右键选“添加自定义操作”  选择第4步制作的InserterDb的类库项目。

net,自动,安装,数据库,安装包2

设置刚添加的自定义操作的属性,CustomActionData  为  /targetdir=”[TARGETDIR]\”

net,自动,安装,数据库,安装包3

这里的targetdir是自定义的参数名称,目的是获取用户选择的程序安装路径,用于修改Mysql配置文件中的%BaseDir%参数。

 

测试源码下载: Source

注意,由于mysql文件太大,该目录只留目录名,文件自己拷贝进去即可。

WPF 后台任务 等待动画 样例

运行效果:

前台代码:

[csharp] view plain copy

  1. <Window x :Class=“Waiting.Window1”
  2.         xmlns=“http://schemas.microsoft.com/winfx/2006/xaml/presentation”
  3.         xmlns:x =“http://schemas.microsoft.com/winfx/2006/xaml”
  4.         Title=“后台忙” Height=“94.635” Width=“197.361”>
  5.     <Grid >
  6.         <Button Content =“开始” HorizontalAlignment=“Left” Margin=“10,10,0,0” Name =“button1” VerticalAlignment=“Top” Click=“button1_Click” Width =“48” />
  7.         <Label Name =“lab_pro” Content=“” Height=“25” VerticalAlignment =“Top” HorizontalAlignment=“Left” Margin=“80,20,0,0” />
  8.         <!–动画代码,只要填写name属性即可–>
  9.         <Grid Name =“loading” Visibility=“Collapsed” Height=“41” Grid.Row =“0” VerticalAlignment=“Top” Margin=“126,10,0,0” HorizontalAlignment=“Left” Width =“42”>
  10.             <Grid.Resources>
  11.                 <DrawingBrush x :Key=“brush” Stretch=“None” AlignmentX =“Center” AlignmentY=“Top”>
  12.                     <DrawingBrush.Drawing>
  13.                         <GeometryDrawing Brush =“Black”>
  14.                             <GeometryDrawing.Geometry>
  15.                                 <EllipseGeometry RadiusX =“2” RadiusY=“5”/>
  16.                             </GeometryDrawing.Geometry>
  17.                         </GeometryDrawing>
  18.                     </DrawingBrush.Drawing>
  19.                 </DrawingBrush>
  20.             </Grid.Resources>
  21.             <Rectangle x :Name=“r01” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  22.                 <Rectangle.RenderTransform>
  23.                     <RotateTransform Angle =“0”/>
  24.                 </Rectangle.RenderTransform>
  25.             </Rectangle>
  26.             <Rectangle x :Name=“r02” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  27.                 <Rectangle.RenderTransform>
  28.                     <RotateTransform Angle =“30”/>
  29.                 </Rectangle.RenderTransform>
  30.             </Rectangle>
  31.             <Rectangle x :Name=“r03” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  32.                 <Rectangle.RenderTransform>
  33.                     <RotateTransform Angle =“60”/>
  34.                 </Rectangle.RenderTransform>
  35.             </Rectangle>
  36.             <Rectangle x :Name=“r04” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  37.                 <Rectangle.RenderTransform>
  38.                     <RotateTransform Angle =“90”/>
  39.                 </Rectangle.RenderTransform>
  40.             </Rectangle>
  41.             <Rectangle x :Name=“r05” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  42.                 <Rectangle.RenderTransform>
  43.                     <RotateTransform Angle =“120”/>
  44.                 </Rectangle.RenderTransform>
  45.             </Rectangle>
  46.             <Rectangle x :Name=“r06” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  47.                 <Rectangle.RenderTransform>
  48.                     <RotateTransform Angle =“150”/>
  49.                 </Rectangle.RenderTransform>
  50.             </Rectangle>
  51.             <Rectangle x :Name=“r07” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  52.                 <Rectangle.RenderTransform>
  53.                     <RotateTransform Angle =“180”/>
  54.                 </Rectangle.RenderTransform>
  55.             </Rectangle>
  56.             <Rectangle x :Name=“r08” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  57.                 <Rectangle.RenderTransform>
  58.                     <RotateTransform Angle =“210”/>
  59.                 </Rectangle.RenderTransform>
  60.             </Rectangle>
  61.             <Rectangle x :Name=“r09” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  62.                 <Rectangle.RenderTransform>
  63.                     <RotateTransform Angle =“240”/>
  64.                 </Rectangle.RenderTransform>
  65.             </Rectangle>
  66.             <Rectangle x :Name=“r10” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  67.                 <Rectangle.RenderTransform>
  68.                     <RotateTransform Angle =“270”/>
  69.                 </Rectangle.RenderTransform>
  70.             </Rectangle>
  71.             <Rectangle x :Name=“r11” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  72.                 <Rectangle.RenderTransform>
  73.                     <RotateTransform Angle =“300”/>
  74.                 </Rectangle.RenderTransform>
  75.             </Rectangle>
  76.             <Rectangle x :Name=“r12” Fill=“{StaticResource brush}” Opacity =“0.5” RenderTransformOrigin=“0.5,0.5”>
  77.                 <Rectangle.RenderTransform>
  78.                     <RotateTransform Angle =“330”/>
  79.                 </Rectangle.RenderTransform>
  80.             </Rectangle>
  81.             <Grid.Triggers>
  82.                 <EventTrigger RoutedEvent =“Grid.Loaded”>
  83.                     <BeginStoryboard>
  84.                         <Storyboard RepeatBehavior =“Forever”>
  85.                             <DoubleAnimation Storyboard.TargetName =“r01” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.00000” To=“0”/>
  86.                             <DoubleAnimation Storyboard.TargetName =“r02” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.08333” To=“0”/>
  87.                             <DoubleAnimation Storyboard.TargetName =“r03” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.16666” To=“0”/>
  88.                             <DoubleAnimation Storyboard.TargetName =“r04” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.24999” To=“0”/>
  89.                             <DoubleAnimation Storyboard.TargetName =“r05” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.33332” To=“0”/>
  90.                             <DoubleAnimation Storyboard.TargetName =“r06” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.41665” To=“0”/>
  91.                             <DoubleAnimation Storyboard.TargetName =“r07” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.49998” To=“0”/>
  92.                             <DoubleAnimation Storyboard.TargetName =“r08” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.58331” To=“0”/>
  93.                             <DoubleAnimation Storyboard.TargetName =“r09” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.66664” To=“0”/>
  94.                             <DoubleAnimation Storyboard.TargetName =“r10” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.74997” To=“0”/>
  95.                             <DoubleAnimation Storyboard.TargetName =“r11” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.83330” To=“0”/>
  96.                             <DoubleAnimation Storyboard.TargetName =“r12” Storyboard.TargetProperty=“Opacity” AutoReverse=“True” Duration=“0:0:0.08333” BeginTime =“0:0:0.91663” To=“0”/>
  97.                         </Storyboard>
  98.                     </BeginStoryboard>
  99.                 </EventTrigger>
  100.             </Grid.Triggers>
  101.         </Grid>
  102.     </Grid >
  103. </Window>

后台代码:

[csharp] view plain copy

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Data;
  9. using System.Windows.Documents;
  10. using System.Windows.Input;
  11. using System.Windows.Media;
  12. using System.Windows.Media.Imaging;
  13. using System.Windows.Shapes;
  14. namespace Waiting
  15. {
  16.     /// <summary>
  17.     /// Window1.xaml 的交互逻辑
  18.     /// </summary>
  19.     public partial class Window1 : Window
  20.     {
  21.         public Window1()
  22.         {
  23.             InitializeComponent();
  24.         }
  25.         BackgroundWorker bgMeet;
  26.         private void button1_Click(object sender, RoutedEventArgs e)
  27.         {
  28.             bgMeet = new BackgroundWorker ();
  29.             //能否报告进度更新
  30.             bgMeet.WorkerReportsProgress = true;
  31.             //要执行的后台任务
  32.             bgMeet.DoWork += new DoWorkEventHandler (bgMeet_DoWork);
  33.             //进度报告方法
  34.             bgMeet.ProgressChanged += new ProgressChangedEventHandler (bgMeet_ProgressChanged);
  35.             //后台任务执行完成时调用的方法
  36.             bgMeet.RunWorkerCompleted += new RunWorkerCompletedEventHandler (bgMeet_RunWorkerCompleted);
  37.             bgMeet.RunWorkerAsync(); //任务启动
  38.         }
  39.         //执行任务
  40.         void bgMeet_DoWork(object sender, DoWorkEventArgs e)
  41.         {
  42.             //开始播放等待动画
  43.             this.Dispatcher.Invoke(new Action(() =>
  44.             {
  45.                 loading.Visibility = System.Windows. Visibility.Visible;
  46.             }));
  47.             //开始后台任务
  48.             GetData();
  49.         }
  50.         //报告任务进度
  51.         void bgMeet_ProgressChanged(object sender, ProgressChangedEventArgs e)
  52.         {
  53.             this.Dispatcher.Invoke(new Action(() =>
  54.             {
  55.                 this.lab_pro.Content = e.ProgressPercentage + “%”;
  56.             }));
  57.         }
  58.         //任务执行完成后更新状态
  59.         void bgMeet_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  60.         {
  61.             loading.Visibility = System.Windows. Visibility.Collapsed;
  62.             this.Dispatcher.Invoke(new Action(() =>
  63.             {
  64.                 this.lab_pro.Content = “完成” ;
  65.             }));
  66.         }
  67.         //模拟耗时任务
  68.         public void GetData()
  69.         {
  70.             for (int i = 0; i < 6; i++)
  71.             {
  72.                 bgMeet.ReportProgress(20*i);
  73.                 System.Threading. Thread.Sleep(400);
  74.             }
  75.         }
  76.     }
  77. }

C# 执行Mysql数据库脚本 创建数据库和表

开发过程中可能需要将数据库导入到生产库中,我们可以通过MySQL workbench将数据库导出成sql文件,然后在C#代码中直接执行sql脚本,创建相应的数据库和表。

项目中需要添加引用mysql.Data.dll

CS文件中需要应用using MySql.Data.MySqlClient;

[csharp] view plain copy

  1. private void button_connTest_Click(object sender, RoutedEventArgs e)
  2. {
  3.     string connStr = “server=192.168.3.21;user=root;database=;port=3306;password=root;”;
  4.     MySqlConnection conn = new MySqlConnection(connStr);
  5.     try
  6.     {
  7.         textbox_log.Text += “Connecting to MySQL…\r\n”;
  8.         conn.Open();
  9.         FileInfo file = new FileInfo(“D:/endb.sql”);  //filename是sql脚本文件路径。
  10.         string sql = file.OpenText().ReadToEnd();
  11.         MySqlScript script = new MySqlScript(conn);
  12.         script.Query = sql;
  13.         int count = script.Execute();
  14.         textbox_log.Text += “Executed “ + count + ” statement(s)\r\n”;
  15.         textbox_log.Text += “Delimiter: “ + script.Delimiter+“\r\n”;
  16.         //textbox_log.Text += “Query: ” + script.Query + “\r\n”;
  17.     }
  18.     catch (Exception ex)
  19.     {
  20.         textbox_log.Text += ex.ToString();
  21.     }
  22.     conn.Close();
  23.     textbox_log.Text += “Execute Successfully.”;
  24. }


执行完成后,就可以看到新的数据库自动创建成功了。

如果只是简单的执行建表或者其他语句,那么数据库连接字符串将要写对应的数据库名,如:

[csharp] view plain copy

  1. string connStr = “server=192.168.3.21;user=root;database=MyDB;port=3306;password=root;”;
  2. MySqlConnection conn = new MySqlConnection(connStr);

相关知识:通过Mysql WorkBench将数据库导出成sql脚本,选择菜单–>Server–>Data Export

2