gpt4 book ai didi

c# - 如何枚举 NVMe (M2) 驱动器并在 c# 中获取它们的温度?

转载 作者:行者123 更新时间:2023-11-30 16:20:50 42 4
gpt4 key购买 nike

如何枚举 NVMe (M2) 驱动器并在 c# 中获取它们的温度?

它不能通过 WMI 常规查询访问。

有这个 MSFT 引用在 c 中执行此操作,但它相当晦涩,代码不完整:
https://docs.microsoft.com/en-us/windows/desktop/fileio/working-with-nvme-devices#temperature-queries

最佳答案

一个快速的实现(但不是最简单的)是使用 nvme.h winioctl.h ntddstor.h API 并在 c# 中与它互操作。这是一个完整的实现。

申报方:

#include <nvme.h>
#include <winioctl.h>
#include <ntddstor.h>

#ifndef Pinvoke
#define Pinvoke extern "C" __declspec(dllexport)
#endif

typedef void(__stdcall *MessageChangedCallback)(const wchar_t* string);

class __declspec(dllexport) NVmeQuery final
{
public:
NVmeQuery(MessageChangedCallback managedDelegate);
~NVmeQuery();
template <class ... T>
auto LogMessage(T&& ... args) -> void;
auto GetTemp(const wchar_t* nvmePath) -> unsigned long;

MessageChangedCallback LogMessageChangedCallback{};

PNVME_HEALTH_INFO_LOG SmartHealthInfo{};
PSTORAGE_PROPERTY_QUERY query{};
PSTORAGE_PROTOCOL_SPECIFIC_DATA protocolSpecificData{};
PSTORAGE_PROTOCOL_DATA_DESCRIPTOR protocolDataDescriptor{};
};

定义侧:
我用来向托管端追溯互操作错误消息的功能;这对于理解哪里出了问题、如何/哪里出了问题至关重要:
template<class ...T>
auto NVmeQuery::LogMessage(T&&... args) -> void
{
wchar_t updatedMessage[256];
swprintf_s(updatedMessage, forward<T>(args)...);
if (LogMessageChangedCallback != nullptr)
LogMessageChangedCallback(updatedMessage);
}

核心功能,设计起来并不简单。它有 4 个部分:
1. 获取 NVMe 驱动器的句柄
2. 准备查询
3.做查询
4.检查和传输结果
auto NVmeQuery::GetTemp(const wchar_t* nvmePath) -> unsigned long
{
auto nvmeHandle = CreateFile(nvmePath, 0, 0,
0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
{
auto lastErrorID = GetLastError();
if (lastErrorID != 0)
{
LPVOID errorBuffer{};
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, lastErrorID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorBuffer, 0, nullptr);
LogMessage(L"Query: ERROR creating handle to NVMe [%s]: %d, %s", nvmePath, lastErrorID, errorBuffer);
}
}

unsigned long bufferLength = FIELD_OFFSET(STORAGE_PROPERTY_QUERY, AdditionalParameters)
+ sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + NVME_MAX_LOG_SIZE;
void *buffer = malloc(bufferLength);
ZeroMemory(buffer, bufferLength);

query = (PSTORAGE_PROPERTY_QUERY)buffer;
protocolDataDescriptor = (PSTORAGE_PROTOCOL_DATA_DESCRIPTOR)buffer;
protocolSpecificData = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters;

query->PropertyId = StorageDeviceProtocolSpecificProperty;
query->QueryType = PropertyStandardQuery;

protocolSpecificData->ProtocolType = ProtocolTypeNvme;
protocolSpecificData->DataType = NVMeDataTypeLogPage;
protocolSpecificData->ProtocolDataRequestValue = NVME_LOG_PAGE_HEALTH_INFO;
protocolSpecificData->ProtocolDataRequestSubValue = 0;
protocolSpecificData->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
protocolSpecificData->ProtocolDataLength = sizeof(NVME_HEALTH_INFO_LOG);

unsigned long returnedLength{};

继续,实际查询,然后是杂项检查:
    auto result = DeviceIoControl(nvmeHandle, IOCTL_STORAGE_QUERY_PROPERTY,
buffer, bufferLength,
buffer, bufferLength,
&returnedLength, nullptr);

if (!result || returnedLength == 0)
{
auto lastErrorID = GetLastError();
LPVOID errorBuffer{};
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, lastErrorID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorBuffer, 0, nullptr);
LogMessage(L"Query: drive path: %s, nvmeHandle %lu", nvmePath, nvmeHandle);
LogMessage(L"Query: ERROR DeviceIoControl 0x%x %s", lastErrorID, errorBuffer);
}

