关于七牛qetag算法的一些记录

背景 今天做七牛相关接口开发的时候发现七牛的文件查询列表返回,有个hash字段,但是不知道是怎么进行计算的。后来查询到七牛的官方仓库 qetag .官方对这个算法的描述是这样的: qetag 是一个计算文件在七牛云存储上的 hash 值(也是文件下载时的 etag 值)的实用程序。 七牛的 hash/etag 算法是公开的。算法大体如下: 如果你能够确认文件 <= 4M,那么 hash = UrlsafeBase64([0x16, sha1(FileContent)])。也就是,文件的内容的sha1值(20个字节),前面加一个byte(值为0x16),构成 21 字节的二进制数据,然后对这 21 字节的数据做 urlsafe 的 base64 编码。 如果文件 > 4M,则 hash = UrlsafeBase64([0x96, sha1([sha1(Block1), sha1(Block2), …])]),其中 Block 是把文件内容切分为 4M 为单位的一个个块,也就是 BlockI = FileContent[I*4M:(I+1)*4M]。 为何需要公开 hash/etag 算法?这个和 “消重” 问题有关,详细见: https://developer.qiniu.com/kodo/kb/1365/how-to-avoid-the-users-to-upload-files-with-the-same-key http://segmentfault.com/q/1010000000315810 为何在 sha1 值前面加一个byte的标记位(0x16或0x96)? 0x16 = 22,而 2^22 = 4M。所以前面的 0x16 其实是文件按 4M 分块的意思。 0x96 = 0x80 | 0x16。其中的 0x80 表示这个文件是大文件(有多个分块),hash 值也经过了2重的 sha1 计算。 语言封装 C# 实现 基于官方仓库的csharp代码做了部分修改 ...

2024-01-22 · 3 分钟 · czyt

如何使用C#检查网络连接

调用Windows API 在C#中可以通过Pinvoke调用windows API方式来进行网络连接的检查: [DllImport("wininet.dll", SetLastError=true)] extern static bool InternetGetConnectedState(out int lpdwFlags, int dwReserved); [Flags] enum ConnectionStates { Modem = 0x1, LAN = 0x2, Proxy = 0x4, RasInstalled = 0x10, Offline = 0x20, Configured = 0x40, } MSDN地址 https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetgetconnectedstate 对应的返回值Description如下: Value Meaning INTERNET_CONNECTION_CONFIGURED0x40 Local system has a valid connection to the Internet, but it might or might not be currently connected. INTERNET_CONNECTION_LAN0x02 Local system uses a local area network to connect to the Internet. INTERNET_CONNECTION_MODEM0x01 Local system uses a modem to connect to the Internet. INTERNET_CONNECTION_MODEM_BUSY0x08 No longer used. INTERNET_CONNECTION_OFFLINE0x20 Local system is in offline mode. INTERNET_CONNECTION_PROXY0x04 Local system uses a proxy server to connect to the Internet. INTERNET_RAS_INSTALLED0x10 Local system has RAS installed. 例子 : ...

2023-08-31 · 2 分钟 · czyt

使用C#以编程方式切换Windows专注模式

