做的虚拟显示器有个功能是设置显示器的Edid,Edid的数据是由应用程序传进来的,为防止每次开机、重启都要进行Edid的设置,需要对EDID信息进行保存。为了保存Edid,我需要在驱动里面操作文件,然而在操作文件的时候会出现一些问题。
读文件如下:
BOOL CvMonitor::SyncEdidFromFile()
{
if (PASSIVE_LEVEL < KeGetCurrentIrql()) {
return FALSE;
}
HANDLE hEdidfile;
UNICODE_STRING filename;
IO_STATUS_BLOCK iostatus;
OBJECT_ATTRIBUTES objectAttributes;
RtlInitUnicodeString(&filename, EDID_FILE_PATH);
InitializeObjectAttributes(&objectAttributes, &filename, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
NTSTATUS ntStatus = ZwCreateFile(&hEdidfile, GENERIC_READ, &objectAttributes, &iostatus, nullptr, FILE_ATTRIBUTE_NORMAL,
0, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, nullptr, 0);
if (!NT_SUCCESS(ntStatus)) {
return FALSE;
}
FILE_STANDARD_INFORMATION fsi;
ntStatus = ZwQueryInformationFile(hEdidfile, &iostatus, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
if (!NT_SUCCESS(ntStatus) || fsi.AllocationSize.QuadPart == 0) {
ZwClose(hEdidfile);
return FALSE;
}
ntStatus = ZwReadFile(hEdidfile,
nullptr, nullptr, nullptr, &iostatus,
m_edid, min(fsi.AllocationSize.LowPart, EDIT_LENGTH),
NULL, nullptr);
ZwClose(hEdidfile);
return NT_SUCCESS(ntStatus);
}
写文件如下:
void CvMonitor::SyncEdidToFile()
{
if (PASSIVE_LEVEL < KeGetCurrentIrql()) {
return;
}
HANDLE hEdidfile;
UNICODE_STRING filename;
IO_STATUS_BLOCK iostatus;
OBJECT_ATTRIBUTES objectAttributes;
RtlInitUnicodeString(&filename, EDID_FILE_PATH);
InitializeObjectAttributes(&objectAttributes, &filename, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
NTSTATUS ntStatus = ZwCreateFile(&hEdidfile, GENERIC_WRITE, &objectAttributes, &iostatus, nullptr, FILE_ATTRIBUTE_NORMAL,
0, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, nullptr, 0);
if (!NT_SUCCESS(ntStatus)) {
return;
}
ZwWriteFile(hEdidfile, nullptr, nullptr, nullptr,
&iostatus, m_edid, sizeof(m_edid), nullptr, nullptr);
ZwClose(hEdidfile);
}
调试过程中发现读文件可以成功,写文件不成功,修改了很多的参数还是不行。
最终发现是由于IRQL导致的,由于我设置Edid是由应用程序下发的,通信方式使用DeviceIoControl,走到驱动层的IRQL为dispatch-level,该level下是不能操作文件的,所以需要进行处理:
void CvMonitor::SetEdid(const BYTE *byEdid, ULONG nLength)
{
/* 需要将中断权限降低,否则无法操作文件 */
KIRQL oldirql = KeGetCurrentIrql();
if (oldirql >= DISPATCH_LEVEL) {
KeLowerIrql(PASSIVE_LEVEL);
}
if (memcmp(byEdid, m_edid, min(sizeof(m_edid), nLength)) != 0) {
memcpy(m_edid, byEdid, min(sizeof(m_edid), nLength));
if (!EdidValid()) {
memcpy(m_edid, s_defaultEdid, sizeof(m_edid));
}
else {
SyncEdidToFile();
}
/* 模拟将显示器插拔,插拔后系统重新读取edid */
Reset();
}
KfRaiseIrql(oldirql);
}
先将IRQL降到PASSIVE_LEVEL,操作完后还原。文件操作就OK了!