IoCreateSymbolicLink函数的作用以及符号链接、设备名称之间的关系

本文介绍了Windows驱动程序中IoCreateSymbolicLink函数的作用及使用方法。该函数用于创建符号链接,使用户空间的应用程序能够通过友好的名字访问内核空间的设备对象。文章还详细解释了设备对象名称和符号链接名称的命名规则。

IoCreateSymbolicLink这个函数的功能是创建符号链接,通常在DriverEntry中调用。

wdk文档中对这个函数介绍如下:

The IoCreateSymbolicLink routine sets up a symbolic link between a device object name and a user-visible name for the device. 

这句话的意思就是IoCreateSymbolicLink在设备名和用户可见名字之间创建链接。


通常在调用IoCreateDevice创建设备对象的时候会传入设备名称。这个设备名称只能在内核层使用,只能被内核层的函数识别。如果IoCreateDevice中没有指定设备名称,那么I/O管理器会自动分配一个数字作为设备的名称。例如"\Device\00000001"。

如果想要在用户层的应用程序中访问(比如调用CreateFile函数打开),就需要创建符号链接,相当于为设备对象创建一个别名,供应用程序访问。应用层是无法直接通过设备名字来打开对象的,必须建立一个暴露给应用程序的符号链接。

比如:c盘的符号链接名称是"C:",对应的设备名称是"\Device\HarddiskVolume1”


在驱动程序中,定义设备对象名称需要以L"\\device\\"开头,

定义符号链接的名称需要以L"\\dosDevices\\"开头,(注意dosDevices中的最后一个字符是s,初学者很容易漏写这个字符,然后IoCreateSymbolicLink函数返回失败,半天找不到原因);或者以L"\\??\\"开头也可以。