if (protocolDataDescriptor->Version != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR) ||
protocolDataDescriptor->Size != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR))
{
LogMessage(L"Query: Data descriptor header not valid (size of descriptor: %llu)", sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR));
LogMessage(L"Query: DataDesc: version %lu, size %lu", protocolDataDescriptor->Version, protocolDataDescriptor->Size);
}

protocolSpecificData = &protocolDataDescriptor->ProtocolSpecificData;
if (protocolSpecificData->ProtocolDataOffset < sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) ||
protocolSpecificData->ProtocolDataLength < sizeof(NVME_HEALTH_INFO_LOG))
LogMessage(L"Query: ProtocolData Offset/Length not valid");

SmartHealthInfo = (PNVME_HEALTH_INFO_LOG)((PCHAR)protocolSpecificData + protocolSpecificData->ProtocolDataOffset);
CloseHandle(nvmeHandle);
auto temp = ((ULONG)SmartHealthInfo->Temperature[1] << 8 | SmartHealthInfo->Temperature[0]) - 273;
return temp;
} // end of GetTemp

对于互操作:
Pinvoke auto __stdcall New(MessageChangedCallback managedDelegate) -> NVmeQuery*
{
return new NVmeQuery(managedDelegate);
}

Pinvoke auto GetTemp(NVmeQuery* p, const wchar_t* nvmePath) -> unsigned long
{
return p->GetTemp(nvmePath);
}

和 c# 方面:
public static class NVMeQuery
{
[DllImport("NVMeQuery.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr New(InteropBase.AssetCallback callback);
[DllImport("NVMeQuery.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern ulong GetTemp(IntPtr p, IntPtr drivePath);
}

public class NVMeQueries : InteropBase
{
public NVMeQueries()
{
_ptr = NVMeQuery.New(_onAssetErrorMessageChanged);
}

public ulong GetTemp() => GetTemp(@"\\.\PhysicalDrive4");

public ulong GetTemp(string drivePath)
{
var strPtr = Marshal.StringToHGlobalAuto(drivePath);
var result = NVMeQuery.GetTemp(_ptr, strPtr);
Marshal.FreeHGlobal(strPtr);
return result;
}
}

我用于互操作的通用基类:
public class InteropBase : INotifyPropertyChanged
{
protected IntPtr _ptr;
protected readonly AssetCallback _onAssetErrorMessageChanged;

public delegate void AssetCallback(IntPtr strPtr);

public List<string> LogMessages { get; private set; } = new List<string>();

public InteropBase()
{
_onAssetErrorMessageChanged = LogUpdater;
}

private unsafe void LogUpdater(IntPtr strPtr)
{
var LastLogMessage = new string((char*)strPtr);
LogMessages.Add(LastLogMessage);
OnPropertyChanged(nameof(LogMessages));
}

public event PropertyChangedEventHandler PropertyChanged;

[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

就我而言,我想查询的 NVMe 驱动器是第四个物理驱动器。我们得到所有这些:
Get-WmiObject Win32_DiskDrive

在我的情况下,这将给出:
Partitions : 4
DeviceID : \\.\PHYSICALDRIVE4
Model : Samsung SSD 970 EVO Plus 1TB
Size : 1000202273280
Caption : Samsung SSD 970 EVO Plus 1TB

评论

这个实现非常快(没有 LogMessage 调用时不到 1ms);您可以定义和填写自己的结构,以获取其他相关信息;在这种情况下,该结构必须保存在 native 类的字段中(例如,在本例中为 SmartHealthInfo),并且查询将只传输指向该结构的指针。

关于c# - 如何枚举 NVMe (M2) 驱动器并在 c# 中获取它们的温度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55132739/

42 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com