Process Start 相关

在自己的【CSharp语法之 Process.Start 相关】 文章中有详解

前言

偶然发现,如果想用如下代码在 .NET 6 中打开指定 URL:

1
Process.Start("https://baidu.com");

会引发异常:

而同样的代码在 .NET Framework 中是可以正常执行的。

难道,.NET 6 下的实现逻辑不一样?

深入探究

通过调用堆栈,我们发现最后调用的是StartWithCreateProcess方法:

对应的 .NET 6 源代码如下:

1
2
3
4
5
6
7
8
private bool StartCore(ProcessStartInfo startInfo)
{
if (!startInfo.UseShellExecute)
{
return this.StartWithCreateProcess(startInfo);
}
return this.StartWithShellExecuteEx(startInfo);
}

这和 .NET Framework 中的实现逻辑基本一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public bool Start()
{
this.Close();
ProcessStartInfo processStartInfo = this.StartInfo;
if (processStartInfo.FileName.Length == 0)
{
throw new InvalidOperationException(SR.GetString("FileNameMissing"));
}
if (processStartInfo.UseShellExecute)
{
return this.StartWithShellExecuteEx(processStartInfo);
}
return this.StartWithCreateProcess(processStartInfo);
}

那么问题出在哪呢?

通过 dnspy 调试 .NET Framework 版本的测试程序,我们发现,最后执行的是StartWithShellExecuteEx而不是StartWithCreateProcess方法:

而之所以走不同的逻辑分支,是由processStartInfo.UseShellExecute控制的。

所以,解决方案也很简单,设置UseShellExecute = true:

1
Process.Start(new ProcessStartInfo("https://baidu.com") { UseShellExecute = true });

结论

造成这样的原因,是因为UseShellExecute在 .NET 6 上默认为 false:

1
public bool UseShellExecute { get; set; }

而在 .NET Framework 上默认为 true:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[DefaultValue(true)]
[MonitoringDescription("ProcessUseShellExecute")]
[NotifyParentProperty(true)]
public bool UseShellExecute
{
get
{
return this.useShellExecute;
}
set
{
this.useShellExecute = value;
}
}

private bool useShellExecute = true;

UseShellExecute = false时,代码会将传入参数作为文件名使用,从而引发“系统找不到指定的文件”异常。

打开邮件

打开outlook邮件窗口–打开软件,后边没使用

在C#中,要打开Outlook并执行操作,你可以使用Microsoft Outlook PIA (Primary Interop Assembly) 或者通过COM对象模型。以下是使用COM对象模型打开Outlook并创建一个新电子邮件的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System;
using Outlook = Microsoft.Office.Interop.Outlook;

class Program
{
static void Main(string[] args)
{
// 创建一个新的Application对象并打开Outlook
Outlook.Application outlookApp = new Outlook.Application();

// 创建一个新的MailItem对象
Outlook.MailItem mailItem = (Outlook.MailItem)outlookApp.CreateItem(Outlook.OlItemType.olMailItem);

// 填写电子邮件的基本信息
mailItem.To = "recipient@example.com";
mailItem.Subject = "Hello";
mailItem.Body = "This is a test email.";

// 显示电子邮件
mailItem.Display(true);

// 释放对象
System.Runtime.InteropServices.Marshal.ReleaseComObject(mailItem);
System.Runtime.InteropServices.Marshal.ReleaseComObject(outlookApp);

Console.WriteLine("Email opened in Outlook.");
}
}

确保你的项目引用了Microsoft Outlook Object Library。如果你的开发环境是Visual Studio,你可以通过NuGet包管理器安装Microsoft.Office.Interop.Outlook。

请注意,由于COM对象的交互性质,这种方法可能会引发各种异常,比如Outlook没有安装或者COM对象访问被禁用等问题。在实际部署时,你可能需要处理这些异常。

报错找不到文件

我有一个用C#编写的简单应用程序。net 6.0。按下按钮后,我想打开一个Outlook窗口,其中包含文本框中的e-mail内容和文本框中的收件人。

问题是,该应用程序用于公司领域的计算机。我无法使用smtp以编程方式发送电子邮件。

必须从当前登录到计算机(窗口10)的用户的帐户中完全禁止邮件,该用户已配置Outlook帐户,以便将其分配给windows帐户。

我试图使用Microsoft.Office.Interop.Outlook,但我出错了

1
Could not load file or assembly 'Microsoft.Office.Interop.Outlook, Version = 15.0.0.0, Culture = neutral, PublicKeyToken = 71e9bce111e9429c

