C# Winform插件开发笔记
- Process调起应用程序
- Arguments传参(Windows服务无法使用以下方式发起调用,因为不属于同一session,需要引入底层类库,模拟用户进程以启动新程序)
1 Process process = new Process(); 2 process.EnableRaisingEvents = true; 3 process.Exited += designerExitedHandler; 4 process.StartInfo = new ProcessStartInfo 5 { 6 FileName = "\"" + appPath + "\"", 7 Arguments = string.Format("/F=\"{0}\"", filePath) 8 };
- Arguments传参(Windows服务无法使用以下方式发起调用,因为不属于同一session,需要引入底层类库,模拟用户进程以启动新程序)
- Main函数参数传入:为字符串数组,调用时通过一个空格分隔的字符串传入(调试时可通过项目右键->属性->Debug-> Start Options配置)
- WebClient应改用HttpClient
- HttpClient旨在实例化一次,并在应用程序的整个生命周期内重新使用。 实例化每个请求的 HttpClient 类将耗尽大量负载下可用的套接字数。 这将导致 SocketException 错误。
- https://docs.microsoft.com/zh-cn/dotnet/api/system.net.http.httpclient?view=net-6.0
- https://docs.microsoft.com/zh-cn/dotnet/api/system.net.webclient?view=net-6.0
- 本地化
- 新建.resx文件,如PluginMessages.resx、PluginMessages.zh-CN.resx,在其中配置词条译文即可
- 通过本地化文件.词条名引用,如 PluginMessages.Error
throw new ValidationException(PluginMessages.ArgumentsError);
- 应用程序会根据当前Thread.CurrentThread.CurrentUICulture自动选择译文
- 也可通过代码更改
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
- 隐藏Form窗口
protected override CreateParams CreateParams { get { const int WS_EX_APPWINDOW = 0x40000; const int WS_EX_TOOLWINDOW = 0x80; CreateParams cp = base.CreateParams; cp.ExStyle &= (~WS_EX_APPWINDOW); // 不显示在TaskBar cp.ExStyle |= WS_EX_TOOLWINDOW; // 不显示在Alt+Tab return cp; } } private void FrmEditTemplate_Load(object sender, EventArgs eventArgs) { //窗体最小化显示 this.WindowState = FormWindowState.Minimized; //不显示在任务栏中 this.ShowInTaskbar = false;
}
- 新建.resx文件,如PluginMessages.resx、PluginMessages.zh-CN.resx,在其中配置词条译文即可
- 操作完成后自动关闭应用程序
Environment.Exit(Environment.ExitCode);
https://www.jb51.net/article/58816.htm
-
multipart/form 方式提交数据
1 /// <summary> 2 /// 使用multipart/form-data方式上传文件及提交其他数据 3 /// </summary> 4 /// <param name="headers">请求头参数</param> 5 /// <param name="nameValueCollection">键值对参数</param> 6 /// <param name="fileCollection">文件参数:参数名,文件路径</param> 7 /// <returns>接口返回结果</returns> 8 public static string UploadMultipartFormData(string url, string httpMethod, Dictionary<string, string> headers, NameValueCollection nameValueCollection, NameValueCollection fileCollection) 9 { 10 var requestMessage = new HttpRequestMessage(new HttpMethod(httpMethod), url); 11 foreach (var item in headers) 12 { 13 requestMessage.Headers.Add(item.Key, item.Value); 14 } 15 16 using (var content = new MultipartFormDataContent()) 17 { 18 // 键值对参数 19 string[] allKeys = nameValueCollection.AllKeys; 20 foreach (string key in allKeys) 21 { 22 var dataContent = new ByteArrayContent(Encoding.UTF8.GetBytes(nameValueCollection[key])); 23 dataContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") 24 { 25 Name = key 26 }; 27 content.Add(dataContent); 28 } 29 30 //处理文件内容 31 string[] fileKeys = fileCollection.AllKeys; 32 foreach (string key in fileKeys) 33 { 34 byte[] bmpBytes = File.ReadAllBytes(fileCollection[key]); 35 var fileContent = new ByteArrayContent(bmpBytes);//填充文件字节 36 fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") 37 { 38 Name = key, 39 FileName = Path.GetFileName(fileCollection[key]) 40 }; 41 content.Add(fileContent); 42 } 43 44 using (var httpClient = new HttpClient()) 45 { 46 var httpResponse = httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, CancellationToken.None).Result; 47 48 var statusCode = httpResponse.StatusCode; 49 var responseContent = httpResponse.Content; 50 string data = responseContent.ReadAsStringAsync().Result; 51 52 if (statusCode != System.Net.HttpStatusCode.OK) 53 { 54 throw new ValidationException($"{PluginMessages.UploadFailed}: HttpStatusCode={statusCode}, {data}"); 55 } 56 return data;//返回操作结果 57 } 58 } 59 }
- 网页调用本地应用
- 在注册表配置本地应用路径(URL Protocol),a标签或window.open传入注册表配置即可打开,参数写在URL Protocol后面,只能写一个,自定义分隔符解析参数,程序需要UrlDecode参数,网页发布后由于浏览器安全机制,可能每次都会弹是否打开的询问框,如果是https,会对一个复选框,勾选后以后不会弹出询问框。
- 获取正在运行的应用
- 获取正在运行的应用
1 Process[] processes = Process.GetProcesses(); 2 _log.Info("processes.Length = " + processes.Length); 3 List<string> listProc = new List<string>(); 4 foreach (Process p in processes) 5 { 6 if (p.MainWindowHandle.ToInt32() > 0) 7 { 8 if (p.MainWindowTitle.StartsWith("BarTender")) 9 { 10 // 存在已运行的bartender进程时无法打开文档,需要先关闭 11 throw new ValidationException(PluginMessages.BartendIsRunning); 12 } 13 _log.Info("p.MainWindowTitle = " + p.MainWindowTitle); 14 } 15 }
- 其它类
- BackgroundWorker 在单独的线程上执行操作,可以更新Form内控件属性,用BeginInvoke会报跨线程错误。https://docs.microsoft.com/zh-cn/dotnet/api/system.componentmodel.backgroundworker?view=netframework-4.7
- ApplicationSettingsBase 作为派生具体包装类以实现 Window 窗体应用程序中的应用程序设置功能的基类。https://docs.microsoft.com/zh-cn/dotnet/api/system.configuration.applicationsettingsbase?view=dotnet-plat-ext-6.0
- ClickOnce发布程序,自动检查更新并安装 https://blog.csdn.net/tcjiaan/article/details/12285533
- Mutex:互斥锁,可保证只有一个进程实例运行
- SerialPort:串口读写数据
-
一些坑
- Process.GetProcessesByName可能取不到正在运行的进程
- HttpWebRequest.Create(url).GetResponse() 不支持HTTPS,需要额外设置
- Bartender提供的Printers获取到的默认机可能会错误
string _defaultPrinter = new PrintDocument().PrinterSettings.PrinterName;
// 增加Https支持 ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | (SecurityProtocolType)0x300 //Tls11 | (SecurityProtocolType)0xC00; //Tls12