<think>好的,用户现在想在Windows中用C语言实现通过随机设备名和符号链接名进行通信的具体方案,并且提到了两种可能的方案:一种是使用注册表存储随机名,驱动创建设备;另一种是使用固定设备名,首次通信后更改。我需要根据用户提供的引用和之前的回答来给出解决方案。 首先,回顾之前的回答,用户已经展示了一个使用CreateSymbolicLink的C程序,但用户现在提到的是驱动层面的实现,而不是应用层的符号链接。引用[1]中提到了驱动生成设备对象(如/Device/MyCDO)并创建符号链接(如/DosDevices/MyCDOSL),应用层通过打开符号链接名来访问设备。所以用户现在的问题可能涉及到驱动程序和应用程序的交互,而不仅仅是应用层的文件符号链接。 用户提到的方案1是使用注册表存储随机设备名,驱动在创建设备时生成随机名,并将该名称存储在注册表中,这样应用程序可以从注册表读取设备名进行通信。方案2是驱动使用固定设备名,首次通信后由应用程序或驱动更改符号链接名,后续使用新的随机名。 需要分析这两种方案的可行性。首先,驱动程序在Windows中创建设备对象时,通常需要指定设备名,例如\Device\RandomName。然后为该设备创建一个符号链接,例如\DosDevices\MySymLink,应用程序通过\\.\MySymLink访问设备。如果设备名是随机的,每次驱动加载时生成不同的设备名,那么符号链接可能需要对应更新,或者应用程序需要动态获取设备名。 方案1中,驱动生成随机设备名,并存储在注册表中,应用程序读取注册表获取设备名,然后通过符号链接访问。但这里可能需要驱动在每次加载时生成新的设备名,并更新注册表。然而,驱动如何将设备名写入注册表?这可能需要驱动与用户模式应用程序进行通信,或者驱动在初始化时直接操作注册表。但驱动程序操作注册表需要相应的权限,并且可能涉及到同步问题,比如应用程序何时读取注册表,是否在驱动完全写入之后。此外,注册表操作会增加复杂性,可能影响系统稳定性。 方案2是驱动使用固定设备名,首次通信后更改符号链接。例如,驱动创建一个固定设备名,如\Device\FixedDevice,并创建一个初始符号链接,如\DosDevices\TempLink。应用程序首次通过TempLink访问设备,发送一个请求,驱动生成新的随机符号链接名,并更新符号链接指向同一个设备。之后,应用程序使用新的符号链接名进行通信。这种方法可能更可靠,因为驱动可以控制符号链接的创建和更新,而应用程序只需要在首次连接时使用固定名,之后根据驱动的响应获取新名。 但如何实现驱动动态更改符号链接?在驱动中,可以使用IoCreateSymbolicLink创建符号链接,并在需要时调用IoDeleteSymbolicLink删除旧的,再创建新的。这需要驱动程序能够处理来自应用程序的请求,比如IOCTL(输入输出控制码),当应用程序发送特定请求时,驱动生成新的符号链接名,并更新符号链接。 另外,引用[3]提到驱动中使用ExAllocatePoolWithTag分配内存,需要注意内存泄漏问题。在生成随机名时,驱动需要正确分配和释放内存,避免泄漏。 现在需要考虑如何在驱动中生成随机设备名。可以使用随机字符串生成函数,比如在驱动中调用RtlRandomEx生成随机数,然后转换为字符。但设备名通常需要符合一定的格式,比如长度限制和合法字符。例如,设备名可能只允许字母、数字和下划线,且长度不超过某个值。 在方案1中,驱动生成随机设备名,如\Device\Random123,然后创建符号链接\DosDevices\MySymLink。但是这样符号链接名是固定的,如果设备名随机,可能需要每次驱动加载时创建不同的符号链接名,或者保持符号链接名固定,但指向不同的设备名。但后者可能不太适用,因为符号链接一旦创建,如果设备名已经存在,可能无法重复创建。或者每次驱动加载时,先删除旧的符号链接,再创建新的。但这样可能引起应用程序在驱动重新加载后无法找到设备,除非应用程序也动态获取新的符号链接名。 因此,可能更可行的是方案2,即驱动使用固定设备名,但符号链接名在首次通信后更改。例如,驱动启动时创建固定设备名\Device\FixedDevice,并生成一个随机符号链接名,比如\DosDevices\SymLinkABCD,然后将该符号链接名通过某种方式传递给应用程序。应用程序首次连接时使用固定的符号链接名,比如\DosDevices\InitialLink,驱动在收到首次请求后生成新的随机符号链接名,并告知应用程序,之后应用程序使用新名进行通信。但如何告知应用程序新的符号链接名呢?可能需要通过设备IOCTL接口,应用程序发送请求后,驱动返回新生成的符号链接名,然后应用程序使用该名称进行后续通信。 或者,驱动在每次启动时生成一个随机符号链接名,并将该名称存储在某个位置,比如注册表或共享内存,应用程序读取该位置获取当前的符号链接名。但这样又回到了方案1的问题,即如何同步驱动和应用程序之间的数据存储。 另一种方法是,应用程序在每次启动时枚举所有符号链接,找到与驱动相关的那个。例如,驱动在创建符号链接时使用特定前缀,应用程序遍历所有符号链接,找到匹配前缀的那个。但这种方法可能效率较低,且需要驱动和应用程序之间有约定的命名规则。 回到用户的提问,用户希望的具体方案可能更偏向于驱动程序的实现,而不仅仅是应用层的符号链接操作。因此,我需要结合引用[1]中的驱动创建设备符号链接的过程,以及如何让应用程序通过符号链接名进行通信。 在方案1中,驱动在加载时生成随机设备名,例如\Device\RandomDevice123,并创建对应的符号链接\DosDevices\RandomSymLink456。驱动将该符号链接名写入注册表的某个位置,例如HKEY_LOCAL_MACHINE\Software\MyDriver\SymLinkName。应用程序启动时读取该注册表项,获取符号链接名,然后使用\\.\RandomSymLink456打开设备。但驱动程序如何写入注册表?这可能需要驱动程序调用Zw系列函数,如ZwCreateKey、ZwSetValueKey等,但这些操作在驱动中需要谨慎处理,且需要相应的权限。同时,应用程序读取注册表也需要处理可能的权限问题,特别是如果驱动将信息存储在HKEY_LOCAL_MACHINE下,普通应用程序可能需要管理员权限才能读取。 方案2中,驱动使用固定设备名,例如\Device\MyFixedDevice,并在首次通信时生成随机符号链接名。例如,当应用程序首次打开\\.\InitialSymLink时,驱动处理该请求,生成新的随机符号链接名,例如\DosDevices\NewSymLink789,并删除旧的InitialSymLink,创建新的符号链接。然后,驱动通过IOCTL将新的符号链接名返回给应用程序,应用程序后续使用新名称进行通信。这种方式需要驱动能够动态修改符号链接,并且应用程序能够处理这种变更。 在实现方案2时,需要注意以下几点: 1. 驱动程序在初始化时创建固定设备对象和初始符号链接。 2. 当应用程序首次打开初始符号链接时,驱动生成新的随机符号链接名,并更新符号链接。 3. 驱动需要将新生成的符号链接名传递给应用程序,可能通过设备IOCTL或直接写入设备缓冲区。 4. 应用程序在首次通信后获取新符号链接名,并后续使用该名称。 这种方法的好处是,每次驱动启动时,初始符号链接名可以是固定的,应用程序首次连接后获得随机生成的新名称,增强了安全性,因为攻击者难以预测符号链接名。但需要处理并发情况,比如多个应用程序同时首次连接时的竞争条件。 在代码实现方面,驱动程序需要处理IRP_MJ_CREATE请求,当检测到是通过初始符号链接打开时,触发生成新符号链接名的逻辑,并返回新名称给应用程序。例如,在驱动程序的IRP_MJ_CREATE处理函数中,检查设备对象是否是初始符号链接,如果是,则生成新名称,创建新符号链接,删除旧符号链接,并将新名称通过IRP返回给应用程序。 然而,IRP_MJ_CREATE通常不用于传递数据,因此可能需要使用IOCTL来触发符号链接的更新。例如,应用程序首先打开初始符号链接,然后发送一个IOCTL请求,驱动处理该请求时生成新符号链接名,并通过输出缓冲区返回给应用程序。之后,应用程序关闭初始符号链接,使用新名称重新打开。 另外,驱动程序在生成随机符号链接名时,需要确保名称的唯一性,避免与现有符号链接冲突。可以使用高熵的随机数生成算法,并结合时间戳或进程ID等信息。 关于内存管理,引用[3]提到驱动中使用ExAllocatePoolWithTag分配内存,需确保在不再需要时释放,避免内存泄漏。例如,生成随机名称时分配的内存需要正确释放。 总结,方案1和方案2各有优缺点。方案1依赖注册表存储随机名,可能引入同步和权限问题;方案2通过动态更新符号链接名,需要处理驱动和应用程序之间的通信协调。根据用户的需求,可能更倾向于方案2,因为它不需要依赖外部存储,且能动态增强安全性。 最后,需要确保示例代码的正确性,包括驱动程序的创建设备符号链接,处理IOCTL,以及应用程序如何与驱动交互。同时,错误处理也是关键,比如符号链接创建失败时的回滚操作,资源释放等。</think>根据引用[1]中驱动层设备对象与符号链接的交互机制,结合Windows内核开发特性,提供两种可行的C语言实现方案: ### 方案1:注册表存储随机名(驱动+应用层协同) ```c // 驱动层核心代码(WDM模型) NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryPath) { WCHAR devNameBuffer[50]; WCHAR symLinkBuffer[50]; // 生成随机设备名(示例:\Device\CDO_8F3A) RtlStringCbPrintfW(devNameBuffer, sizeof(devNameBuffer), L"\\Device\\CDO_%04X", RtlRandomEx(&seed) & 0xFFFF); // 创建设备对象 IoCreateDevice(pDriverObj, 0, &devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObj); // 生成符号链接名并写入注册表 RtlStringCbPrintfW(symLinkBuffer, sizeof(symLinkBuffer), L"\\DosDevices\\SL_%04X", RtlRandomEx(&seed) & 0xFFFF); ZwSetValueKey(hRegistryKey, &valueName, 0, REG_SZ, symLinkBuffer, wcslen(symLinkBuffer)*2+2); // 创建符号链接 IoCreateSymbolicLink(&symLink, &devName); } ``` ```c // 应用层获取设备名 HKEY hKey; RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\MyDriver", 0, KEY_READ, &hKey); WCHAR symLinkName[50]; RegQueryValueEx(hKey, L"SymLink", NULL, NULL, (LPBYTE)symLinkName, &size); swprintf(finalPath, L"\\\\?\\%s", symLinkName); // 最终路径如"\\\\.\\SL_8F3A" HANDLE hDevice = CreateFile(finalPath, ...); ``` ### 方案2:动态更新符号链接(首次通信后变更) ```c // 驱动层IOCTL处理 NTSTATUS HandleIOCTL(PDEVICE_OBJECT pDevObj, PIRP pIrp) { PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp); if (stack->Parameters.DeviceIoControl.IoControlCode == IOCTL_UPDATE_SYMLINK) { WCHAR newSymLink[50]; RtlStringCbPrintfW(newSymLink, sizeof(newSymLink), L"\\DosDevices\\SL_%04X", RtlRandomEx(&seed)); // 删除旧链接并创建新链接 IoDeleteSymbolicLink(&oldSymLink); IoCreateSymbolicLink(&newSymLink, &devName); // 返回新符号链接名给应用层 RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer, newSymLink, sizeof(newSymLink)); pIrp->IoStatus.Information = sizeof(newSymLink); } return STATUS_SUCCESS; } ``` ```c // 应用层通信流程 // 首次使用固定名称连接 HANDLE hDevice = CreateFile(L"\\\\.\\INIT_SYMLINK", GENERIC_READ | GENERIC_WRITE, ...); // 发送IOCTL请求更新链接 WCHAR newSymLink[50] = {0}; DeviceIoControl(hDevice, IOCTL_UPDATE_SYMLINK, NULL, 0, newSymLink, sizeof(newSymLink), &retSize, NULL); // 使用新链接重新打开设备 CloseHandle(hDevice); swprintf(finalPath, L"\\\\?\\%s", newSymLink); hDevice = CreateFile(finalPath, ...); // 后续通信使用新符号链接 ``` ### 两种方案对比 | 特性 | 方案1(注册表存储) | 方案2(动态更新) | |---------------------|---------------------------|--------------------------| | 实现复杂度 | 中(需注册表操作) | 高(需同步机制) | | 安全性 | 低(注册表可被其他进程读取) | 高(每次会话动态变化) | | 兼容性 | 需管理员权限写注册表 | 普通用户权限即可 | | 适用场景 | 设备名不频繁变更 | 需要会话级隔离的通信 | ### 关键实现要点 1. **设备名生成算法**:建议采用`RtlRandomEx`+时间戳的混合熵源(引用[4]的指针差异可增强随机性): ```c ULONG seed = KeQueryTimeIncrement() + (ULONG_PTR)PsGetCurrentProcessId(); ``` 2. **符号链接权限控制**:通过`IoCreateDeviceSecure`设置设备对象的ACL(引用[1]设备访问控制要求): ```c const GUID DEVICE_GUID = {...}; IoCreateDeviceSecure(pDriverObj,..., &DEVICE_GUID, SDDL_DEVOBJ_SYS_ALL_ADM_RWX,...); ``` 3. **内存管理规范**:驱动层必须使用`ExAllocatePool2`(Windows 10+)或`ExAllocatePoolWithTag`(引用[3]内存泄漏警告): ```c PVOID pBuffer = ExAllocatePool2(POOL_FLAG_PAGED, bufferSize, 'MyTag'); /* 使用后必须释放 */ ExFreePoolWithTag(pBuffer, 'MyTag'); ``` 4. **32/64位兼容性**:设备名定义需使用`UNICODE_STRING`结构体(引用[4]指针尺寸差异处理): ```c DECLARE_CONST_UNICODE_STRING(devName, L"\\Device\\MyCDO"); ``` ### 增强健壮性建议 - 注册表操作应包含重试机制: ```c for (int i=0; i<3; i++) { status = ZwSetValueKey(...); if (NT_SUCCESS(status)) break; KeDelayExecutionThread(KernelMode, FALSE, 100); } ``` - 符号链接更新需要同步锁: ```c KSPIN_LOCK symLinkLock; KeAcquireSpinLock(&symLinkLock, &oldIrql); /* 执行符号链接修改操作 */ KeReleaseSpinLock(&symLinkLock, oldIrql); ```
<think>好的,用户的问题是关于在Windows中使用C语言实现随机设备名和符号链接名与驱动通信的功能。我需要先理解这些概念以及它们在Windows驱动开发中的具体应用。 首先,设备名是驱动在系统中注册的名称,应用程序通过这个名称来访问驱动。而符号链接名则是设备名的一个别名,通常更友好,方便应用程序使用。随机设备名可能是为了安全考虑,避免固定的名称被恶意程序利用。因此,用户的需求可能涉及到如何生成随机的设备名和符号链接名,并确保应用程序能通过这些名称与驱动通信。 接下来,我需要回顾Windows驱动开发的基本流程。驱动通常需要创建一个设备对象,并注册符号链接。在用户态,应用程序可以通过CreateFile打开设备,使用DeviceIoControl进行通信。但如何生成随机名称呢?在驱动层,可能需要使用特定的函数来生成随机字符串作为设备名和符号链接名。 然后,考虑如何用C语言实现这些步骤。驱动部分需要使用Windows Driver Kit(WDK),涉及DriverEntry例程、设备创建、符号链接管理等。用户态程序则需要调用相应的API,如CreateFile,并处理随机生成的符号链接名。这里可能需要动态获取符号链接名,或者在驱动和应用程序之间共享某种随机生成的标识符。 还需要注意安全性问题,随机名称可以防止简单的枚举攻击,但需要确保生成足够的随机性。同时,符号链接名的创建和删除需要在驱动加载和卸载时正确管理,避免资源泄漏。 另外,参考提供的引用中,尤其是引用[2]提到的unlocked_ioctl在kernel 3.0之后被移除,但Windows驱动开发可能使用不同的接口,比如IOCTL。需要确认Windows驱动中控制命令的处理方法,可能涉及到IRP_MJ_DEVICE_CONTROL的处理。 最后,整理实现步骤,包括驱动端的设备创建、符号链接生成,用户程序的通信方法,并给出示例代码框架。同时,验证这些步骤是否符合Windows驱动开发的最佳实践,以及如何处理可能的错误情况,比如名称冲突或生成失败。</think>在Windows系统中使用C语言实现随机设备名和符号链接名与驱动通信,需要遵循以下技术路线: ### 一、驱动端实现步骤 1. **生成随机设备名** ```c WCHAR deviceNameBuffer[64]; RtlStringCbPrintfW(deviceNameBuffer, sizeof(deviceNameBuffer), L"\\Device\\RandomDevice_%08x", RtlRandomEx(&seed)); // 使用RtlRandomEx生成随机数[^2] ``` 2. **创建设备对象** ```c UNICODE_STRING deviceName; RtlInitUnicodeString(&deviceName, deviceNameBuffer); IoCreateDevice(DriverObject, 0, &deviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); ``` 3. **创建随机符号链接** ```c WCHAR symLinkBuffer[64]; RtlStringCbPrintfW(symLinkBuffer, sizeof(symLinkBuffer), L"\\DosDevices\\RandSymLink_%08x", RtlRandomEx(&seed)); UNICODE_STRING symLinkName; RtlInitUnicodeString(&symLinkName, symLinkBuffer); IoCreateSymbolicLink(&symLinkName, &deviceName); ``` ### 二、应用层通信方法 1. **动态获取符号链接名** 需要建立应用层与驱动层的命名约定,例如: - 通过注册表传递符号链接名 - 使用固定前缀+通配符枚举设备 ```c HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_MYDRIVER, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); ``` 2. **建立通信通道** ```c HANDLE hDevice = CreateFile(symLinkName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); ``` 3. **发送控制命令** ```c DeviceIoControl(hDevice, IOCTL_MYDRIVER_COMMAND, inputBuffer, inputSize, outputBuffer, outputSize, &bytesReturned, NULL); ``` ### 三、关键安全措施 1. 使用`RtlRandomEx`替代标准库rand函数,确保更强的随机性[^3] 2. 设备对象设置安全描述符 ```c PSECURITY_DESCRIPTOR sd; ConvertStringSecurityDescriptorToSecurityDescriptor( L"D:P(A;;GA;;;SY)(A;;GA;;;BA)", SDDL_REVISION_1, &sd, NULL); pDeviceObject->SecurityDescriptor = sd; ``` ### 四、完整开发流程示例 1. 驱动加载时生成随机设备名和符号链接 2. 通过事件日志或注册表传递符号链接信息 3. 应用程序动态获取符号链接名 4. 建立安全通信通道 5. 完成交互后驱动自动清理符号链接
### C语言在Windows驱动程序中实现随机设备名和符号链接的通信逻辑 为了实现在Windows环境中使用C语言完成随机设备名和符号链接的相关操作,包括更新设备名、删除旧的设备以及创建新的设备符号链接的过程,以下是详细的说明: #### 1. 设备对象与符号链接的基础概念 在Windows驱动开发中,设备对象是由`IoCreateDevice`函数创建的一个核心结构体[^2]。它表示一个物理或虚拟硬件资源,并可以通过符号链接暴露给用户模式应用程序。符号链接是一个字符串路径,通常位于`\??\`命名空间下,允许用户模式的应用程序通过该路径访问设备。 #### 2. 使用随机名称生成器 要生成随机设备名,可以借助C语言中的标准库函数如`srand`和`rand`来构建唯一的字符序列作为设备名的一部分。例如: ```c #include <stdlib.h> #include <time.h> void GenerateRandomName(char* buffer, size_t length) { srand((unsigned int)time(NULL)); const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; if (length) *(buffer + --length) = '\0'; for (size_t i = 0; i < length; ++i) { unsigned int key = rand() % (int)(sizeof(charset) - 1); buffer[i] = charset[key]; } } ``` 此代码片段会生成指定长度的随机字符串[^1]。 #### 3. 创建设备对象 当驱动加载时,需调用`IoCreateDevice`函数创建一个新的设备对象。如果需要更改现有设备的名字,则应先销毁原设备再重新创建新名字的设备。 ```c NTSTATUS CreateNewDevice(PDRIVER_OBJECT DriverObject, PUNICODE_STRING DeviceName) { NTSTATUS status; PDEVICE_OBJECT deviceObject; status = IoCreateDevice( DriverObject, sizeof(DEVICE_EXTENSION), DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &deviceObject); if (!NT_SUCCESS(status)) { DbgPrint("Failed to create new device object\n"); return status; } // 设置标志位和其他初始化工作... return STATUS_SUCCESS; } ``` #### 4. 删除已有设备及其符号链接 若想移除现有的设备实例连同其关联的符号链接,可依次执行以下步骤: - 调用`IoDeleteSymbolicLink`清除符号链接; - 接着调用`IoDeleteDevice`释放对应的设备对象。 具体实现如下所示: ```c VOID DeleteExistingDeviceAndSymLink(PUNICODE_STRING SymLinkName, PDEVICE_OBJECT DeviceObject) { if (SymLinkName && *SymLinkName->Buffer != L'\0') { BOOLEAN result = IoDeleteSymbolicLink(SymLinkName); if (!result) { DbgPrint("Error deleting symbolic link.\n"); } else { DbgPrint("Successfully deleted symbolic link.\n"); } } if (DeviceObject) { IoDeleteDevice(DeviceObject); DbgPrint("Deleted the existing device successfully.\n"); } } ``` #### 5. 建立新的符号链接 一旦成功创建了一个具有唯一名称的新设备之后,就可以为其建立相应的符号链接以便于外部应用能够找到这个设备。这一步骤可通过调用`IoCreateSymbolicLink`完成。 ```c NTSTATUS EstablishSymbolicLink(PUNICODE_STRING SymLinkName, PUNICODE_STRING DeviceName) { NTSTATUS status = IoCreateSymbolicLink(SymLinkName, DeviceName); if (!NT_SUCCESS(status)) { DbgPrint("Failed to establish symbolic link with error code: %X", status); return status; } DbgPrint("Symbolic Link Established Successfully."); return STATUS_SUCCESS; } ``` 以上过程涵盖了从生成随机设备名到最终设置好可用符号链接的整体流程。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值