缘起 最近需要以编程方式调用windows api实现windows10专注模式的切换,但是Google一圈,没有现成代码。找到的相关帖子要么是cpp的要么是rust的,而且是undocument的Windows api。 Csharp调用 以下是完整代码 public static class FocusAssistToogle { private const string NtdllDlDll = "ntdll.dll"; private const uint DataBufferSize = 4; private static readonly byte[] DisableDataBuf = { 0x00, 0x00, 0x00, 0x00 }; // 01仅优先通知 02 仅限闹钟 private static readonly byte[] EnableDataBuf = { 0x02, 0x00, 0x00, 0x00 }; [DllImport(NtdllDlDll, SetLastError = true)] private static extern int ZwUpdateWnfStateData( ref WnfSWnfStateName sWnfStateName, byte[] buffer, uint bufferSize, IntPtr previousStateData, IntPtr currentStateData, uint previousStateDataSize, uint currentStateDataSize); [StructLayout(LayoutKind.Sequential)] private struct WnfSWnfStateName { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public uint[] Data; } private static WnfSWnfStateName _wnfSWnfShelQuietMomentShellModeChanged = new WnfSWnfStateName { Data = new uint[] { 0xa3bf5075, 0xd83063e } }; public static bool EnableFocusMode() { int result = ZwUpdateWnfStateData(ref _wnfSWnfShelQuietMomentShellModeChanged, EnableDataBuf, DataBufferSize, IntPtr.Zero, IntPtr.Zero, 0, 0); return result == 0; } public static bool DisableFocusMode() { int result = ZwUpdateWnfStateData(ref _wnfSWnfShelQuietMomentShellModeChanged, DisableDataBuf, DataBufferSize, IntPtr.Zero, IntPtr.Zero, 0, 0); return result == 0; } } 调用时,最好先调用DisableFoucusMode方法,再调用EnableFocusMode,参考StackOverflow上的说明 ...

2023-07-24 · 2 分钟 · czyt

使用C#连接wifi

需要安装库 Managed Native Wifi 获取Wifi列表 示例代码 private List<AvailableNetworkPack> GetAvaliableNetworks() { var networks = new List<AvailableNetworkPack>(); foreach (var network in NativeWifi.EnumerateAvailableNetworks()) { if (!networks.Contains(network)) { networks.Add(network); } } return networks; } 连接wifi 创建Profile 应该支持这些类型open, WEP and WPA-PSK的网络 private static string CreateSecurityWifiProfile(string ssid, string password) { string hex = CreateHexSSIDName(ssid); return string.Format(@"<?xml version=""1.0""?> <WLANProfile xmlns=""http://www.microsoft.com/networking/WLAN/profile/v1""> <name>{0}</name> <SSIDConfig> <SSID> <hex>{2}</hex> <name>{0}</name> </SSID> </SSIDConfig> <connectionType>ESS</connectionType> <connectionMode>auto</connectionMode> <MSM> <security> <authEncryption> <authentication>WPA2PSK</authentication> <encryption>AES</encryption> <useOneX>false</useOneX> </authEncryption> <sharedKey> <keyType>passPhrase</keyType> <protected>false</protected> <keyMaterial>{1}</keyMaterial> </sharedKey> </security> </MSM> <MacRandomization xmlns=""http://www.microsoft.com/networking/WLAN/profile/v3""> <enableRandomization>false</enableRandomization> </MacRandomization> </WLANProfile>", ssid, password, hex); } private static string CreateOpenWifiProfile(string ssid) { string hex = CreateHexSSIDName(ssid); return string.Format(@"<?xml version=""1.0""?> <WLANProfile xmlns=""http://www.microsoft.com/networking/WLAN/profile/v1""> <name>{0}</name> <SSIDConfig> <SSID> <hex>{1}</hex> <name>{0}</name> </SSID> </SSIDConfig> <connectionType>ESS</connectionType> <connectionMode>auto</connectionMode> <MSM> <security> <authEncryption> <authentication>open</authentication> <encryption>none</encryption> <useOneX>false</useOneX> </authEncryption> </security> </MSM> </WLANProfile>", ssid, hex); } private static string CreateHexSSIDName(string ssid) { byte[] bytes = System.Text.Encoding.UTF8.GetBytes(ssid); return BitConverter.ToString(bytes).Replace("-", ""); } 连接到wifi var networks = GetAvaliableNetworks(); var network = networks.First(n => n.Ssid.ToString() == "gophers"); var profile = NativeWifi.EnumerateProfiles().FirstOrDefault(x => x.Name == "NTSP_CD"); if (profile is null) { var profileXml = CreateProfile("gophers", "gopherspwd"); // create a profile var profileResult = NativeWifi.SetProfile(network.Interface.Id, ProfileType.PerUser, profileXml, "AES", true); } // do connect var wasConnected = NativeWifi.ConnectNetwork(network.Interface.Id, network.Ssid.ToString(), network.BssType); 附录 一些常用的profile配置xml 摘自 项目https://github.com/DigiExam/simplewifi/ ...

2023-04-24 · 4 分钟 · czyt

我和AI讨论的一些C#话题

很久没有用C#,最近需要使用。下面是跟AI问的一些C#的问题,未作整理。 基础 Span类型 c# Span的应用场景及具体例子 Span是一种新的引用类型,它允许存储和操作连续的内存块。它特别适用于那些需要高效处理内存块的应用场景,例如字符串处理、IO操作和序列化/反序列化。 下面是一些具体的例子: 字符串处理:Span可以用于快速地操作字符串,例如从字符串中截取子串、替换子串、查找子串等。下面是一个例子: string str = "hello world"; Span<char> span = str.AsSpan(6, 5); // 从第6个字符开始,截取长度为5的子串 IO操作:Span可以用于将IO操作映射到内存中,以提高性能。下面是一个例子: using (FileStream stream = File.OpenRead("file.txt")) { byte[] buffer = new byte[1024 * 1024]; while (stream.Read(buffer) > 0) { Span<byte> span = buffer.AsSpan(); // 将字节数组映射为Span // 处理Span } } 序列化/反序列化:Span可以用于快速地序列化和反序列化数据,尤其是在网络传输等场景中。下面是一个例子: public static void Serialize<T>(T value, Span<byte> buffer) where T : struct { unsafe { fixed (byte* ptr = buffer) { *(T*)ptr = value; // 将值拷贝到Span中 } } } public static T Deserialize<T>(Span<byte> buffer) where T : struct { unsafe { fixed (byte* ptr = buffer) { return *(T*)ptr; // 从Span中读取值 } } } 异步 c# SynchronizationContext的使用场景及例子 SynchronizationContext是一个抽象类,定义了一种同步上下文的机制,允许代码在指定的线程中执行。通常情况下,SynchronizationContext的实现会将执行代码的操作封装成一个任务,并将该任务分配到与指定的上下文相关的线程中执行。 SynchronizationContext通常用于以下情况: Windows Forms应用程序中的UI线程处理 在Windows Forms应用程序中,UI线程负责处理用户界面操作。如果其他线程尝试在UI线程之外更新用户界面,则可能会引发异常。这时 ,可以使用WindowsFormsSynchronizationContext类,该类可以将执行代码封装到UI线程上下文中,并在其相关的线程上执行。 例如: textBox1.Invoke(new Action(() => { textBox1.Text = "Hello World"; })); ASP.NET Web应用程序中的请求处理 在ASP.NET Web应用程序中,每个请求都在单独的线程上执行,处理请求时可以使用AspNetSynchronizationContext类将代码封装到请求 线程上下文中,并在其相关的线程上执行。 例如: ...

2023-03-24 · 3 分钟 · czyt

C#扫码枪输入Hook

摘自网络,测试可用 public class ScanerHook { public delegate void ScanerDelegate(ScanerCodes codes); public event ScanerDelegate ScanerEvent; //private const int WM_KEYDOWN = 0x100;//KEYDOWN //private const int WM_KEYUP = 0x101;//KEYUP //private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN //private const int WM_SYSKEYUP = 0x105;//SYSKEYUP //private static int HookProc(int nCode, Int32 wParam, IntPtr lParam); private int hKeyboardHook = 0;//声明键盘钩子处理的初始值 private ScanerCodes codes = new ScanerCodes();//13为键盘钩子 //定义成静态,这样不会抛出回收异常 private static HookProc hookproc; delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] //设置钩子 private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] //卸载钩子 private static extern bool UnhookWindowsHookEx(int idHook); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] //继续下个钩子 private static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam); [DllImport("user32", EntryPoint = "GetKeyNameText")] private static extern int GetKeyNameText(int IParam, StringBuilder lpBuffer, int nSize); [DllImport("user32", EntryPoint = "GetKeyboardState")] //获取按键的状态 private static extern int GetKeyboardState(byte[] pbKeyState); [DllImport("user32", EntryPoint = "ToAscii")] //ToAscii职能的转换指定的虚拟键码和键盘状态的相应字符或字符 private static extern bool ToAscii(int VirtualKey, int ScanCode, byte[] lpKeySate, ref uint lpChar, int uFlags); //int VirtualKey //[in] 指定虚拟关键代码进行翻译。 //int uScanCode, // [in] 指定的硬件扫描码的关键须翻译成英文。高阶位的这个值设定的关键,如果是(不压) //byte[] lpbKeyState, // [in] 指针,以256字节数组,包含当前键盘的状态。每个元素(字节)的数组包含状态的一个关键。如果高阶位的字节是一套,关键是下跌(按下)。在低比特,如/果设置表明,关键是对切换。在此功能,只有肘位的CAPS LOCK键是相关的。在切换状态的NUM个锁和滚动锁定键被忽略。 //byte[] lpwTransKey, // [out] 指针的缓冲区收到翻译字符或字符。 //uint fuState); // [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise. [DllImport("kernel32.dll")] //使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效 public static extern IntPtr GetModuleHandle(string name); public ScanerHook() { } public bool Start() { if (hKeyboardHook == 0) { hookproc = new HookProc(KeyboardHookProc); //GetModuleHandle 函数 替代 Marshal.GetHINSTANCE //防止在 framework4.0中 注册钩子不成功 IntPtr modulePtr = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); //WH_KEYBOARD_LL=13 //全局钩子 WH_KEYBOARD_LL // hKeyboardHook = SetWindowsHookEx(13, hookproc, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0); hKeyboardHook = SetWindowsHookEx(13, hookproc, modulePtr, 0); } return (hKeyboardHook != 0); } public bool Stop() { if (hKeyboardHook != 0) { bool retKeyboard = UnhookWindowsHookEx(hKeyboardHook); hKeyboardHook = 0; return retKeyboard; } return true; } private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam) { EventMsg msg = (EventMsg)Marshal.PtrToStructure(lParam, typeof(EventMsg)); codes.Add(msg); if (ScanerEvent != null && msg.message == 13 && msg.paramH > 0 && !string.IsNullOrEmpty(codes.Result)) { ScanerEvent(codes); } return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam); } public class ScanerCodes { private int ts = 100; // 指定输入间隔为300毫秒以内时为连续输入 private List<List<EventMsg>> _keys = new List<List<EventMsg>>(); private List<int> _keydown = new List<int>(); // 保存组合键状态 private List<string> _result = new List<string>(); // 返回结果集 private DateTime _last = DateTime.Now; private byte[] _state = new byte[256]; private string _key = string.Empty; private string _cur = string.Empty; public EventMsg Event { get { if (_keys.Count == 0) { return new EventMsg(); } else { return _keys[_keys.Count - 1][_keys[_keys.Count - 1].Count - 1]; } } } public List<int> KeyDowns { get { return _keydown; } } public DateTime LastInput { get { return _last; } } public byte[] KeyboardState { get { return _state; } } public int KeyDownCount { get { return _keydown.Count; } } public string Result { get { if (_result.Count > 0) { return _result[_result.Count - 1].Trim(); } else { return null; } } } public string CurrentKey { get { return _key; } } public string CurrentChar { get { return _cur; } } public bool isShift { get { return _keydown.Contains(160); } } public void Add(EventMsg msg) { #region 记录按键信息 // 首次按下按键 if (_keys.Count == 0) { _keys.Add(new List<EventMsg>()); _keys[0].Add(msg); _result.Add(string.Empty); } // 未释放其他按键时按下按键 else if (_keydown.Count > 0) { _keys[_keys.Count - 1].Add(msg); } // 单位时间内按下按键 else if ((DateTime.Now - _last).TotalMilliseconds < ts) { _keys[_keys.Count - 1].Add(msg); } // 从新记录输入内容 else { _keys.Add(new List<EventMsg>()); _keys[_keys.Count - 1].Add(msg); _result.Add(string.Empty); } #endregion _last = DateTime.Now; #region 获取键盘状态 // 记录正在按下的按键 if (msg.paramH == 0 && !_keydown.Contains(msg.message)) { _keydown.Add(msg.message); } // 清除已松开的按键 if (msg.paramH > 0 && _keydown.Contains(msg.message)) { _keydown.Remove(msg.message); } #endregion #region 计算按键信息 int v = msg.message & 0xff; int c = msg.paramL & 0xff; StringBuilder strKeyName = new StringBuilder(500); if (GetKeyNameText(c * 65536, strKeyName, 255) > 0) { _key = strKeyName.ToString().Trim(new char[] { ' ', '\0' }); GetKeyboardState(_state); if (_key.Length == 1 && msg.paramH == 0)// && msg.paramH == 0 { // 根据键盘状态和shift缓存判断输出字符 _cur = ShiftChar(_key, isShift, _state).ToString(); _result[_result.Count - 1] += _cur; } else if (_key.Length == 5 && msg.paramH == 0 && msg.paramL == 78 && msg.message == 107)// && msg.paramH == 0 { // 根据键盘状态和shift缓存判断输出字符 _cur = Convert.ToChar('+').ToString(); _result[_result.Count - 1] += _cur; } else { _cur = string.Empty; } } #endregion } private char ShiftChar(string k, bool isShiftDown, byte[] state) { bool capslock = state[0x14] == 1; bool numlock = state[0x90] == 1; bool scrolllock = state[0x91] == 1; bool shiftdown = state[0xa0] == 1; char chr = (capslock ? k.ToUpper() : k.ToLower()).ToCharArray()[0]; if (isShiftDown) { if (chr >= 'a' && chr <= 'z') { chr = (char)((int)chr - 32); } else if (chr >= 'A' && chr <= 'Z') { if (chr == 'Z') { string s = ""; } chr = (char)((int)chr + 32); } else { string s = "`1234567890-=[];',./"; string u = "~!@#$%^&*()_+{}:\"<>?"; if (s.IndexOf(chr) >= 0) { return (u.ToCharArray())[s.IndexOf(chr)]; } } } return chr; } } public struct EventMsg { public int message; public int paramL; public int paramH; public int Time; public int hwnd; } } 某些情况下,特别是使用类似像微软拼音之类的中文输入法时,扫码枪会出现字符被输入法截获,显示不是很友好,所以需要使用InputMethod.SetPreferredImeState(target, InputMethodState.Off)来关闭输入法,只能输入英文,待业务调用完成后再切换回来。target可以是业务的主界面窗口,也可以是foucus的输入框。

2022-11-01 · 4 分钟 · czyt

C# dllimport 备忘录

dllImport的入口点问题 通过Dependencies 查询Dll对应方法的EntryPoint 然后在dllimport的attribute中显式申明EntryPoint [DllImport("demo.dll", SetLastError = true,EntryPoint ="??0DemoManager@EcgParser@Gfeit@@AEAA@XZ")] public static extern IntPtr DemoManager(); 导入类方法的问题 最好的方式还是使用C++构造wrapper,然后通过windowsApi的方式调用Pinvoke 参考链接 swig nuget swig C++/C# interoperability Working with C++ Interface Classes from C# Call function in unmanaged DLL from C# and pass custom data types [Marshal] SWIG and C# Example for SWIG to wrap C++ library in .Net 6

2022-10-09 · 1 分钟 · czyt

为Kratos prtobuf文件添加多种编译输出

Csharp 安装Grpc.tools https://www.nuget.org/packages/Grpc.Tools/ 下载解压 nupkg文件(改扩展名为zip),也可以使用附件的7z包 解压 找到tools中对应系统架构的软件,设置下环境变量,让系统可以找到就行。 Linux 需要创建一个符号链接 ln -s `which grpc_csharp_plugin` /usr/bin/protoc-gen-grpc-csharp 修改Kratos项目的Make文件 在api这个make任务中添加下面内容 --csharp_out=./api/pipe/v1 \ --grpc-csharp_out=./api/pipe/v1 \ 完整内容为 .PHONY: api # generate api proto api: protoc --proto_path=./api \ --proto_path=./third_party \ --go_out=paths=source_relative:./api \ --go-http_out=paths=source_relative:./api \ --go-grpc_out=paths=source_relative:./api \ --csharp_out=./api/pipe/v1 \ --grpc-csharp_out=./api/pipe/v1 \ --openapi_out==paths=source_relative:. \ 参考 https://github.com/grpc/grpc/blob/master/src/csharp/BUILD-INTEGRATION.md 📎tools.7z Python 安装必要包 pip install grpclib protobuf 查询路径 which protoc-gen-grpclib_python 或者 which protoc-gen-python_grpc我这里返回信息如下: ➜ czyt which protoc-gen-grpclib_python /usr/sbin/protoc-gen-grpclib_python 如法炮制,创建软链接 ln -s /usr/sbin/protoc-gen-grpclib_python /usr/sbin/protoc-gen-grpc_python 修改Makefile 添加下面的内容,再执行make api生成api即可。 --python_out=./api \ --grpc_python_out=./api \ Dart 安装插件 dart pub global activate protoc_plugin 将插件工具加入环境变量 export PATH="$PATH:$HOME/.pub-cache/bin" 脚本添加--dart_out=选项 --dart_out=./api \ TypeScript 纯typescript类 ts-proto typescript类+服务 安装proto工具 go get go.einride.tri p tech/protoc-gen-typescript-http ...

2022-04-08 · 1 分钟 · czyt