您需要向该项目添加COM引用。右键单击项目的依赖项并选择Add COM references

在对话框窗口中,您可以找到Outlook的条目并将其选中。

维奥拉!单击Ok按钮并在代码中使用OOM。

方法一、通过mailto标签发送邮件(常用)

​ 通过mailto不是正真意义上的发送邮件,它只是会自动调用我们本地默认的邮件服务软件(这取决于我们本地安装了什么邮件软件,outlook,firemail等等),发送还是需要我们自己点击发送才能完成。

  mailto标签有很多实用的方法,比如:加入邮件的默认主题、抄送地址、暗送(密件抄送)地址,邮件内容等待…..

1、为邮件加入发件人

  格式:mailto:发件人地址

  代码示例

1
2
3
4
5
6
7
8
9
10
/// <summary>
/// sendFrom:发件人
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendEmail_ItemClick(object sender, ItemClickEventArgs e)
{
string message = string.Format("mailto:{0}", sendFrom);
System.Diagnostics.Process.Start(message);//调用进程启动邮件
}

2、为邮件加入默认标题

  格式:mailto:发件人地址?subject=邮件主题

  代码示例

1
2
3
4
5
6
7
8
9
10
11
/// <summary>
/// sendFrom:发件人
/// subjetc:主题
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendEmail_ItemClick(object sender, ItemClickEventArgs e)
{
string message = string.Format("mailto:{0}?subject={1}", sendFrom, subjetc);
System.Diagnostics.Process.Start(message);//调用进程启动邮件
}

3、为邮件加入默认的抄送地址

格式:mailto:发件人地址?cc=抄送地址

  代码示例

1
2
3
4
5
6
7
8
9
10
11
/// <summary>
/// sendFrom:发件人
/// sendCC:抄送地址
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendEmail_ItemClick(object sender, ItemClickEventArgs e)
{
string message = string.Format("mailto:{0}?cc={1}", sendFrom, sendCC);
System.Diagnostics.Process.Start(message);//调用进程启动邮件
}

4、为邮件加入默认暗送(密件抄送)地址

格式:mailto:发件人地址?bcc=密送地址

  代码示例

1
2
3
4
5
6
7
8
9
10
11
/// <summary>
/// sendFrom:发件人
/// sendBCC:密送地址
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendEmail_ItemClick(object sender, ItemClickEventArgs e)
{
string message = string.Format("mailto:{0}?bcc={1}", sendFrom, sendBCC);
System.Diagnostics.Process.Start(message);//调用进程启动邮件
}

6、多个邮件地址

格式:mailto:发件人地址1,发件地址2,发件地址3(以逗号分隔)

7、综合型的:加入默认抄送地址,标题,内容

格式:mailto:发件人地址1,发件人地址2?cc=抄送地址&bcc=密送地址&subject=主题&body=邮件内容

  代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/// <summary>
/// sendFrom:发件人
/// sendCC:抄送
/// subjetc:密送
/// subjetc:主题
/// content:邮件内容
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendEmail_ItemClick(object sender, ItemClickEventArgs e)
{
string message = string.Format("mailto:{0}?cc={1}&bcc={2}&subject={3}&body={4}", sendFrom, sendCC,
sendBCC, subjetc, content);
System.Diagnostics.Process.Start(message);//调用进程启动邮件
}

如图所示:

方法二、通过SMTP协议发送邮件

