最近在开发一款与ps4有关的桌面辅助软件,通过它可以获取ps4上游戏的存档核心。整理记录一下学到的一些在使用C#语言开发桌面软件中学到的知识点.
字符串IP信息转化为IPAddress实例
IPAddress.Parse(ipstr);
判断字符串是否为IP地址
IPAddress.TryParse(ipstr, out, _); //下划线可以忽略不适用的变量
一个特定的IP地址和端口的组合,多将socket绑定到本地地址或者将socket绑定到非本地地址
enp = new IPEndPoint(addr, port);
sock = new Socket(enp.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
sock.NoDelay = true; //该值指定流 Socket 是否正在使用 Nagle 算法。
sock.ReceiveBufferSize = NET_MAX_LENGTH; //它指定 Socket 接收缓冲区的大小。
sock.SendBufferSize = NET_MAX_LENGTH; //该值指定 Socket 发送缓冲区的大小。
sock.ReceiveTimeout = -1; //该值指定之后同步 Receive 调用将超时的时间长度。 -1 无限大
IAsyncResult result = sock.BeginConnect(enp, null, null); //开始一个对远程主机连接的异步请求。
result.AsyncWaitHandle.WaitOne(3000); //异步操作的状态。
IsConnected = sock.Connected; //该值指示 Socket 是在上次 Send 还是 Receive 操作时连接到远程主机。
sock.Send(bytes, left, SocketFlags.None); //发送数据
sock.Receive(status, 4, SocketFlags.NOne); //接收数据
判定字符串是否为空
string.IsNullOrEmpty(str);
字符串前加@表示强制不转义
@"D:\Github\Readme.md";
使用$符号允许字符串里使用变量
$"my name is {name}";
字符串插入
msg = msg.Insert(0," ");
数字字符串转化为16进制字节数组
private static byte[] strToToHexByte(string hexString)
{
hexString = hexString.Replace(" ", "");
if ((hexString.Length % 2) != 0)
hexString += " ";
byte[] returnBytes = new byte[hexString.Length / 2];
for (int i = 0; i < returnBytes.Length; i++)
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
return returnBytes;
}
字节倒序,大端变小端模式
Array.Reverse(bytes);
生成随机字符串
private static string GetRandomString()
{
return SystemTime.Now.ToString("yyyyMMdd-HHmmss") + Guid.NewGuid().ToString("n").Substring(0, 6);
}
声明常量,可以全局使用:
public const int WM = 0x123;
声明静态类,可以是他变得全局使用:
private static RestClient Client = new RestClient("https://www.izhangbo/api/demo");
声明变量,你可以赋值,也可以不赋值:
private int taskId;
private username = "demoname";
属性设置、赋值器
private bool IsConnected {get; private set;} = true;
使用var定义变量时有以下四个特点:
1. 必须在定义时初始化。也就是必须是var s = “abcd”形式,而不能是如下形式: var s; s = “abcd”;
2. 一但初始化完成,就不能再给变量赋与初始化值类型不同的值了。
3. var要求是局部变量。
4. 使用var定义变量和object不同,它在效率上和使用强类型方式定义变量完全一样。
string[] commandLineArgs = Environment.GetCommandLineArgs();
if (commandLineArgs.Length == 3 && commandLineArgs[1] == "-ip")
{
ipTextBox.Text = commandLineArgs[2];
cmdstart = true;
}
系统框
MessageBox.Show("弹框提示的消息内容");
复制内容到剪贴板
Clipboard.SetText(copystring)
系统程序退出
Environment.Exit(0)
异常捕获
try{
...
}catch(Exception ex){
Log($"get some error:{ex.Message}");
Log(ex.Message + Environment.NewLine + ex.StackTrace, "error");
}
// 不要抛出已有异常,会丢失堆栈信息
catch(Exception ex){
throw ex;
}
// 应该这么写
catch(Exception ex){
throw new Exception(ex.Message,ex);
}
文件是否存在:
File.Exists("../demo.text")
目录是否存在
Directory.Exists(@"./file/")
创建目录
Directory.CreateDirectory(@"./file/");
获取路径文件名
Path.GetFileNameWithoutExtension(demo_file)
获取文件的路径
Path.GetDirectoryName(demo_file)
文件删除
File.Delete(demo_file)
删除目录
DirectoryInfo tempcoredata_di = new DirectoryInfo(tempcoredata_dir);
if (Directory.Exists(tempcoredata_dir))
{
tempcoredata_di.Delete(true);
}
获取目录中的文件信息
foreach (FileInfo f in filetemp_di.GetFiles("*.bin", SearchOption.TopDirectoryOnly))
{
//这里只查找后缀是bin的文件
}
直接读取同级目录下的文件内容
content = File.ReadAllText("ip.log");
保存内容到同级目录文件里
File.WriteAllText("ip.log",content);
追加内容到文件中
File.AppendAllText(@"ip.log", $"{logstr}{Environment.NewLine}");
判断目录中是否含某个文件
private bool haveFile(string path)
{
string[] strings= Directory.GetFileSystemEntries(path);
foreach (string s in strings)
{
if (File.Exists(s))
{
return true;
}
else
{
if (haveFile(s))
{
return true;
}
else
{
continue;
}
}
}
return false;
}
Async是C#的修饰符,可以用来修饰method或者expression,用来声明一个异步的method或者expression一个异步的method仅支持以下返回值:
Task
Task<TResult> // 泛型
void // 定义void返回仅仅是为了跟普通函数保持兼容
任何包含可访问GetAwaiter()方法的类型(从C# 7.0开始支持)
Async方法在执行的时候,开始是以同步的方式执行(即在调用方的thread里跑的),直到遇到await关键字,从await关键字开始,C#会另起一个thread来执行await后面的代码。如果Async方法里面的代码没有包含await的代码会怎么样?那整个函数就会同步执行,跟普通函数没差别。编译器也会给你个警告。
static void Main(string[] args)
{
Console.WriteLine("Main start, Thread ID is " + Thread.CurrentThread.ManagedThreadId);
var teaResult = PrepareteaAsync();
Console.WriteLine("Main waiting, Thread ID is " + Thread.CurrentThread.ManagedThreadId);
teaResult.GetAwaiter().GetResult();
}
private static async Task PrepareteaAsync()
{
Console.WriteLine("PrepareteaAsync start, Thread ID is " + Thread.CurrentThread.ManagedThreadId);
await TimeConsumingMethod(3000);
Console.WriteLine("PrepareteaAsync end, Thread ID is " + Thread.CurrentThread.ManagedThreadId);
}
using
部分它可以让我使用windows的各种底层接口库,也可以引入第三方库使用,比如我使用的json库、日志库:
using Newtongsoft.Json.Linq;
using NLog;
using NLog.Windows.Forms;
不过第三方库需要安装对应的Nuget
程序包,类似与python的pip
using还用来操作资源流,数据流、文件流啥的,他可以保证在资源使用完毕后自动关闭资源,这样很人性、很安全,我喜欢。
using (StreamWriter sw = new StreamWriter("config.json"))
{
sw.WriteLine(DemoJsonString)
}
从程序集里调取文件并使用
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
using (var stream = assembly.GetManifestResourceStream("PS4Saves.ps4debug.bin"))
partial
它可以让开发人员在写类的时候根据代码的功能模块分成多个文件来写,比如
Form.cs 里写所有的逻辑
public partical class Form{...}
Form.Designer.cs 里写UI界面代码
partical class Form{}
他们的代码最后都会合并在一个Form类里。
override 表示对应的方法为父类的重写方法。
“重写”:在父类中有一个方法,但是子类中也需要这个方法的名称,而且子类中的方法的逻辑与父类方法中的逻辑不相同,则需要在子类中对这个方法进行重写
读取:
StreamReader read = new StreamReader("demo.json");
string jsonstr = read.ReadToEnd();
configinfo = JsonConvert.DeserializeObject<JsonOBJ>(jsonstr);
//JsonOBJ是定义好的json结构体
read.close(); //这里可以使用using结构
string ip = configinfo.ip;
写入文件:
config.ip = ip;
config.name = name;
string jsonstring = JsonConvert.SerializeObject(config);
using (StreamWriter sw = new StreamWriter("config.json"))
{
sw.WriteLine(jsonstring);
}
调用类自身成员,不用使用this 直接写名称
class Main{
private int pid;
public Main(su){
pid = su.pid; //这里不用写this.pid = su.pid,但为了阅读理解我还是觉得写上为好。
}
}
获取当前时间
var dateAndTime = DateTime.Now;
格式化输出
var logStr = $"|{dateAndTime:MM/dd/yyyy} {dateAndTime:hh:mm:ss tt}| |{msg}|";
List<User> users = new List<User>();
users.Add(new User { id = id, name = name.Replace("\0", "") });
users.ToArray(); //List 变 数组
数组的循环遍历
foreach (User user in users)
{
if (user.name == name)
{
return user;
}
}
使用Marshal将一个结构体序列化后写入内存,供另一个进程函数使用。
int size = Marshal.SizeOf(obj);
byte[] bytes = new byte[size];
intPtr ptr = Marshal.AllocHGlogal(size); //分配size长度的内存,获得这段内存的地址指针
Marshal.StructureToPtr(obj,ptr,false); //将结构信息写入内存中
Marshal.Copy(ptr, bytes, 0, size); //拷贝内存中的数据到bytes中
Marshal.FreeHGlobal(ptr); //释放内存
内存(字节数据)截取
public static byte[] SubArray(byte[] data, int offset, int length)
{
byte[] bytes = new byte[length];
Buffer.BlockCopy(data, offset, bytes, 0, length); //截取data offset起始位置,length长度的内容到bytes中。
return bytes;
}