核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/// <summary>
/// 发送邮件
/// </summary>
/// <param name="userEmailAddress">发件人地址</param>
/// <param name="userName">发件人姓名(可为空)</param>
/// <param name="password">密码</param>
/// <param name="host">邮件服务器地址</param>
/// <param name="port"></param>
/// <param name="sendToList">收件人(多个电子邮件地址之间必须用逗号字符(“,”)分隔)</param>
/// <param name="sendCCList">抄送人(多个电子邮件地址之间必须用逗号字符(“,”)分隔)</param>
/// <param name="subject">主题</param>
/// <param name="body">内容</param>
/// <param name="attachmentsPath">附件路径</param>
/// <param name="errorMessage">错误信息</param>
public static bool SendMessage(string userEmailAddress, string userName, string password, string host, int port,
string[] sendToList, string[] sendCCList, string subject, string body, string[] attachmentsPath, out string errorMessage)
{
errorMessage = string.Empty;
SmtpClient client = new SmtpClient();
client.Credentials = new System.Net.NetworkCredential(userEmailAddress, password);//用户名、密码
client.DeliveryMethod = SmtpDeliveryMethod.Network;//指定电子邮件发送方式
client.Host = host;//邮件服务器
client.Port = port;//端口号 非SSL方式,默认端口号为:25
client.UseDefaultCredentials = true;

MailMessage msg = new MailMessage();
//加发件人
foreach (string send in sendToList)
{
msg.To.Add(send);
}
//加抄送
foreach (string cc in sendCCList)
{
msg.To.Add(cc);
}

//在有附件的情况下添加附件
if (attachmentsPath != null && attachmentsPath.Length > 0)
{
foreach (string path in attachmentsPath)
{
var attachFile = new Attachment(path);
msg.Attachments.Add(attachFile);
}
}
msg.From = new MailAddress(userEmailAddress, userName);//发件人地址
msg.Subject = subject;//邮件标题
msg.Body = body;//邮件内容
msg.BodyEncoding = System.Text.Encoding.UTF8;//邮件内容编码
msg.IsBodyHtml = true;//是否是HTML邮件
msg.Priority = MailPriority.High;//邮件优先级

try
{
client.Send(msg);
return true;
}
catch (System.Net.Mail.SmtpException ex)
{
errorMessage = ex.Message;
return false;
}
}

如图所示:

计算器

1
System.Diagnostics.Process.Start(@"calc.exe");//打开计算器

默认浏览器

示例界面:

方法一:从注册表中读取默认浏览器可执行文件路径

1
2
3
4
5
6
7
8
9
private void button1_Click(object sender, EventArgs e)
{
//从注册表中读取默认浏览器可执行文件路径
RegistryKey key = Registry.ClassesRoot.OpenSubKey(@"http\shell\open\command\");
string s = key.GetValue("").ToString();
//s就是你的默认浏览器,不过后面带了参数,把它截去,不过需要注意的是:不同的浏览器后面的参数不一样!
//"D:\Program Files (x86)\Google\Chrome\Application\chrome.exe" -- "%1"
System.Diagnostics.Process.Start(s.Substring(0, s.Length - 8), "http://blog.csdn.net/testcs_dn");
}

方法二:

1
2
3
4
5
private void button2_Click(object sender, EventArgs e)
{
//调用系统默认的浏览器
System.Diagnostics.Process.Start("explorer.exe", "http://blog.csdn.net/testcs_dn");
}

方法三:

1
2
3
4
5
private void button3_Click(object sender, EventArgs e)
{
//调用系统默认的浏览器
System.Diagnostics.Process.Start("http://blog.csdn.net/testcs_dn");
}

方法四:调用IE浏览器

1
2
3
4
5
private void button4_Click(object sender, EventArgs e)
{
//调用IE浏览器
System.Diagnostics.Process.Start("iexplore.exe", "http://blog.csdn.net/testcs_dn");
}

方法二和方法三一样,只不过方法三写法简便,

下面说一下方法四

1、所需环境

.NET环境、需要引用System.Diagnostics这个命名空间、 一个准备好的静态网页

2、实现

其实这个程序非常的简单,只是把调用程序的方法用在了这里而已,并且是用特定程序打开特定文件的的一种使用。

1
System.Diagnostics.Process.Start(@"IExplore.exe", "http://blog.csdn.net/testcs_d");

这个函数Start里面有两个参数,第一个是指定的程序的地址,第二个参数是指定的文件的地址,一组合就出现了我们想要的效果(由于IE是系统应用且在环境变量中有了设置,可以直接写.exe的文件)。前提是我们要有上述的网页放在指定位置。

我们不能直接把这个函数写到控件事件中,否则会报错的。我写了一个方法然后调用实现的。如果要调用别的浏览器只要把例子中的”iexplore.exe”改为其他的浏览器程序名既可。

我们还可以用它来进行打开别的文件的操作,比如,用notpad++来打开一个文本文件了,在方法的第一参数上写上notpad++的主程序地址,后面写上文本文件名字就可以了。


相关链接(侵删)

  1. C# 如何通过mailto标签和SMTP协议两种方式发送邮件
  2. Process.Start 为什么会引发“系统找不到指定的文件”异常
  3. C# 打开应用程序或打开默认电子邮件应用程序
  4. C#打开outlook邮件窗口
  5. 【C#】调用默认浏览器打开网页的几种方法

=================我是分割线=================

欢迎到公众号来唠嗑: