├── .github └── workflows │ └── main.yml ├── binding.gyp ├── index.d.ts ├── license ├── package.json ├── readme.md └── src ├── common.h ├── convertPath.h ├── dirWatcher.h ├── ejectDrive.h ├── find.h ├── fsWin.cpp ├── getAttributes.h ├── getCompressedSize.h ├── getDeviceCapabilities.h ├── getDriveDevice.h ├── getLogicalDriveList.h ├── getStorageProperties.h ├── getVolumeInformation.h ├── getVolumeSpace.h ├── setAttributes.h ├── setCompression.h ├── setShortName.h ├── setSparse.h ├── setVolumeLabel.h └── splitPath.h /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build with node-gyp 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | workflow_dispatch: 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | steps: 13 | 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Set up Node.js 18 | uses: actions/setup-node@v4 19 | - name: Install msvc-dev-cmd 20 | uses: ilammy/msvc-dev-cmd@v1 21 | 22 | - name: Install node-gpy 23 | run: npm -g install node-gyp 24 | shell: cmd 25 | 26 | - name: build 27 | run: node-gyp rebuild 28 | shell: cmd 29 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [{ 3 | "target_name": "fswin", 4 | "conditions": [ 5 | ["OS=='win'", { 6 | "sources": ["src/fswin.cpp"] 7 | }] 8 | ] 9 | }] 10 | } -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export declare module DirWatcher { 2 | export type Callback = (event: Event, message: Message | string) => void; 3 | 4 | export interface Options { 5 | WATCH_SUB_DIRECTORIES?: boolean; 6 | CHANGE_FILE_SIZE?: boolean; 7 | CHANGE_LAST_WRITE?: boolean; 8 | CHANGE_LAST_ACCESS?: boolean; 9 | CHANGE_CREATION?: boolean; 10 | CHANGE_ATTRIBUTES?: boolean; 11 | CHANGE_SECURITY?: boolean; 12 | } 13 | 14 | export interface Message { 15 | OLD_NAME?: string; 16 | NEW_NAME?: string; 17 | } 18 | 19 | export type Event = 20 | | 'STARTED' 21 | | 'MOVED' 22 | | 'ADDED' 23 | | 'REMOVED' 24 | | 'MODIFIED' 25 | | 'RENAMED' 26 | | 'ENDED' 27 | | 'ERROR'; 28 | } 29 | 30 | export enum EjectDriveMethod { 31 | DEVICE_IO_CONTROL = 0, // works with optical drive 32 | CM_REQUEST_DEVICE_EJECT_W = 1, // works with USB flash drive 33 | HOT_PLUG_EJECT_DEVICE = 2, // works with most media types but requires user interaction and HotPlug.dll. Only exists in system32 folder which means it is not possible to use this method when you execute a 32bit node process in a 64bit Windows. 34 | } 35 | 36 | export declare module Find { 37 | export interface File { 38 | LONG_NAME: string; 39 | SHORT_NAME: string; 40 | CREATION_TIME: Date; 41 | LAST_ACCESS_TIME: Date; 42 | LAST_WRITE_TIME: Date; 43 | SIZE: number; 44 | RAW_ATTRIBUTES: number; 45 | IS_ARCHIVED: boolean; 46 | IS_COMPRESSED: boolean; 47 | IS_DEVICE: boolean; 48 | IS_DIRECTORY: boolean; 49 | IS_ENCRYPTED: boolean; 50 | IS_HIDDEN: boolean; 51 | IS_NOT_CONTENT_INDEXED: boolean; 52 | IS_OFFLINE: boolean; 53 | IS_READ_ONLY: boolean; 54 | IS_SPARSE_FILE: boolean; 55 | IS_SYSTEM: boolean; 56 | IS_TEMPORARY: boolean; 57 | IS_INTEGRITY_STREAM: boolean; 58 | IS_NO_SCRUB_DATA: boolean; 59 | IS_RECALL_ON_DATA_ACCESS: boolean; 60 | IS_RECALL_ON_OPEN: boolean; 61 | IS_VIRTUAL: boolean; 62 | IS_EA: boolean; 63 | IS_PINNED: boolean; 64 | IS_UNPINNED: boolean; 65 | REPARSE_POINT_TAG: ReparsePointTagType; 66 | } 67 | 68 | export type ReparsePointTagType = 69 | | 'MOUNT_POINT' 70 | | 'HSM' 71 | | 'HSM2' 72 | | 'SIS' 73 | | 'WIM' 74 | | 'CSV' 75 | | 'DFS' 76 | | 'SYMLINK' 77 | | 'DFSR' 78 | | 'DEDUP' 79 | | 'NFS' 80 | | 'FILE_PLACEHOLDER' 81 | | 'WOF' 82 | | 'WCI' 83 | | 'WCI_1' 84 | | 'GLOBAL_REPARSE' 85 | | 'CLOUD' 86 | | 'CLOUD_1' 87 | | 'CLOUD_2' 88 | | 'CLOUD_3' 89 | | 'CLOUD_4' 90 | | 'CLOUD_5' 91 | | 'CLOUD_6' 92 | | 'CLOUD_7' 93 | | 'CLOUD_8' 94 | | 'CLOUD_9' 95 | | 'CLOUD_A' 96 | | 'CLOUD_B' 97 | | 'CLOUD_C' 98 | | 'CLOUD_D' 99 | | 'CLOUD_E' 100 | | 'CLOUD_F' 101 | | 'CLOUD_MASK' 102 | | 'APPEXECLINK' 103 | | 'PROJFS' 104 | | 'STORAGE_SYNC' 105 | | 'WCI_TOMBSTONE' 106 | | 'UNHANDLED' 107 | | 'ONEDRIVE' 108 | | 'PROJFS_TOMBSTONE' 109 | | 'AF_UNIX' 110 | | 'WCI_LINK' 111 | | 'WCI_LINK_1' 112 | | 'DATALESS_CIM' 113 | | '' 114 | | number; 115 | 116 | export type Event = 'FOUND' | 'SUCCEEDED' 117 | 118 | export type SyncCallback = (file: File) => void 119 | export type AsyncProgressiveCallback = (event: Event, message: File) => void 120 | export type AsyncBasicCallback = (files: File[]) => void 121 | 122 | export type ProgressiveModeEnabled = true 123 | } 124 | 125 | export interface Attributes { 126 | CREATION_TIME: Date; 127 | LAST_ACCESS_TIME: Date; 128 | LAST_WRITE_TIME: Date; 129 | SIZE: number; 130 | LINK_COUNT: number; 131 | RAW_ATTRIBUTES: number; 132 | IS_ARCHIVED: boolean; 133 | IS_COMPRESSED: boolean; 134 | IS_DEVICE: boolean; 135 | IS_DIRECTORY: boolean; 136 | IS_ENCRYPTED: boolean; 137 | IS_HIDDEN: boolean; 138 | IS_NOT_CONTENT_INDEXED: boolean; 139 | IS_OFFLINE: boolean; 140 | IS_READ_ONLY: boolean; 141 | IS_SPARSE_FILE: boolean; 142 | IS_SYSTEM: boolean; 143 | IS_TEMPORARY: boolean; 144 | IS_INTEGRITY_STREAM: boolean; 145 | IS_NO_SCRUB_DATA: boolean; 146 | IS_RECALL_ON_DATA_ACCESS: boolean; 147 | IS_RECALL_ON_OPEN: boolean; 148 | IS_VIRTUAL: boolean; 149 | IS_EA: boolean; 150 | IS_PINNED: boolean; 151 | IS_UNPINNED: boolean; 152 | IS_REPARSE_POINT: boolean; 153 | } 154 | 155 | export interface SetAttributes { 156 | IS_ARCHIVED?: boolean; 157 | IS_HIDDEN?: boolean; 158 | IS_NOT_CONTENT_INDEXED?: boolean; 159 | IS_OFFLINE?: boolean; 160 | IS_READ_ONLY?: boolean; 161 | IS_SYSTEM?: boolean; 162 | IS_TEMPORARY?: boolean; 163 | IS_UNPINNED?: boolean; 164 | IS_PINNED?: boolean; 165 | } 166 | 167 | export interface DeviceCapabilities { 168 | LOCK_SUPPORTED: boolean; 169 | EJECT_SUPPORTED: boolean; 170 | REMOVABLE: boolean; 171 | DOCK_DEVICE: boolean; 172 | UNIQUE_ID: boolean; 173 | SILENT_INSTALL: boolean; 174 | RAW_DEVICE_OK: boolean; 175 | SURPRISE_REMOVAL_OK: boolean; 176 | HARDWARE_DISABLED: boolean; 177 | NON_DYNAMIC: boolean; 178 | } 179 | 180 | export interface DriveDevice { 181 | deviceNumber: number; 182 | devicePath: string; 183 | deviceId: string; 184 | parentDeviceId: string; 185 | } 186 | 187 | export type DriveType = 188 | | 'NO_ROOT_DIR' 189 | | 'REMOVABLE' 190 | | 'FIXED' 191 | | 'REMOTE' 192 | | 'CDROM' 193 | | 'RAMDISK' 194 | | 'UNKNOWN'; 195 | export interface LogicalDriveList { 196 | [driveLetter: string]: DriveType; 197 | } 198 | 199 | export declare module StorageProperties { 200 | export interface Properties { 201 | deviceProperty?: boolean; 202 | adapterProperty?: boolean; 203 | deviceWriteCacheProperty?: boolean; 204 | accessAlignmentProperty?: boolean; 205 | deviceSeekPenalty?: boolean; 206 | deviceTrim?: boolean; 207 | deviceLBProvisioningProperty?: boolean; 208 | devicePowerProperty?: boolean; 209 | deviceCopyOffloadProperty?: boolean; 210 | deviceMediumProductType?: boolean; 211 | adapterRpmbProperty?: boolean; 212 | deviceIoCapabilityProperty?: boolean; 213 | adapterTemperatureProperty?: boolean; 214 | deviceTemperatureProperty?: boolean; 215 | adapterSerialNumber?: boolean 216 | } 217 | 218 | export type BusType = 219 | | 'SCSI' 220 | | 'ATAPI' 221 | | 'ATA' 222 | | '1394' 223 | | 'SSA' 224 | | 'Fibre' 225 | | 'USB' 226 | | 'RAID' 227 | | 'iSCSI' 228 | | 'SAS' 229 | | 'SATA' 230 | | 'SD' 231 | | 'MMC' 232 | | 'Virtual' 233 | | 'FileBackedVirtual' 234 | | 'Spaces' 235 | | 'NVMe' 236 | | 'SCM' 237 | | 'UFS' 238 | | number; 239 | 240 | export type SrbType = 241 | | 'SCSIRequestBlock' 242 | | 'StorageRequestBlock' 243 | | number; 244 | 245 | export type WriteCacheType = 246 | | 'unknown' 247 | | 'none' 248 | | 'writeBack' 249 | | 'writeThrough' 250 | | number; 251 | 252 | export type WriteCacheEnabledType = 253 | | 'unknown' 254 | | 'disabled' 255 | | 'enabled' 256 | | number; 257 | 258 | export type WriteCacheChangeableType = 259 | | 'unknown' 260 | | 'notChangeable' 261 | | 'changeable' 262 | | number; 263 | 264 | export type WriteCacheWriteThroughSupportedType = 265 | | 'unknown' 266 | | 'notSupported' 267 | | 'supported' 268 | | number; 269 | 270 | export interface DeviceProperty { 271 | deviceType: number; 272 | deviceTypeModifier: number; 273 | busType: BusType; 274 | commandQueueing: boolean; 275 | removableMedia: boolean; 276 | vendorId: string; 277 | productId: string; 278 | productRevision: string; 279 | serialNumber: string; 280 | } 281 | 282 | export interface AdapterProperty { 283 | maximumTransferLength: number; 284 | maximumPhysicalPages: number; 285 | alignmentMask: number; 286 | adapterUsesPio: boolean; 287 | adapterScansDown: boolean; 288 | commandQueueing: boolean; 289 | acceleratedTransfer: boolean; 290 | busMajorVersion: number; 291 | busMinorVersion: number; 292 | busType: BusType; 293 | srbType: SrbType; 294 | addressType: string; 295 | } 296 | 297 | export interface DeviceWriteCacheProperty { 298 | type: WriteCacheType; 299 | isEnabled: WriteCacheEnabledType; 300 | isChangeable: WriteCacheChangeableType; 301 | isWriteThroughSupported: WriteCacheWriteThroughSupportedType; 302 | flushCacheSupported: boolean; 303 | userDefinedPowerProtection: boolean; 304 | NVCacheEnabled: boolean; 305 | } 306 | 307 | export interface AccessAlignmentProperty { 308 | bytesPerCacheLine: number; 309 | bytesOffsetForCacheAlignment: number; 310 | bytesPerLogicalSector: number; 311 | bytesPerPhysicalSector: number; 312 | bytesOffsetForSectorAlignment: number; 313 | } 314 | 315 | export interface DeviceLBProvisioningProperty { 316 | thinProvisioningEnabled: boolean; 317 | thinProvisioningReadZeros: boolean; 318 | anchorSupported: boolean; 319 | unmapGranularityAlignmentValid: boolean; 320 | getFreeSpaceSupported: boolean; 321 | mapSupported: boolean; 322 | optimalUnmapGranularity: bigint; 323 | unmapGranularityAlignment: bigint; 324 | maxUnmapLbaCount: number; 325 | maxUnmapBlockDescriptorCount: number; 326 | } 327 | 328 | export interface DevicePowerProperty { 329 | deviceAttentionSupported: boolean; 330 | asynchronousNotificationSupported: boolean; 331 | idlePowerManagementEnabled: boolean; 332 | d3ColdEnabled: boolean; 333 | d3ColdSupported: boolean; 334 | noVerifyDuringIdlePower: boolean; 335 | idleTimeoutInMS: number; 336 | } 337 | 338 | export interface DeviceCopyOffloadProperty { 339 | maximumTokenLifetime: number; 340 | defaultTokenLifetime: number; 341 | maximumTransferSize: bigint; 342 | optimalTransferCount: bigint; 343 | maximumDataDescriptors: number; 344 | maximumTransferLengthPerDescriptor: number; 345 | optimalTransferLengthPerDescriptor: number; 346 | optimalTransferLengthGranularity: number; 347 | } 348 | 349 | export type DeviceMediumProductType = 350 | | 'CFast' 351 | | 'CompactFlash' 352 | | 'MemoryStick' 353 | | 'MultiMediaCard' 354 | | 'SecureDigitalCard' 355 | | 'QXD' 356 | | 'UniversalFlashStorage' 357 | | number; 358 | 359 | export interface AdapterRpmbProperty { 360 | sizeInBytes: number; 361 | maxReliableWriteSizeInBytes: number; 362 | frameFormat: number; 363 | } 364 | 365 | export interface DeviceIoCapabilityProperty { 366 | lunMaxIoCount: number; 367 | adapterMaxIoCount: number; 368 | } 369 | 370 | export interface TemperatureInfo { 371 | index: number; 372 | temperature: number; 373 | overThreshold: number; 374 | underThreshold: number; 375 | overThresholdChangable: boolean; 376 | underThresholdChangable: boolean; 377 | eventGenerated: boolean; 378 | } 379 | 380 | export interface DeviceTemperatureProperty { 381 | criticalTemperature: number; 382 | warningTemperature: number; 383 | temperatureInfo: TemperatureInfo[]; 384 | } 385 | 386 | export interface RootObject { 387 | deviceProperty: DeviceProperty; 388 | adapterProperty: AdapterProperty; 389 | deviceWriteCacheProperty: DeviceWriteCacheProperty; 390 | accessAlignmentProperty: AccessAlignmentProperty; 391 | deviceSeekPenalty: boolean; 392 | deviceTrim: boolean; 393 | deviceLBProvisioningProperty: DeviceLBProvisioningProperty; 394 | devicePowerProperty: DevicePowerProperty; 395 | deviceCopyOffloadProperty: DeviceCopyOffloadProperty; 396 | deviceMediumProductType: DeviceMediumProductType; 397 | adapterRpmbProperty: AdapterRpmbProperty; 398 | deviceIoCapabilityProperty: DeviceIoCapabilityProperty; 399 | deviceTemperatureProperty: DeviceTemperatureProperty; 400 | } 401 | 402 | } 403 | 404 | export interface VolumeInformation { 405 | LABEL: string; 406 | FILESYSTEM: string; 407 | SERIALNUMBER: number; 408 | RAW_FLAGS: number; 409 | CASE_SENSITIVE_SEARCH: boolean; 410 | CASE_PRESERVED_NAMES: boolean; 411 | UNICODE_ON_DISK: boolean; 412 | PERSISTENT_ACLS: boolean; 413 | FILE_COMPRESSION: boolean; 414 | VOLUME_QUOTAS: boolean; 415 | RETURNS_CLEANUP_RESULT_INFO: boolean; 416 | VOLUME_IS_COMPRESSED: boolean; 417 | NAMED_STREAMS: boolean; 418 | READ_ONLY_VOLUME: boolean; 419 | SEQUENTIAL_WRITE_ONCE: boolean; 420 | DAX_VOLUME: boolean; 421 | SUPPORTS_SPARSE_FILES: boolean; 422 | SUPPORTS_REPARSE_POINTS: boolean; 423 | SUPPORTS_REMOTE_STORAGE: boolean; 424 | SUPPORTS_POSIX_UNLINK_RENAME: boolean; 425 | SUPPORTS_BYPASS_IO: boolean; 426 | SUPPORTS_OBJECT_IDS: boolean; 427 | SUPPORTS_ENCRYPTION: boolean; 428 | SUPPORTS_TRANSACTIONS: boolean; 429 | SUPPORTS_HARD_LINKS: boolean; 430 | SUPPORTS_EXTENDED_ATTRIBUTES: boolean; 431 | SUPPORTS_OPEN_BY_FILE_ID: boolean; 432 | SUPPORTS_USN_JOURNAL: boolean; 433 | SUPPORTS_INTEGRITY_STREAMS: boolean; 434 | SUPPORTS_BLOCK_REFCOUNTING: boolean; 435 | SUPPORTS_SPARSE_VDL: boolean; 436 | SUPPORTS_GHOSTING: boolean; 437 | } 438 | 439 | export interface VolumeSpace { 440 | FREE: number; 441 | TOTAL: number; 442 | } 443 | 444 | export interface SplitPath { 445 | PARENT: string; 446 | NAME: string; 447 | } 448 | 449 | // https://github.com/xxoo/node-fswin/wiki/convertPath-and-convertPathSync 450 | export function convertPath(pathToConvert: string, callback: (result?: string) => void, isLong?: boolean): boolean; 451 | export function convertPathSync(pathToConvert: string, isLong?: boolean): string | undefined; 452 | 453 | // https://github.com/xxoo/node-fswin/wiki/dirWatcher 454 | export class dirWatcher { 455 | constructor(dirToWatch: string, callback: DirWatcher.Callback, options?: DirWatcher.Options); 456 | close(): boolean; 457 | } 458 | 459 | // https://github.com/xxoo/node-fswin/wiki/ejectDrive-and-ejectDriveSync 460 | export function ejectDrive(letter: string, method: EjectDriveMethod, callback: (succeeded: boolean) => void): boolean; 461 | export function ejectDriveSync(letter: string, method: EjectDriveMethod): boolean 462 | 463 | // https://github.com/xxoo/node-fswin/wiki/find-and-findSync 464 | export function find(pathToFind: string, callback: Find.AsyncProgressiveCallback, isProgressiveMode: Find.ProgressiveModeEnabled): boolean; 465 | export function find(pathToFind: string, callback: Find.AsyncBasicCallback): boolean; 466 | export function findSync(pathToFind: string, callback: Find.SyncCallback): number; 467 | export function findSync(pathToFind: string): Find.File[]; 468 | 469 | // https://github.com/xxoo/node-fswin/wiki/getAttributes-and-getAttributesSync 470 | export function getAttributes(path: string, callback: (result?: Attributes) => void): boolean; 471 | export function getAttributesSync(path: string): Attributes; 472 | 473 | // https://github.com/xxoo/node-fswin/wiki/getDeviceCapabilities-and-getDeviceCapabilitiesSync 474 | export function getDeviceCapabilities(id: string, callback: (result: DeviceCapabilities) => void): boolean; 475 | export function getDeviceCapabilitiesSync(id: string): DeviceCapabilities; 476 | 477 | // https://github.com/xxoo/node-fswin/wiki/getDriveDevice-and-getDriveDeviceSync 478 | export function getDriveDevice(letter: string, callback: (result: DriveDevice) => void): boolean; 479 | export function getDriveDeviceSync(letter: string): DriveDevice; 480 | 481 | // https://github.com/xxoo/node-fswin/wiki/getLogicalDriveList-and-getLogicalDriveListSync 482 | export function getLogicalDriveList(callback: (result: LogicalDriveList) => void): boolean; 483 | export function getLogicalDriveListSync(): LogicalDriveList; 484 | 485 | // https://github.com/xxoo/node-fswin/wiki/getStorageProperties-and-getStoragePropertiesSync 486 | export function getStorageProperties(path: string, properties: StorageProperties.Properties, callback: (result: StorageProperties.RootObject) => void): boolean; 487 | export function getStoragePropertiesSync(path: string, properties: StorageProperties.Properties): StorageProperties.RootObject; 488 | 489 | // https://github.com/xxoo/node-fswin/wiki/splitPath 490 | export function splitPath(fullPath: string): SplitPath; 491 | 492 | // https://github.com/xxoo/node-fswin/wiki/getVolumeInformation-and-getVolumeInformationSync 493 | export function getVolumeInformation(path: string, callback: (result: VolumeInformation) => void): boolean; 494 | export function getVolumeInformationSync(path: string): VolumeInformation; 495 | 496 | // https://github.com/xxoo/node-fswin/wiki/getVolumeSpace-and-getVolumeSpaceSync 497 | export function getVolumeSpace(path: string, callback: (result: VolumeSpace) => void): boolean; 498 | export function getVolumeSpaceSync(path: string): VolumeSpace; 499 | 500 | // https://github.com/xxoo/node-fswin/wiki/setAttributes-and-setAttributesSync 501 | export function setAttributes(path: string, attributes: SetAttributes, callback: (succeeded?: boolean) => void): boolean; 502 | export function setAttributesSync(path: string, attributes: SetAttributes): boolean; 503 | 504 | // https://github.com/xxoo/node-fswin/wiki/setVolumeLabel-and-setVolumeLabelSync 505 | export function setVolumeLabel(path: string, label: string, callback: (succeeded: boolean) => void): boolean; 506 | export function setVolumeLabelSync(volume: string, label: string): boolean; 507 | 508 | export namespace ntfs { 509 | // https://github.com/xxoo/node-fswin/wiki/getCompressedSize-and-getCompressedSizeSync 510 | export function getCompressedSize(file: string, callback: (result: number) => void): boolean; 511 | export function getCompressedSizeSync(file: string): number; 512 | 513 | // https://github.com/xxoo/node-fswin/wiki/setCompression-and-setCompressionSync 514 | export function setCompression(fileOrDir: string, callback: (succeeded: boolean) => void, compress?: boolean, create?: boolean): boolean; 515 | export function setCompressionSync(fileOrDir: string, compress?: boolean, create?: boolean): number; 516 | 517 | // https://github.com/xxoo/node-fswin/wiki/setShortName-and-setShortNameSync 518 | export function setShortName(pathToSet: string, newShortName?: string, callback?: (succeeded: boolean) => void): boolean; 519 | export function setShortNameSync(pathToSet: string, newShortName?: string): boolean; 520 | 521 | // https://github.com/xxoo/node-fswin/wiki/setSparse-and-setSparseSync 522 | export function setSparse(file: string, callback: (succeeded: boolean) => void, create?: boolean): boolean; 523 | export function setSparseSync(file: string, create?: boolean): boolean; 524 | } 525 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | Copyright Xiao Shen and other fsWin contributors. All rights reserved. 2 | Permission is hereby granted, free of charge, to any person obtaining a copy 3 | of this software and associated documentation files (the "Software"), to 4 | deal in the Software without restriction, including without limitation the 5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 6 | sell copies of the Software, and to permit persons to whom the Software is 7 | furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in 10 | all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 17 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 18 | IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fswin", 3 | "version": "3.24.829", 4 | "description": "nodejs fs extensions for windows", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "engines": { 8 | "node": ">= 8.0" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/xxoo/node-fswin.git" 13 | }, 14 | "keywords": [ 15 | "native", 16 | "fs", 17 | "filesystem", 18 | "watch", 19 | "windows", 20 | "wildcards", 21 | "ntfs" 22 | ], 23 | "author": "Xiao Shen", 24 | "license": "MIT", 25 | "readmeFilename": "readme.md" 26 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Introduction [![Build with node-gyp](https://github.com/xxoo/node-fswin/actions/workflows/main.yml/badge.svg)](https://github.com/xxoo/node-fswin/actions/workflows/main.yml) 2 | 3 | [`fswin`](https://www.npmjs.com/package/fswin) is a native [`node.js`](http://nodejs.org) add-on that works on windows. 4 | It has ported some platform specified filesystem APIs. And made them easy to use in javascript. 5 | 6 | For api documents and examples please see [wiki](https://github.com/xxoo/node-fswin/wiki/Structure) 7 | 8 | NOTE: fswin depends on n-api since v3. which requires node.js v8 or later. 9 | 10 | ## How to Install 11 | 12 | You may install fswin by using the following command line: 13 | 14 | ``` 15 | npm install fswin 16 | ``` 17 | 18 | The package contains prebuilt binaries for `node.js`, `electron` and `nw.js` on all architectures. 19 | 20 | ## How to Build 21 | 22 | first you need to install [Visual Studio](http://www.visualstudio.com) 2019 or later. 23 | 24 | then you need to install [Python](https://www.python.org/downloads/windows) 25 | 26 | then you need to install node-gyp 27 | 28 | ``` 29 | npm -g install node-gyp 30 | ``` 31 | 32 | then you need to clone the repo 33 | 34 | ``` 35 | git clone https://github.com/xxoo/node-fswin.git 36 | cd node-fswin 37 | ``` 38 | 39 | finally you can build the source 40 | 41 | for `node.js`: 42 | 43 | ``` 44 | node-gyp rebuild 45 | ``` 46 | 47 | for `electron`: 48 | 49 | ``` 50 | node-gyp rebuild --target=x.x.x --dist-url=https://atom.io/download/electron 51 | ``` 52 | 53 | for `nw.js` please follow the [official document](https://nwjs.readthedocs.io/en/latest/For%20Users/Advanced/Use%20Native%20Node%20Modules/) 54 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | //#include 7 | #pragma comment(lib, "setupapi.lib") 8 | 9 | #define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) 10 | 11 | constexpr auto SYB_EXP_INVAL = "EINVAL"; 12 | constexpr auto SYB_ERR_WRONG_ARGUMENTS = "WRONG_ARGUMENTS"; 13 | constexpr auto SYB_ERR_NOT_A_CONSTRUCTOR = "THIS_FUNCTION_IS_NOT_A_CONSTRUCTOR"; 14 | constexpr auto SYB_FILEATTR_ISARCHIVED = "IS_ARCHIVED"; 15 | constexpr auto SYB_FILEATTR_ISHIDDEN = "IS_HIDDEN"; 16 | constexpr auto SYB_FILEATTR_ISNOTCONTENTINDEXED = "IS_NOT_CONTENT_INDEXED"; 17 | constexpr auto SYB_FILEATTR_ISOFFLINE = "IS_OFFLINE"; 18 | constexpr auto SYB_FILEATTR_ISREADONLY = "IS_READ_ONLY"; 19 | constexpr auto SYB_FILEATTR_ISSYSTEM = "IS_SYSTEM"; 20 | constexpr auto SYB_FILEATTR_ISTEMPORARY = "IS_TEMPORARY"; 21 | constexpr auto SYB_FILEATTR_CREATIONTIME = "CREATION_TIME"; 22 | constexpr auto SYB_FILEATTR_LASTACCESSTIME = "LAST_ACCESS_TIME"; 23 | constexpr auto SYB_FILEATTR_LASTWRITETIME = "LAST_WRITE_TIME"; 24 | constexpr auto SYB_FILEATTR_SIZE = "SIZE"; 25 | constexpr auto SYB_FILEATTR_ISDIRECTORY = "IS_DIRECTORY"; 26 | constexpr auto SYB_FILEATTR_ISCOMPRESSED = "IS_COMPRESSED"; 27 | constexpr auto SYB_FILEATTR_ISENCRYPTED = "IS_ENCRYPTED"; 28 | constexpr auto SYB_FILEATTR_ISSPARSEFILE = "IS_SPARSE_FILE"; 29 | constexpr auto SYB_FILEATTR_ISDEVICE = "IS_DEVICE"; 30 | constexpr auto SYB_FILEATTR_ISINTEGERITYSTREAM = "IS_INTEGRITY_STREAM"; 31 | constexpr auto SYB_FILEATTR_ISNOSCRUBDATA = "IS_NO_SCRUB_DATA"; 32 | constexpr auto SYB_FILEATTR_ISRECALLONDATAACCESS = "IS_RECALL_ON_DATA_ACCESS"; 33 | constexpr auto SYB_FILEATTR_ISRECALLONOPEN = "IS_RECALL_ON_OPEN"; 34 | constexpr auto SYB_FILEATTR_ISVIRTUAL = "IS_VIRTUAL"; 35 | constexpr auto SYB_FILEATTR_ISEA = "IS_EA"; 36 | constexpr auto SYB_FILEATTR_ISPINNED = "IS_PINNED"; 37 | constexpr auto SYB_FILEATTR_ISUNPINNED = "IS_UNPINNED"; 38 | constexpr auto SYB_FILEATTR_RAWATTRS = "RAW_ATTRIBUTES"; 39 | constexpr auto NETWORK_PATH = L"\\\\?\\UNC\\"; 40 | constexpr auto LOCALE_PATH = L"\\\\?\\"; 41 | constexpr auto MAX_LONG_PATH = 32767; 42 | 43 | typedef CHAR(__stdcall* _RtlSetThreadPlaceholderCompatibilityMode)(__in CHAR Mode); 44 | static const _RtlSetThreadPlaceholderCompatibilityMode RtlSetThreadPlaceholderCompatibilityMode = (_RtlSetThreadPlaceholderCompatibilityMode)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlSetThreadPlaceholderCompatibilityMode"); 45 | 46 | wchar_t* getCurrentPathByHandle(HANDLE hnd) { 47 | wchar_t* r = NULL; 48 | DWORD sz0, sz = GetFinalPathNameByHandleW(hnd, NULL, 0, FILE_NAME_NORMALIZED); 49 | if (sz > 0) { 50 | wchar_t* s = new wchar_t[sz]; 51 | DWORD sz1 = (DWORD)wcslen(NETWORK_PATH); 52 | DWORD sz2 = (DWORD)wcslen(LOCALE_PATH); 53 | GetFinalPathNameByHandleW(hnd, s, sz, FILE_NAME_NORMALIZED); 54 | if (wcsncmp(s, NETWORK_PATH, sz1) == 0) { 55 | sz0 = sz1 - 2; 56 | s[sz0] = L'\\'; 57 | } else if (wcsncmp(s, LOCALE_PATH, sz2) == 0 && ((s[sz2] >= L'a'&&s[sz2] <= L'z') || (s[sz2] >= L'A'&&s[sz2] <= L'Z')) && s[sz2 + 1] == L':') { 58 | sz0 = (DWORD)wcslen(LOCALE_PATH); 59 | } else { 60 | sz0 = 0; 61 | } 62 | if (sz0 > 0) { 63 | size_t l = sz - sz0; 64 | r = new wchar_t[l]; 65 | wcscpy_s(r, l, &s[sz0]); 66 | delete[]s; 67 | } else { 68 | r = s; 69 | } 70 | } 71 | return r; 72 | } 73 | bool ensurePrivilege(const char* privilegeName) { 74 | bool result = false; 75 | HANDLE hToken; 76 | if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { 77 | LUID tkid; 78 | if (LookupPrivilegeValueA(NULL, privilegeName, &tkid)) { 79 | PRIVILEGE_SET ps; 80 | ps.PrivilegeCount = 1; 81 | ps.Control = PRIVILEGE_SET_ALL_NECESSARY; 82 | ps.Privilege[0].Luid = tkid; 83 | ps.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; 84 | BOOL chkresult; 85 | if (PrivilegeCheck(hToken, &ps, &chkresult)) { 86 | if (chkresult) { 87 | result = true; 88 | } else { 89 | TOKEN_PRIVILEGES tp; 90 | tp.PrivilegeCount = 1; 91 | tp.Privileges[0] = ps.Privilege[0]; 92 | result = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL) ? true : false; 93 | } 94 | } 95 | } 96 | CloseHandle(hToken); 97 | } 98 | return result; 99 | } 100 | ULONGLONG combineHiLow(const DWORD hi, const DWORD low) { 101 | ULARGE_INTEGER ul; 102 | ul.HighPart = hi; 103 | ul.LowPart = low; 104 | return ul.QuadPart; 105 | } 106 | int64_t fileTimeToJsDateVal(const FILETIME* ft) {//converts FILETIME to javascript date value 107 | return combineHiLow(ft->dwHighDateTime, ft->dwLowDateTime) / 10000 - 11644473600000; 108 | } -------------------------------------------------------------------------------- /src/convertPath.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class convertPath { 5 | public: 6 | static wchar_t* func(const wchar_t* path, bool islong) {//you need to delete the result yourself if it is not NULL 7 | wchar_t* tpath; 8 | DWORD sz = islong ? GetLongPathNameW(path, NULL, 0) : GetShortPathNameW(path, NULL, 0); 9 | if (sz > 0) { 10 | tpath = new wchar_t[sz]; 11 | islong ? GetLongPathNameW(path, tpath, sz) : GetShortPathNameW(path, tpath, sz); 12 | } else { 13 | tpath = NULL; 14 | } 15 | return tpath; 16 | } 17 | static napi_value init(napi_env env, bool isSync = false) { 18 | napi_value f; 19 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 20 | return f; 21 | } 22 | private: 23 | const struct cbdata { 24 | napi_async_work work; 25 | napi_ref self; 26 | napi_ref cb; 27 | wchar_t* path; 28 | bool islong; 29 | wchar_t* result; 30 | }; 31 | static napi_value sync(napi_env env, napi_callback_info info) { 32 | napi_value result; 33 | napi_get_new_target(env, info, &result); 34 | if (result) { 35 | result = NULL; 36 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 37 | } else { 38 | napi_value argv[2]; 39 | size_t argc = 2; 40 | napi_get_cb_info(env, info, &argc, argv, NULL, NULL); 41 | if (argc < 1) { 42 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 43 | } else { 44 | size_t str_len; 45 | napi_value tmp; 46 | napi_coerce_to_string(env, argv[0], &tmp); 47 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 48 | str_len += 1; 49 | wchar_t* str = new wchar_t[str_len]; 50 | napi_get_value_string_utf16(env, tmp, (char16_t*)str, str_len, NULL); 51 | bool islong = false; 52 | if (argc > 1) { 53 | napi_coerce_to_bool(env, argv[1], &tmp); 54 | napi_get_value_bool(env, tmp, &islong); 55 | } 56 | wchar_t* s = func(str, islong); 57 | delete[]str; 58 | if (s) { 59 | napi_create_string_utf16(env, (char16_t*)s, wcslen(s), &result); 60 | delete[]s; 61 | } else { 62 | napi_get_null(env, &result); 63 | } 64 | } 65 | } 66 | return result; 67 | } 68 | static napi_value async(napi_env env, napi_callback_info info) { 69 | napi_value result; 70 | napi_get_new_target(env, info, &result); 71 | if (result) { 72 | result = NULL; 73 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 74 | } else { 75 | napi_value argv[3], self; 76 | size_t argc = 3; 77 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 78 | if (argc >= 2) { 79 | napi_valuetype t; 80 | napi_typeof(env, argv[1], &t); 81 | if (t == napi_function) { 82 | cbdata* data = new cbdata; 83 | data->islong = false; 84 | size_t str_len; 85 | napi_value tmp; 86 | napi_create_reference(env, argv[1], 1, &data->cb); 87 | napi_create_reference(env, self, 1, &data->self); 88 | napi_coerce_to_string(env, argv[0], &tmp); 89 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 90 | str_len += 1; 91 | data->path = new wchar_t[str_len]; 92 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->path, str_len, NULL); 93 | if (argc > 2) { 94 | napi_coerce_to_bool(env, argv[2], &tmp); 95 | napi_get_value_bool(env, tmp, &data->islong); 96 | } 97 | napi_create_string_latin1(env, "fswin.convertPath", NAPI_AUTO_LENGTH, &tmp); 98 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 99 | if (napi_queue_async_work(env, data->work) == napi_ok) { 100 | napi_get_boolean(env, true, &result); 101 | } else { 102 | napi_get_boolean(env, false, &result); 103 | napi_delete_reference(env, data->cb); 104 | napi_delete_reference(env, data->self); 105 | napi_delete_async_work(env, data->work); 106 | delete[]data->path; 107 | delete data; 108 | } 109 | } 110 | } 111 | if (!result) { 112 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 113 | } 114 | } 115 | return result; 116 | } 117 | static void execute(napi_env env, void* data) { 118 | cbdata* d = (cbdata*)data; 119 | d->result = func(d->path, d->islong); 120 | } 121 | static void complete(napi_env env, napi_status status, void* data) { 122 | cbdata* d = (cbdata*)data; 123 | delete[]d->path; 124 | napi_value cb, self, argv; 125 | napi_get_reference_value(env, d->cb, &cb); 126 | napi_get_reference_value(env, d->self, &self); 127 | if (status == napi_ok && d->result) { 128 | napi_create_string_utf16(env, (char16_t*)d->result, wcslen(d->result), &argv); 129 | delete[]d->result; 130 | } else { 131 | napi_get_null(env, &argv); 132 | } 133 | napi_call_function(env, self, cb, 1, &argv, NULL); 134 | napi_delete_reference(env, d->cb); 135 | napi_delete_reference(env, d->self); 136 | napi_delete_async_work(env, d->work); 137 | delete d; 138 | } 139 | }; -------------------------------------------------------------------------------- /src/dirWatcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "find.h" 3 | #include "splitPath.h" 4 | constexpr auto SYB_BUFFERSIZE = 64 * 1024; 5 | constexpr auto SYB_EVT_ERR = "ERROR"; 6 | constexpr auto SYB_EVT_ERR_INITIALIZATION_FAILED = L"INITIALIZATION_FAILED"; 7 | constexpr auto SYB_EVT_ERR_UNABLE_TO_CONTINUE_WATCHING = L"UNABLE_TO_CONTINUE_WATCHING"; 8 | constexpr auto SYB_EVT_ERR_UNABLE_TO_WATCH_SELF = L"UNABLE_TO_WATCH_SELF"; 9 | constexpr auto SYB_EVT_RENAMED = "RENAMED"; 10 | constexpr auto SYB_EVT_RENAMED_OLD_NAME = "OLD_NAME"; 11 | constexpr auto SYB_EVT_RENAMED_NEW_NAME = "NEW_NAME"; 12 | constexpr auto SYB_OPT_WATCH_SUB_DIRECTORIES = "WATCH_SUB_DIRECTORIES"; 13 | constexpr auto SYB_OPT_CHANGE_FILE_SIZE = "CHANGE_FILE_SIZE"; 14 | constexpr auto SYB_OPT_CHANGE_LAST_WRITE = "CHANGE_LAST_WRITE"; 15 | constexpr auto SYB_OPT_CHANGE_LAST_ACCESS = "CHANGE_LAST_ACCESS"; 16 | constexpr auto SYB_OPT_CHANGE_CREATION = "CHANGE_CREATION"; 17 | constexpr auto SYB_OPT_CHANGE_ATTRIBUTES = "CHANGE_ATTRIBUTES"; 18 | constexpr auto SYB_OPT_CHANGE_SECURITY = "CHANGE_SECURITY"; 19 | 20 | class dirWatcher { 21 | public: 22 | static napi_value init(napi_env env) { 23 | napi_value result, proto, tmp; 24 | napi_create_function(env, NULL, 0, Create, NULL, &result); 25 | napi_create_function(env, NULL, 0, close, NULL, &tmp); 26 | napi_get_named_property(env, result, "prototype", &proto); 27 | napi_set_named_property(env, proto, "close", tmp); 28 | napi_create_reference(env, result, 1, &constructor); 29 | return result; 30 | } 31 | private: 32 | const struct msg { 33 | const char* type; 34 | const wchar_t* content; 35 | msg* next; 36 | }; 37 | static napi_ref constructor; 38 | 39 | static napi_value Create(napi_env env, napi_callback_info info) { 40 | napi_value result, target, argv[3]; 41 | size_t argc = 3; 42 | napi_get_cb_info(env, info, &argc, argv, &result, NULL); 43 | napi_get_new_target(env, info, &target); 44 | if (target) { 45 | if (argc >= 2) { 46 | napi_valuetype t; 47 | napi_typeof(env, argv[1], &t); 48 | if (t == napi_function) { 49 | dirWatcher* self = new dirWatcher(); 50 | self->watchingParent = self->watchingPath = 0; 51 | self->pathmsg = self->parentmsg = NULL; 52 | self->oldName = self->newName = self->longName = self->shortName = NULL; 53 | self->parenthnd = self->pathhnd = INVALID_HANDLE_VALUE; 54 | self->options = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE; 55 | self->subDirs = true; 56 | napi_value tmp; 57 | if (argc > 2 && napi_coerce_to_object(env, argv[2], &tmp) == napi_ok) { 58 | napi_value v; 59 | bool b; 60 | napi_has_named_property(env, tmp, SYB_OPT_WATCH_SUB_DIRECTORIES, &b); 61 | if (b) { 62 | napi_get_named_property(env, tmp, SYB_OPT_WATCH_SUB_DIRECTORIES, &v); 63 | napi_get_value_bool(env, v, &self->subDirs); 64 | } 65 | napi_has_named_property(env, tmp, SYB_OPT_CHANGE_FILE_SIZE, &b); 66 | if (b) { 67 | napi_get_named_property(env, tmp, SYB_OPT_CHANGE_FILE_SIZE, &v); 68 | napi_get_value_bool(env, v, &b); 69 | if (!b) { 70 | self->options ^= FILE_NOTIFY_CHANGE_SIZE; 71 | } 72 | } 73 | napi_has_named_property(env, tmp, SYB_OPT_CHANGE_LAST_WRITE, &b); 74 | if (b) { 75 | napi_get_named_property(env, tmp, SYB_OPT_CHANGE_LAST_WRITE, &v); 76 | napi_get_value_bool(env, v, &b); 77 | if (!b) { 78 | self->options ^= FILE_NOTIFY_CHANGE_LAST_WRITE; 79 | } 80 | } 81 | napi_has_named_property(env, tmp, SYB_OPT_CHANGE_LAST_ACCESS, &b); 82 | if (b) { 83 | napi_get_named_property(env, tmp, SYB_OPT_CHANGE_LAST_ACCESS, &v); 84 | napi_get_value_bool(env, v, &b); 85 | if (b) { 86 | self->options |= FILE_NOTIFY_CHANGE_LAST_ACCESS; 87 | } 88 | } 89 | napi_has_named_property(env, tmp, SYB_OPT_CHANGE_CREATION, &b); 90 | if (b) { 91 | napi_get_named_property(env, tmp, SYB_OPT_CHANGE_CREATION, &v); 92 | napi_get_value_bool(env, v, &b); 93 | if (b) { 94 | self->options |= FILE_NOTIFY_CHANGE_CREATION; 95 | } 96 | } 97 | napi_has_named_property(env, tmp, SYB_OPT_CHANGE_ATTRIBUTES, &b); 98 | if (b) { 99 | napi_get_named_property(env, tmp, SYB_OPT_CHANGE_ATTRIBUTES, &v); 100 | napi_get_value_bool(env, v, &b); 101 | if (b) { 102 | self->options |= FILE_NOTIFY_CHANGE_ATTRIBUTES; 103 | } 104 | } 105 | napi_has_named_property(env, tmp, SYB_OPT_CHANGE_SECURITY, &b); 106 | if (b) { 107 | napi_get_named_property(env, tmp, SYB_OPT_CHANGE_SECURITY, &v); 108 | napi_get_value_bool(env, v, &b); 109 | if (b) { 110 | self->options |= FILE_NOTIFY_CHANGE_SECURITY; 111 | } 112 | } 113 | } 114 | napi_coerce_to_string(env, argv[0], &tmp); 115 | size_t str_len; 116 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 117 | str_len += 1; 118 | self->path = new wchar_t[str_len]; 119 | napi_get_value_string_utf16(env, tmp, (char16_t*)self->path, str_len, NULL); 120 | napi_value resname; 121 | napi_create_string_latin1(env, "fswin.dirWatcher", NAPI_AUTO_LENGTH, &resname); 122 | napi_wrap(env, result, self, Destroy, NULL, &self->wrapper); 123 | napi_create_reference(env, argv[1], 1, &self->callback); 124 | napi_create_async_work(env, NULL, resname, beginWatchingPath, finishWatchingPath, self, &self->pathwork); 125 | napi_create_async_work(env, NULL, resname, beginWatchingParent, finishWatchingParent, self, &self->parentwork); 126 | self->watchPath(env); 127 | } 128 | } 129 | if (!result) { 130 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 131 | } 132 | } else { 133 | napi_value cons; 134 | napi_get_reference_value(env, constructor, &cons); 135 | napi_new_instance(env, cons, argc, argv, &result); 136 | } 137 | return result; 138 | } 139 | static void Destroy(napi_env env, void* nativeObject, void* finalize_hint) { 140 | dirWatcher* self = (dirWatcher*)nativeObject; 141 | napi_delete_reference(env, self->wrapper); 142 | napi_delete_reference(env, self->callback); 143 | if (self->watchingPath == 0 && self->pathhnd == INVALID_HANDLE_VALUE) { 144 | delete self; 145 | } else {//the user did not call close method before dirWatcher is garbage collected 146 | self->callback = self->wrapper = NULL; 147 | self->stopWatching(env); 148 | } 149 | } 150 | static napi_value close(napi_env env, napi_callback_info info) {//this method returns false if dirWatcher is failed to create or already closed 151 | napi_value that, result; 152 | dirWatcher* self; 153 | napi_get_cb_info(env, info, NULL, NULL, &that, NULL); 154 | napi_unwrap(env, that, (void**)&self); 155 | if (self->watchingPath == 0 && self->pathhnd == INVALID_HANDLE_VALUE) { 156 | napi_get_boolean(env, false, &result); 157 | } else { 158 | self->stopWatching(env); 159 | napi_get_boolean(env, true, &result); 160 | } 161 | return result; 162 | } 163 | static void beginWatchingPath(napi_env env, void* data) { 164 | dirWatcher* self = (dirWatcher*)data; 165 | self->watchingPath = 2; 166 | if (self->pathhnd == INVALID_HANDLE_VALUE) { 167 | self->pathhnd = CreateFileW(self->path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); 168 | if (self->pathhnd == INVALID_HANDLE_VALUE) { 169 | self->pathmsg = new msg; 170 | self->pathmsg->type = SYB_EVT_ERR; 171 | self->pathmsg->content = SYB_EVT_ERR_INITIALIZATION_FAILED; 172 | self->pathmsg->next = NULL; 173 | } else if (self->parenthnd == INVALID_HANDLE_VALUE && self->parentwork) { 174 | self->savePath(env, true); 175 | } 176 | } else { 177 | self->pathbuffer = malloc(SYB_BUFFERSIZE); 178 | if (self->pathbuffer) { 179 | DWORD b; 180 | self->watchingPathResult = ReadDirectoryChangesW(self->pathhnd, self->pathbuffer, SYB_BUFFERSIZE, self->subDirs, self->options, &b, NULL, NULL); 181 | } else { 182 | self->watchingPathResult = FALSE; 183 | } 184 | } 185 | } 186 | static void finishWatchingPath(napi_env env, napi_status status, void* data) { 187 | dirWatcher* self = (dirWatcher*)data; 188 | self->watchingPath = 0; 189 | if (status != napi_ok) { 190 | self->checkWatchingStoped(env); 191 | } else if (self->pathmsg) { 192 | while (self->pathmsg) { 193 | napi_value v; 194 | napi_create_string_utf16(env, (char16_t*)self->pathmsg->content, NAPI_AUTO_LENGTH, &v); 195 | self->callCb(env, self->pathmsg->type, v); 196 | msg* m = self->pathmsg; 197 | self->pathmsg = m->next; 198 | delete m; 199 | } 200 | if (self->pathhnd == INVALID_HANDLE_VALUE) { 201 | self->checkWatchingStoped(env); 202 | } else { 203 | self->watchPath(env); 204 | } 205 | } else if (self->pathhnd == INVALID_HANDLE_VALUE) { 206 | free(self->pathbuffer); 207 | self->checkWatchingStoped(env); 208 | } else if (self->watchingPathResult) { 209 | void* buffer = self->pathbuffer; 210 | self->watchPath(env); 211 | FILE_NOTIFY_INFORMATION* pInfo; 212 | DWORD d = 0; 213 | do { 214 | pInfo = (FILE_NOTIFY_INFORMATION*)((ULONG_PTR)buffer + d); 215 | d += pInfo->NextEntryOffset; 216 | napi_value filename; 217 | napi_create_string_utf16(env, (char16_t*)pInfo->FileName, pInfo->FileNameLength / sizeof(wchar_t), &filename); 218 | if (pInfo->Action == FILE_ACTION_ADDED) { 219 | self->callCb(env, "ADDED", filename); 220 | } else if (pInfo->Action == FILE_ACTION_REMOVED) { 221 | self->callCb(env, "REMOVED", filename); 222 | } else if (pInfo->Action == FILE_ACTION_MODIFIED) { 223 | self->callCb(env, "MODIFIED", filename); 224 | } else if (pInfo->Action == FILE_ACTION_RENAMED_OLD_NAME) { 225 | if (self->newName) { 226 | napi_value arg, tmp; 227 | napi_create_object(env, &arg); 228 | napi_set_named_property(env, arg, SYB_EVT_RENAMED_OLD_NAME, filename); 229 | napi_create_string_utf16(env, (char16_t*)self->newName, NAPI_AUTO_LENGTH, &tmp); 230 | napi_set_named_property(env, arg, SYB_EVT_RENAMED_NEW_NAME, tmp); 231 | delete[]self->newName; 232 | self->newName = NULL; 233 | self->callCb(env, SYB_EVT_RENAMED, arg); 234 | } else { 235 | size_t sz = (size_t)pInfo->FileNameLength + 1; 236 | self->oldName = new wchar_t[sz]; 237 | wcscpy_s(self->oldName, sz, pInfo->FileName); 238 | } 239 | } else if (pInfo->Action == FILE_ACTION_RENAMED_NEW_NAME) { 240 | if (self->oldName) { 241 | napi_value arg, tmp; 242 | napi_create_object(env, &arg); 243 | napi_set_named_property(env, arg, SYB_EVT_RENAMED_NEW_NAME, filename); 244 | napi_create_string_utf16(env, (char16_t*)self->oldName, NAPI_AUTO_LENGTH, &tmp); 245 | napi_set_named_property(env, arg, SYB_EVT_RENAMED_OLD_NAME, tmp); 246 | delete[]self->oldName; 247 | self->oldName = NULL; 248 | self->callCb(env, SYB_EVT_RENAMED, arg); 249 | } else { 250 | size_t sz = (size_t)pInfo->FileNameLength + 1; 251 | self->newName = new wchar_t[sz]; 252 | wcscpy_s(self->newName, sz, pInfo->FileName); 253 | } 254 | } 255 | } while (pInfo->NextEntryOffset > 0); 256 | free(buffer); 257 | } else { 258 | free(self->pathbuffer); 259 | napi_value v; 260 | napi_create_string_utf16(env, (char16_t*)SYB_EVT_ERR_UNABLE_TO_CONTINUE_WATCHING, NAPI_AUTO_LENGTH, &v); 261 | self->callCb(env, SYB_EVT_ERR, v); 262 | self->stopWatching(env); 263 | } 264 | } 265 | static void beginWatchingParent(napi_env env, void* data) { 266 | dirWatcher* self = (dirWatcher*)data; 267 | self->watchingParent = 2; 268 | if (self->parenthnd == INVALID_HANDLE_VALUE) { 269 | self->savePath(env); 270 | } else { 271 | DWORD d; 272 | self->watchingParentResult = ReadDirectoryChangesW(self->parenthnd, self->parentbuffer, SYB_BUFFERSIZE, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME, &d, NULL, NULL); 273 | } 274 | } 275 | static void finishWatchingParent(napi_env env, napi_status status, void* data) { 276 | dirWatcher* self = (dirWatcher*)data; 277 | self->watchingParent = 0; 278 | if (status != napi_ok) { 279 | self->checkWatchingStoped(env); 280 | } else if (self->parentmsg) { 281 | while (self->parentmsg) { 282 | napi_value v; 283 | napi_create_string_utf16(env, (char16_t*)self->parentmsg->content, NAPI_AUTO_LENGTH, &v); 284 | self->callCb(env, self->parentmsg->type, v); 285 | msg* m = self->parentmsg; 286 | self->parentmsg = m->next; 287 | delete m; 288 | } 289 | if (self->parenthnd == INVALID_HANDLE_VALUE) { 290 | self->checkWatchingStoped(env); 291 | } else { 292 | self->watchParent(env); 293 | } 294 | } else if (self->parenthnd == INVALID_HANDLE_VALUE) { 295 | self->checkWatchingStoped(env); 296 | } else if (self->watchingParentResult) { 297 | FILE_NOTIFY_INFORMATION* pInfo; 298 | DWORD d = 0; 299 | do { 300 | pInfo = (FILE_NOTIFY_INFORMATION*)((ULONG_PTR)self->parentbuffer + d); 301 | d += pInfo->NextEntryOffset; 302 | if ((pInfo->Action == FILE_ACTION_RENAMED_OLD_NAME || pInfo->Action == FILE_ACTION_REMOVED) && wcsncmp(self->longName, pInfo->FileName, MAX(pInfo->FileNameLength / sizeof(wchar_t), wcslen(self->longName))) == 0 || (self->shortName && wcsncmp(self->shortName, pInfo->FileName, MAX(pInfo->FileNameLength / sizeof(wchar_t), wcslen(self->shortName))) == 0)) { 303 | CloseHandle(self->parenthnd); 304 | self->parenthnd = INVALID_HANDLE_VALUE; 305 | break; 306 | } 307 | } while (pInfo->NextEntryOffset > 0); 308 | self->watchParent(env); 309 | } else { 310 | napi_value v; 311 | napi_create_string_utf16(env, (char16_t*)SYB_EVT_ERR_UNABLE_TO_WATCH_SELF, NAPI_AUTO_LENGTH, &v); 312 | self->callCb(env, SYB_EVT_ERR, v); 313 | self->stopWatchingParent(env); 314 | } 315 | } 316 | 317 | msg* pathmsg; 318 | msg* parentmsg; 319 | HANDLE pathhnd; 320 | HANDLE parenthnd; 321 | napi_async_work pathwork; 322 | napi_async_work parentwork; 323 | BOOL watchingPathResult; 324 | BOOL watchingParentResult; 325 | BYTE watchingPath; 326 | BYTE watchingParent; 327 | bool subDirs; 328 | DWORD options; 329 | wchar_t* path; 330 | wchar_t* oldName; 331 | wchar_t* newName; 332 | wchar_t* shortName; 333 | wchar_t* longName; 334 | void* pathbuffer; 335 | BYTE* parentbuffer[SYB_BUFFERSIZE]; 336 | napi_ref wrapper; 337 | napi_ref callback; 338 | 339 | void watchPath(napi_env env) { 340 | if (napi_queue_async_work(env, this->pathwork) == napi_ok) { 341 | this->watchingPath = 1; 342 | } else { 343 | napi_value v; 344 | napi_create_string_utf16(env, (char16_t*)(this->parenthnd == INVALID_HANDLE_VALUE && this->parentwork ? SYB_EVT_ERR_INITIALIZATION_FAILED : SYB_EVT_ERR_UNABLE_TO_CONTINUE_WATCHING), NAPI_AUTO_LENGTH, &v); 345 | this->callCb(env, SYB_EVT_ERR, v); 346 | this->stopWatching(env); 347 | } 348 | } 349 | void watchParent(napi_env env) { 350 | if (napi_queue_async_work(env, this->parentwork) == napi_ok) { 351 | this->watchingParent = 1; 352 | } else { 353 | napi_value v; 354 | napi_create_string_utf16(env, (char16_t*)SYB_EVT_ERR_UNABLE_TO_WATCH_SELF, NAPI_AUTO_LENGTH, &v); 355 | this->callCb(env, SYB_EVT_ERR, v); 356 | this->stopWatchingParent(env); 357 | } 358 | } 359 | void savePath(napi_env env, bool starting = false) { 360 | wchar_t* realPath = getCurrentPathByHandle(this->pathhnd); 361 | bool e = false; 362 | if (realPath) { 363 | delete[]this->path; 364 | this->path = realPath; 365 | splitPath::splitedPath* sp = splitPath::func(this->path); 366 | if (sp->parentLen > 0) { 367 | if (this->shortName) { 368 | delete[]this->shortName; 369 | this->shortName = NULL; 370 | } 371 | if (this->longName) { 372 | delete[]this->longName; 373 | this->longName = NULL; 374 | } 375 | find::resultData* fr = find::func(this->path); 376 | if (fr) { 377 | size_t l = wcslen(fr->data.cFileName) + 1; 378 | this->longName = new wchar_t[l]; 379 | wcscpy_s(this->longName, l, fr->data.cFileName); 380 | if (wcslen(fr->data.cAlternateFileName) > 0 && wcscmp(fr->data.cFileName, fr->data.cAlternateFileName) != 0) { 381 | l = wcslen(fr->data.cAlternateFileName) + 1; 382 | this->shortName = new wchar_t[l]; 383 | wcscpy_s(this->shortName, l, fr->data.cAlternateFileName); 384 | } 385 | delete fr; 386 | } 387 | if (this->longName) { 388 | size_t l = (size_t)sp->parentLen + 1; 389 | wchar_t* parent = new wchar_t[l]; 390 | wcsncpy_s(parent, l, realPath, sp->parentLen); 391 | this->parenthnd = CreateFileW(parent, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); 392 | delete[]parent; 393 | if (this->parenthnd == INVALID_HANDLE_VALUE) { 394 | e = true; 395 | } else if (starting) { 396 | if (napi_queue_async_work(env, this->parentwork) == napi_ok) { 397 | this->watchingParent = 1; 398 | } else { 399 | e = true; 400 | } 401 | } 402 | } 403 | } else { 404 | this->stopWatchingParent(env); 405 | } 406 | delete sp; 407 | } else { 408 | e = true; 409 | } 410 | msg* m = new msg; 411 | m->content = this->path; 412 | if (starting) { 413 | m->type = "STARTED"; 414 | this->pathmsg = m; 415 | } else { 416 | m->type = "MOVED"; 417 | this->parentmsg = m; 418 | } 419 | if (e) { 420 | m->next = new msg; 421 | m->next->type = SYB_EVT_ERR; 422 | m->next->content = SYB_EVT_ERR_UNABLE_TO_WATCH_SELF; 423 | m->next->next = NULL; 424 | this->stopWatchingParent(env); 425 | } else { 426 | m->next = NULL; 427 | } 428 | } 429 | void stopWatching(napi_env env) { 430 | if (this->pathhnd != INVALID_HANDLE_VALUE) { 431 | CloseHandle(this->pathhnd); 432 | this->pathhnd = INVALID_HANDLE_VALUE; 433 | } 434 | if (this->newName) { 435 | delete[]this->newName; 436 | this->newName = NULL; 437 | } 438 | if (this->oldName) { 439 | delete[]this->oldName; 440 | this->oldName = NULL; 441 | } 442 | if (this->watchingPath == 1) { 443 | napi_cancel_async_work(env, this->pathwork); 444 | } 445 | this->stopWatchingParent(env); 446 | this->checkWatchingStoped(env); 447 | } 448 | void stopWatchingParent(napi_env env) { 449 | if (this->parenthnd != INVALID_HANDLE_VALUE) { 450 | BOOL res = CloseHandle(this->parenthnd); 451 | this->parenthnd = INVALID_HANDLE_VALUE; 452 | } 453 | if (this->longName) { 454 | delete[]this->longName; 455 | this->longName = NULL; 456 | } 457 | if (this->shortName) { 458 | delete[]this->shortName; 459 | this->shortName = NULL; 460 | } 461 | if (this->watchingParent == 1) { 462 | napi_cancel_async_work(env, this->parentwork); 463 | } 464 | } 465 | void checkWatchingStoped(napi_env env) { 466 | if (this->watchingPath == 0 && this->watchingParent == 0) { 467 | napi_value v; 468 | napi_get_undefined(env, &v); 469 | this->callCb(env, "ENDED", v); 470 | napi_delete_async_work(env, this->pathwork); 471 | napi_delete_async_work(env, this->parentwork); 472 | delete[]this->path; 473 | if (!this->wrapper) { 474 | delete this; 475 | } 476 | } 477 | } 478 | void callCb(napi_env env, const char* evt_type, napi_value content) { 479 | if (this->callback && this->wrapper) { 480 | napi_value argv[2], func, that; 481 | napi_create_string_latin1(env, evt_type, NAPI_AUTO_LENGTH, &argv[0]); 482 | argv[1] = content; 483 | napi_get_reference_value(env, this->callback, &func); 484 | napi_get_reference_value(env, this->wrapper, &that); 485 | napi_call_function(env, that, func, 2, &argv[0], NULL); 486 | } 487 | } 488 | }; 489 | napi_ref dirWatcher::constructor; -------------------------------------------------------------------------------- /src/ejectDrive.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class ejectDrive { 5 | public: 6 | static bool func(const char l, const uint32_t method = 0) { 7 | bool result = false; 8 | if (method) { 9 | DEVINST DevInst = getDriveDevice::getParentDevInst(l); 10 | if (DevInst) { 11 | wchar_t* devid = getDriveDevice::getDevInstIdByDevInst(DevInst); 12 | if (devid) { 13 | if (method == 1) { 14 | DWORD cap = getDeviceCapabilities::func(devid); 15 | if (cap & CM_DEVCAP_SURPRISEREMOVALOK) { 16 | if (cap & CM_DEVCAP_DOCKDEVICE) { 17 | ensurePrivilege("SeLoadDriverPrivilege"); 18 | } else { 19 | ensurePrivilege("SeUndockPrivilege"); 20 | } 21 | result = CM_Request_Device_EjectW(DevInst, NULL, NULL, 0, 0) == CR_SUCCESS; 22 | } else { 23 | ensurePrivilege("SeLoadDriverPrivilege"); 24 | result = CM_Query_And_Remove_SubTreeW(DevInst, NULL, NULL, 0, CM_REMOVE_NO_RESTART) == CR_SUCCESS; 25 | } 26 | } else if (HotPlugEjectDevice) { 27 | result = HotPlugEjectDevice(NULL, devid, 0) == CR_SUCCESS; 28 | } 29 | delete[]devid; 30 | } 31 | } 32 | } else { 33 | char p1[] = { '\\', '\\', '?', '\\', l, ':', 0 }; 34 | HANDLE handle = CreateFileA(p1, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 35 | if (handle != INVALID_HANDLE_VALUE) { 36 | DWORD bytes = 0; 37 | PREVENT_MEDIA_REMOVAL d; 38 | d.PreventMediaRemoval = FALSE; 39 | if (DeviceIoControl(handle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytes, NULL) && DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &bytes, NULL) && DeviceIoControl(handle, IOCTL_STORAGE_MEDIA_REMOVAL, &d, sizeof(PREVENT_MEDIA_REMOVAL), NULL, 0, &bytes, NULL) && DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &bytes, NULL)) { 40 | result = true; 41 | } 42 | CloseHandle(handle); 43 | } 44 | } 45 | return result; 46 | } 47 | static napi_value init(napi_env env, bool isSync = false) { 48 | napi_value f; 49 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 50 | return f; 51 | } 52 | private: 53 | typedef CONFIGRET(WINAPI* HPED)(HWND hWnd, PCWSTR DeviceInstanceId, DWORD dwFlags); 54 | static HPED HotPlugEjectDevice; 55 | const struct cbdata { 56 | napi_async_work work; 57 | napi_ref self; 58 | napi_ref cb; 59 | char l; 60 | uint32_t method; 61 | bool result; 62 | }; 63 | static napi_value sync(napi_env env, napi_callback_info info) { 64 | napi_value result; 65 | napi_get_new_target(env, info, &result); 66 | if (result) { 67 | result = NULL; 68 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 69 | } else { 70 | napi_value argv[2]; 71 | size_t argc = 2; 72 | napi_get_cb_info(env, info, &argc, argv, NULL, NULL); 73 | if (argc >= 2) { 74 | size_t str_len; 75 | napi_value tmp; 76 | napi_coerce_to_string(env, argv[0], &tmp); 77 | napi_get_value_string_latin1(env, tmp, NULL, 0, &str_len); 78 | if (str_len > 0) { 79 | char path[2]; 80 | napi_get_value_string_latin1(env, tmp, path, 2, NULL); 81 | if ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z')) { 82 | uint32_t method; 83 | napi_coerce_to_number(env, argv[1], &tmp); 84 | napi_get_value_uint32(env, tmp, &method); 85 | napi_get_boolean(env, func(path[0], method), &result); 86 | } 87 | } 88 | } 89 | if (!result) { 90 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 91 | } 92 | } 93 | return result; 94 | } 95 | static napi_value async(napi_env env, napi_callback_info info) { 96 | napi_value result; 97 | napi_get_new_target(env, info, &result); 98 | if (result) { 99 | result = NULL; 100 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 101 | } else { 102 | napi_value argv[3], self; 103 | size_t argc = 3; 104 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 105 | if (argc >= 3) { 106 | napi_valuetype t; 107 | napi_typeof(env, argv[2], &t); 108 | if (t == napi_function) { 109 | cbdata* data = new cbdata; 110 | size_t str_len; 111 | napi_value tmp; 112 | napi_coerce_to_number(env, argv[1], &tmp); 113 | napi_get_value_uint32(env, tmp, &data->method); 114 | napi_coerce_to_string(env, argv[0], &tmp); 115 | napi_get_value_string_latin1(env, tmp, NULL, 0, &str_len); 116 | if (str_len > 0) { 117 | char path[2]; 118 | napi_get_value_string_latin1(env, tmp, path, 2, NULL); 119 | if ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z')) { 120 | data->l = path[0]; 121 | napi_create_reference(env, argv[2], 1, &data->cb); 122 | napi_create_reference(env, self, 1, &data->self); 123 | napi_create_string_latin1(env, "fswin.ejectDrive", NAPI_AUTO_LENGTH, &tmp); 124 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 125 | if (napi_queue_async_work(env, data->work) == napi_ok) { 126 | napi_get_boolean(env, true, &result); 127 | } else { 128 | napi_get_boolean(env, false, &result); 129 | napi_delete_reference(env, data->cb); 130 | napi_delete_reference(env, data->self); 131 | napi_delete_async_work(env, data->work); 132 | delete data; 133 | } 134 | } 135 | } 136 | } 137 | } 138 | if (!result) { 139 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 140 | } 141 | } 142 | return result; 143 | } 144 | static void execute(napi_env env, void* data) { 145 | cbdata* d = (cbdata*)data; 146 | d->result = func(d->l, d->method); 147 | } 148 | static void complete(napi_env env, napi_status status, void* data) { 149 | cbdata* d = (cbdata*)data; 150 | napi_value cb, self, argv; 151 | napi_get_reference_value(env, d->cb, &cb); 152 | napi_get_reference_value(env, d->self, &self); 153 | napi_get_boolean(env, status == napi_ok && d->result, &argv); 154 | napi_call_function(env, self, cb, 1, &argv, NULL); 155 | napi_delete_reference(env, d->cb); 156 | napi_delete_reference(env, d->self); 157 | napi_delete_async_work(env, d->work); 158 | delete d; 159 | } 160 | }; 161 | 162 | ejectDrive::HPED ejectDrive::HotPlugEjectDevice = (ejectDrive::HPED)GetProcAddress(LoadLibraryA("HotPlug.dll"), "HotPlugEjectDevice"); -------------------------------------------------------------------------------- /src/find.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class find { 5 | public: 6 | const struct resultData {//this is a linked table 7 | WIN32_FIND_DATAW data; 8 | resultData* next; 9 | }; 10 | //progressive callback type, if this callback returns true, the search will stop immediately. the contents of info will be rewritten or released after the callback returns, so make a copy before starting a new thread if you still need to use it 11 | typedef bool(*findResultCall)(const WIN32_FIND_DATAW* info, void* data); 12 | static resultData* func(const wchar_t* path) {//you have to delete every linked data yourself if it is not NULL 13 | resultData* result = new resultData; 14 | CHAR bak; 15 | if (RtlSetThreadPlaceholderCompatibilityMode) { 16 | bak = RtlSetThreadPlaceholderCompatibilityMode(2); 17 | } 18 | HANDLE hnd = FindFirstFileExW(path, FindExInfoStandard, &result->data, FindExSearchNameMatch, NULL, NULL); 19 | if (hnd == INVALID_HANDLE_VALUE) { 20 | delete result; 21 | result = NULL; 22 | } else { 23 | resultData* resultnew,* resultold; 24 | if (isValidInfo(&result->data)) { 25 | resultnew = new resultData; 26 | resultold = result; 27 | } else { 28 | resultnew = result; 29 | resultold = NULL; 30 | result = NULL; 31 | } 32 | while (FindNextFileW(hnd, &resultnew->data)) { 33 | if (isValidInfo(&resultnew->data)) { 34 | if (resultold) { 35 | resultold->next = resultnew; 36 | } else { 37 | result = resultnew; 38 | } 39 | resultold = resultnew; 40 | resultnew = new resultData; 41 | } 42 | } 43 | if (resultold) { 44 | resultold->next = NULL; 45 | } 46 | FindClose(hnd); 47 | if (resultnew != result) { 48 | delete resultnew; 49 | } 50 | } 51 | if (RtlSetThreadPlaceholderCompatibilityMode && bak != 2) { 52 | RtlSetThreadPlaceholderCompatibilityMode(bak); 53 | } 54 | return result; 55 | } 56 | static DWORD funcWithCallback(const wchar_t* path, const findResultCall callback, void* data) {//data could be anything that will directly pass to the callback 57 | WIN32_FIND_DATAW info; 58 | CHAR bak; 59 | if (RtlSetThreadPlaceholderCompatibilityMode) { 60 | bak = RtlSetThreadPlaceholderCompatibilityMode(2); 61 | } 62 | HANDLE hnd = FindFirstFileExW(path, FindExInfoStandard, &info, FindExSearchNameMatch, NULL, NULL); 63 | DWORD result = 0; 64 | bool stop = false; 65 | if (hnd != INVALID_HANDLE_VALUE) { 66 | if (isValidInfo(&info)) { 67 | stop = callback(&info, data); 68 | result++; 69 | } 70 | while (!stop && FindNextFileW(hnd, &info)) { 71 | if (isValidInfo(&info)) { 72 | stop = callback(&info, data); 73 | result++; 74 | } 75 | } 76 | FindClose(hnd); 77 | } 78 | if (RtlSetThreadPlaceholderCompatibilityMode && bak != 2) { 79 | RtlSetThreadPlaceholderCompatibilityMode(bak); 80 | } 81 | return result; 82 | } 83 | static napi_value init(napi_env env, bool isSync = false) { 84 | napi_value f; 85 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 86 | return f; 87 | } 88 | private: 89 | const struct syncCbData { 90 | napi_env env; 91 | napi_value self; 92 | napi_value cb; 93 | }; 94 | const struct asyncCbData { 95 | napi_async_work work; 96 | napi_ref self; 97 | napi_ref cb; 98 | void* data; 99 | //the following data is only used in progressive mode 100 | HANDLE hnd; 101 | size_t count; 102 | bool stop; 103 | }; 104 | static napi_value sync(napi_env env, napi_callback_info info) { 105 | napi_value result; 106 | napi_get_new_target(env, info, &result); 107 | if (result) { 108 | result = NULL; 109 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 110 | } else { 111 | napi_value argv[2], self; 112 | size_t argc = 2; 113 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 114 | if (argc < 1) { 115 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 116 | } else { 117 | size_t str_len; 118 | napi_value tmp, cb = NULL; 119 | napi_coerce_to_string(env, argv[0], &tmp); 120 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 121 | str_len += 1; 122 | wchar_t* str = new wchar_t[str_len]; 123 | napi_get_value_string_utf16(env, tmp, (char16_t*)str, str_len, NULL); 124 | if (argc > 1) { 125 | napi_valuetype t; 126 | napi_typeof(env, argv[1], &t); 127 | if (t == napi_function) { 128 | cb = argv[1]; 129 | } 130 | } 131 | if (cb) { 132 | syncCbData d = { env, self, cb }; 133 | napi_create_int64(env, (int64_t)funcWithCallback(str, syncCallback, &d), &result); 134 | } else { 135 | result = resultDataToArray(env, func(str)); 136 | } 137 | delete[]str; 138 | } 139 | } 140 | return result; 141 | } 142 | static napi_value async(napi_env env, napi_callback_info info) { 143 | napi_value result; 144 | napi_get_new_target(env, info, &result); 145 | if (result) { 146 | result = NULL; 147 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 148 | } else { 149 | napi_value argv[3], self; 150 | size_t argc = 3; 151 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 152 | if (argc >= 2) { 153 | napi_valuetype t; 154 | napi_typeof(env, argv[1], &t); 155 | if (t == napi_function) { 156 | bool isProgressive = false; 157 | asyncCbData* data = new asyncCbData; 158 | size_t str_len; 159 | napi_value tmp; 160 | napi_coerce_to_string(env, argv[0], &tmp); 161 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 162 | str_len += 1; 163 | data->data = new wchar_t[str_len]; 164 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->data, str_len, NULL); 165 | if (argc > 2) { 166 | napi_coerce_to_bool(env, argv[2], &tmp); 167 | napi_get_value_bool(env, tmp, &isProgressive); 168 | } 169 | napi_create_reference(env, argv[1], 1, &data->cb); 170 | napi_create_reference(env, self, 1, &data->self); 171 | if (isProgressive) { 172 | data->hnd = INVALID_HANDLE_VALUE; 173 | data->count = 0; 174 | data->stop = false; 175 | } else { 176 | data->hnd = NULL; 177 | } 178 | napi_create_string_latin1(env, "fswin.find", NAPI_AUTO_LENGTH, &tmp); 179 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 180 | if (napi_queue_async_work(env, data->work) == napi_ok) { 181 | napi_get_boolean(env, true, &result); 182 | } else { 183 | napi_get_boolean(env, false, &result); 184 | napi_delete_async_work(env, data->work); 185 | napi_delete_reference(env, data->cb); 186 | napi_delete_reference(env, data->self); 187 | delete[]data->data; 188 | delete data; 189 | } 190 | } 191 | } 192 | if (!result) { 193 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 194 | } 195 | } 196 | return result; 197 | } 198 | static bool isValidInfo(const WIN32_FIND_DATAW* info) {//determine whether it is the real content 199 | return wcscmp(info->cFileName, L".") != 0 && wcscmp(info->cFileName, L"..") != 0; 200 | } 201 | static napi_value resultDataToArray(napi_env env, resultData* r) { 202 | napi_value result; 203 | napi_create_array(env, &result); 204 | if (r) { 205 | napi_value push, tmp; 206 | napi_get_prototype(env, result, &tmp); 207 | napi_get_named_property(env, tmp, "push", &push); 208 | while (r) { 209 | tmp = convert(env, &r->data); 210 | napi_call_function(env, result, push, 1, &tmp, NULL); 211 | resultData* n = r->next; 212 | delete r; 213 | r = n; 214 | } 215 | } 216 | return result; 217 | } 218 | static napi_value convert(napi_env env, const WIN32_FIND_DATAW* info) { 219 | napi_value result, tmp, date; 220 | napi_create_object(env, &result); 221 | napi_get_global(env, &date); 222 | napi_get_named_property(env, date, "Date", &date); 223 | napi_create_string_utf16(env, (char16_t*)info->cFileName, wcslen(info->cFileName), &tmp); 224 | napi_set_named_property(env, result, "LONG_NAME", tmp); 225 | napi_create_string_utf16(env, (char16_t*)info->cFileName, wcslen(info->cAlternateFileName), &tmp); 226 | napi_set_named_property(env, result, "SHORT_NAME", tmp); 227 | napi_create_int64(env, fileTimeToJsDateVal(&info->ftCreationTime), &tmp); 228 | napi_new_instance(env, date, 1, &tmp, &tmp); 229 | napi_set_named_property(env, result, SYB_FILEATTR_CREATIONTIME, tmp); 230 | napi_create_int64(env, fileTimeToJsDateVal(&info->ftLastAccessTime), &tmp); 231 | napi_new_instance(env, date, 1, &tmp, &tmp); 232 | napi_set_named_property(env, result, SYB_FILEATTR_LASTACCESSTIME, tmp); 233 | napi_create_int64(env, fileTimeToJsDateVal(&info->ftLastWriteTime), &tmp); 234 | napi_new_instance(env, date, 1, &tmp, &tmp); 235 | napi_set_named_property(env, result, SYB_FILEATTR_LASTWRITETIME, tmp); 236 | napi_create_int64(env, combineHiLow(info->nFileSizeHigh, info->nFileSizeLow), &tmp); 237 | napi_set_named_property(env, result, SYB_FILEATTR_SIZE, tmp); 238 | napi_create_int32(env, info->dwFileAttributes, &tmp); 239 | napi_set_named_property(env, result, SYB_FILEATTR_RAWATTRS, tmp); 240 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE, &tmp); 241 | napi_set_named_property(env, result, SYB_FILEATTR_ISARCHIVED, tmp); 242 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED, &tmp); 243 | napi_set_named_property(env, result, SYB_FILEATTR_ISCOMPRESSED, tmp); 244 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_DEVICE, &tmp); 245 | napi_set_named_property(env, result, SYB_FILEATTR_ISDEVICE, tmp); 246 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY, &tmp); 247 | napi_set_named_property(env, result, SYB_FILEATTR_ISDIRECTORY, tmp); 248 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED, &tmp); 249 | napi_set_named_property(env, result, SYB_FILEATTR_ISENCRYPTED, tmp); 250 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN, &tmp); 251 | napi_set_named_property(env, result, SYB_FILEATTR_ISHIDDEN, tmp); 252 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, &tmp); 253 | napi_set_named_property(env, result, SYB_FILEATTR_ISNOTCONTENTINDEXED, tmp); 254 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_OFFLINE, &tmp); 255 | napi_set_named_property(env, result, SYB_FILEATTR_ISOFFLINE, tmp); 256 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_READONLY, &tmp); 257 | napi_set_named_property(env, result, SYB_FILEATTR_ISREADONLY, tmp); 258 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE, &tmp); 259 | napi_set_named_property(env, result, SYB_FILEATTR_ISSPARSEFILE, tmp); 260 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM, &tmp); 261 | napi_set_named_property(env, result, SYB_FILEATTR_ISSYSTEM, tmp); 262 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY, &tmp); 263 | napi_set_named_property(env, result, SYB_FILEATTR_ISTEMPORARY, tmp); 264 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM, &tmp); 265 | napi_set_named_property(env, result, SYB_FILEATTR_ISINTEGERITYSTREAM, tmp); 266 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA, &tmp); 267 | napi_set_named_property(env, result, SYB_FILEATTR_ISNOSCRUBDATA, tmp); 268 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS, &tmp); 269 | napi_set_named_property(env, result, SYB_FILEATTR_ISRECALLONDATAACCESS, tmp); 270 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_RECALL_ON_OPEN, &tmp); 271 | napi_set_named_property(env, result, SYB_FILEATTR_ISRECALLONOPEN, tmp); 272 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_VIRTUAL, &tmp); 273 | napi_set_named_property(env, result, SYB_FILEATTR_ISVIRTUAL, tmp); 274 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_EA, &tmp); 275 | napi_set_named_property(env, result, SYB_FILEATTR_ISEA, tmp); 276 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_PINNED, &tmp); 277 | napi_set_named_property(env, result, SYB_FILEATTR_ISPINNED, tmp); 278 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_UNPINNED, &tmp); 279 | napi_set_named_property(env, result, SYB_FILEATTR_ISUNPINNED, tmp); 280 | const char* tag; 281 | if (info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { 282 | if (info->dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) { 283 | tag = "MOUNT_POINT"; 284 | } else if (info->dwReserved0 == IO_REPARSE_TAG_HSM) { 285 | tag = "HSM"; 286 | } else if (info->dwReserved0 == IO_REPARSE_TAG_HSM2) { 287 | tag = "HSM2"; 288 | } else if (info->dwReserved0 == IO_REPARSE_TAG_SIS) { 289 | tag = "SIS"; 290 | } else if (info->dwReserved0 == IO_REPARSE_TAG_WIM) { 291 | tag = "WIM"; 292 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CSV) { 293 | tag = "CSV"; 294 | } else if (info->dwReserved0 == IO_REPARSE_TAG_DFS) { 295 | tag = "DFS"; 296 | } else if (info->dwReserved0 == IO_REPARSE_TAG_SYMLINK) { 297 | tag = "SYMLINK"; 298 | } else if (info->dwReserved0 == IO_REPARSE_TAG_DFSR) { 299 | tag = "DFSR"; 300 | } else if (info->dwReserved0 == IO_REPARSE_TAG_DEDUP) { 301 | tag = "DEDUP"; 302 | } else if (info->dwReserved0 == IO_REPARSE_TAG_NFS) { 303 | tag = "NFS"; 304 | } else if (info->dwReserved0 == IO_REPARSE_TAG_FILE_PLACEHOLDER) { 305 | tag = "FILE_PLACEHOLDER"; 306 | } else if (info->dwReserved0 == IO_REPARSE_TAG_WOF) { 307 | tag = "WOF"; 308 | } else if (info->dwReserved0 == IO_REPARSE_TAG_WCI) { 309 | tag = "WCI"; 310 | } else if (info->dwReserved0 == IO_REPARSE_TAG_WCI_1) { 311 | tag = "WCI_1"; 312 | } else if (info->dwReserved0 == IO_REPARSE_TAG_GLOBAL_REPARSE) { 313 | tag = "GLOBAL_REPARSE"; 314 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD) { 315 | tag = "CLOUD"; 316 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_1) { 317 | tag = "CLOUD_1"; 318 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_2) { 319 | tag = "CLOUD_2"; 320 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_3) { 321 | tag = "CLOUD_3"; 322 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_4) { 323 | tag = "CLOUD_4"; 324 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_5) { 325 | tag = "CLOUD_5"; 326 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_6) { 327 | tag = "CLOUD_6"; 328 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_7) { 329 | tag = "CLOUD_7"; 330 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_8) { 331 | tag = "CLOUD_8"; 332 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_9) { 333 | tag = "CLOUD_9"; 334 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_A) { 335 | tag = "CLOUD_A"; 336 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_B) { 337 | tag = "CLOUD_B"; 338 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_C) { 339 | tag = "CLOUD_C"; 340 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_D) { 341 | tag = "CLOUD_D"; 342 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_E) { 343 | tag = "CLOUD_E"; 344 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_F) { 345 | tag = "CLOUD_F"; 346 | } else if (info->dwReserved0 == IO_REPARSE_TAG_CLOUD_MASK) { 347 | tag = "CLOUD_MASK"; 348 | } else if (info->dwReserved0 == IO_REPARSE_TAG_APPEXECLINK) { 349 | tag = "APPEXECLINK"; 350 | } else if (info->dwReserved0 == IO_REPARSE_TAG_PROJFS) { 351 | tag = "PROJFS"; 352 | } else if (info->dwReserved0 == IO_REPARSE_TAG_STORAGE_SYNC) { 353 | tag = "STORAGE_SYNC"; 354 | } else if (info->dwReserved0 == IO_REPARSE_TAG_WCI_TOMBSTONE) { 355 | tag = "WCI_TOMBSTONE"; 356 | } else if (info->dwReserved0 == IO_REPARSE_TAG_UNHANDLED) { 357 | tag = "UNHANDLED"; 358 | } else if (info->dwReserved0 == IO_REPARSE_TAG_ONEDRIVE) { 359 | tag = "ONEDRIVE"; 360 | } else if (info->dwReserved0 == IO_REPARSE_TAG_PROJFS_TOMBSTONE) { 361 | tag = "PROJFS_TOMBSTONE"; 362 | } else if (info->dwReserved0 == IO_REPARSE_TAG_AF_UNIX) { 363 | tag = "AF_UNIX"; 364 | } else if (info->dwReserved0 == IO_REPARSE_TAG_WCI_LINK) { 365 | tag = "WCI_LINK"; 366 | } else if (info->dwReserved0 == IO_REPARSE_TAG_WCI_LINK_1) { 367 | tag = "WCI_LINK_1"; 368 | } else if (info->dwReserved0 == IO_REPARSE_TAG_DATALESS_CIM) { 369 | tag = "DATALESS_CIM"; 370 | } else { 371 | tag = NULL; 372 | } 373 | } else { 374 | tag = ""; 375 | } 376 | if (tag) { 377 | napi_create_string_latin1(env, tag, NAPI_AUTO_LENGTH, &tmp); 378 | } else { 379 | napi_create_int32(env, info->dwReserved0, &tmp); 380 | } 381 | napi_set_named_property(env, result, "REPARSE_POINT_TAG", tmp); 382 | return result; 383 | } 384 | static bool syncCallback(const WIN32_FIND_DATAW* info, void* data) { 385 | bool result; 386 | syncCbData* d = (syncCbData*)data; 387 | napi_value res, o = convert(d->env, info); 388 | napi_call_function(d->env, d->self, d->cb, 1, &o, &res); 389 | napi_coerce_to_bool(d->env, res, &res); 390 | napi_get_value_bool(d->env, res, &result); 391 | return result; 392 | } 393 | static void execute(napi_env env, void* data) { 394 | asyncCbData* d = (asyncCbData*)data; 395 | if (d->hnd) { 396 | WIN32_FIND_DATAW* info = new WIN32_FIND_DATAW; 397 | if (d->hnd == INVALID_HANDLE_VALUE) { 398 | CHAR bak; 399 | if ((void*)RtlSetThreadPlaceholderCompatibilityMode) { 400 | bak = RtlSetThreadPlaceholderCompatibilityMode(2); 401 | } 402 | d->hnd = FindFirstFileExW((wchar_t*)d->data, FindExInfoStandard, info, FindExSearchNameMatch, NULL, NULL); 403 | delete[]d->data; 404 | if (d->hnd != INVALID_HANDLE_VALUE) { 405 | while (!isValidInfo(info)) { 406 | if (!FindNextFileW(d->hnd, info)) { 407 | FindClose(d->hnd); 408 | d->hnd = INVALID_HANDLE_VALUE; 409 | break; 410 | } 411 | } 412 | } 413 | if ((void*)RtlSetThreadPlaceholderCompatibilityMode && bak != 2) { 414 | RtlSetThreadPlaceholderCompatibilityMode(bak); 415 | } 416 | } else { 417 | if (d->stop) { 418 | FindClose(d->hnd); 419 | d->hnd = INVALID_HANDLE_VALUE; 420 | } else { 421 | CHAR bak; 422 | if ((void*)RtlSetThreadPlaceholderCompatibilityMode) { 423 | bak = RtlSetThreadPlaceholderCompatibilityMode(2); 424 | } 425 | if (FindNextFileW(d->hnd, info)) { 426 | while (!isValidInfo(info)) { 427 | if (!FindNextFileW(d->hnd, info)) { 428 | FindClose(d->hnd); 429 | d->hnd = INVALID_HANDLE_VALUE; 430 | break; 431 | } 432 | } 433 | } else { 434 | FindClose(d->hnd); 435 | d->hnd = INVALID_HANDLE_VALUE; 436 | } 437 | if ((void*)RtlSetThreadPlaceholderCompatibilityMode && bak != 2) { 438 | RtlSetThreadPlaceholderCompatibilityMode(bak); 439 | } 440 | } 441 | } 442 | if (d->hnd == INVALID_HANDLE_VALUE) { 443 | delete info; 444 | } else { 445 | d->data = info; 446 | } 447 | } else { 448 | resultData* rdata = func((wchar_t*)d->data); 449 | delete[]d->data; 450 | d->data = rdata; 451 | } 452 | } 453 | static void complete(napi_env env, napi_status status, void* data) { 454 | asyncCbData* d = (asyncCbData*)data; 455 | napi_value cb, self; 456 | napi_get_reference_value(env, d->cb, &cb); 457 | napi_get_reference_value(env, d->self, &self); 458 | BYTE finish = 0; 459 | if (status == napi_ok) { 460 | if (d->hnd) { 461 | napi_value result, argv[2]; 462 | if (d->hnd == INVALID_HANDLE_VALUE) { 463 | napi_create_string_latin1(env, "SUCCEEDED", NAPI_AUTO_LENGTH, &argv[0]); 464 | napi_create_int64(env, (int64_t)d->count, &argv[1]); 465 | finish = 1; 466 | } else { 467 | WIN32_FIND_DATAW* info = (WIN32_FIND_DATAW*)d->data; 468 | if (d->stop) { 469 | napi_create_string_latin1(env, "INTERRUPTED", NAPI_AUTO_LENGTH, &argv[0]); 470 | napi_create_int64(env, (int64_t)d->count, &argv[1]); 471 | finish = 1; 472 | } else { 473 | d->count++; 474 | napi_create_string_latin1(env, "FOUND", NAPI_AUTO_LENGTH, &argv[0]); 475 | argv[1] = convert(env, info); 476 | if (napi_queue_async_work(env, d->work) != napi_ok) { 477 | finish = 2; 478 | } 479 | } 480 | delete info; 481 | } 482 | napi_call_function(env, self, cb, 2, (napi_value*)&argv, &result); 483 | napi_coerce_to_bool(env, result, &result); 484 | napi_get_value_bool(env, result, &d->stop); 485 | } else { 486 | napi_value argv = resultDataToArray(env, (resultData*)d->data); 487 | napi_call_function(env, self, cb, 1, &argv, NULL); 488 | finish = 1; 489 | } 490 | } else { 491 | if (d->hnd) { 492 | finish = 2; 493 | if (d->hnd != INVALID_HANDLE_VALUE) { 494 | FindClose(d->hnd); 495 | } 496 | } else { 497 | finish = 1; 498 | napi_value argv; 499 | napi_get_null(env, &argv); 500 | napi_call_function(env, self, cb, 1, &argv, NULL); 501 | } 502 | } 503 | if (finish) { 504 | if (finish > 1) { 505 | napi_value argv[2]; 506 | napi_create_string_latin1(env, "FAILED", NAPI_AUTO_LENGTH, &argv[0]); 507 | napi_create_int64(env, (int64_t)d->count, &argv[1]); 508 | napi_call_function(env, self, cb, 2, (napi_value*)&argv, NULL); 509 | } 510 | napi_delete_reference(env, d->cb); 511 | napi_delete_reference(env, d->self); 512 | napi_delete_async_work(env, d->work); 513 | delete d; 514 | } 515 | } 516 | }; -------------------------------------------------------------------------------- /src/fsWin.cpp: -------------------------------------------------------------------------------- 1 | #include "dirWatcher.h" 2 | #include "splitPath.h" 3 | #include "convertPath.h" 4 | #include "find.h" 5 | #include "getLogicalDriveList.h" 6 | #include "getDriveDevice.h" 7 | #include "getStorageProperties.h" 8 | #include "getDeviceCapabilities.h" 9 | #include "getVolumeInformation.h" 10 | #include "getVolumeSpace.h" 11 | #include "setVolumeLabel.h" 12 | #include "getAttributes.h" 13 | #include "setAttributes.h" 14 | #include "ejectDrive.h" 15 | #include "setShortName.h" 16 | #include "getCompressedSize.h" 17 | #include "setCompression.h" 18 | #include "setSparse.h" 19 | 20 | NAPI_MODULE_INIT() { 21 | napi_value o; 22 | napi_create_string_latin1(env, "3.24.829", NAPI_AUTO_LENGTH, &o); 23 | napi_set_named_property(env, exports, "version", o); 24 | napi_set_named_property(env, exports, "dirWatcher", dirWatcher::init(env)); 25 | napi_set_named_property(env, exports, "splitPath", splitPath::init(env)); 26 | napi_set_named_property(env, exports, "convertPath", convertPath::init(env)); 27 | napi_set_named_property(env, exports, "convertPathSync", convertPath::init(env, true)); 28 | napi_set_named_property(env, exports, "find", find::init(env)); 29 | napi_set_named_property(env, exports, "findSync", find::init(env, true)); 30 | napi_set_named_property(env, exports, "getLogicalDriveList", getLogicalDriveList::init(env)); 31 | napi_set_named_property(env, exports, "getLogicalDriveListSync", getLogicalDriveList::init(env, true)); 32 | napi_set_named_property(env, exports, "getDriveDevice", getDriveDevice::init(env)); 33 | napi_set_named_property(env, exports, "getDriveDeviceSync", getDriveDevice::init(env, true)); 34 | napi_set_named_property(env, exports, "getDeviceCapabilities", getDeviceCapabilities::init(env)); 35 | napi_set_named_property(env, exports, "getDeviceCapabilitiesSync", getDeviceCapabilities::init(env, true)); 36 | napi_set_named_property(env, exports, "getStorageProperties", getStorageProperties::init(env)); 37 | napi_set_named_property(env, exports, "getStoragePropertiesSync", getStorageProperties::init(env, true)); 38 | napi_set_named_property(env, exports, "getVolumeInformation", getVolumeInformation::init(env)); 39 | napi_set_named_property(env, exports, "getVolumeInformationSync", getVolumeInformation::init(env, true)); 40 | napi_set_named_property(env, exports, "getVolumeSpace", getVolumeSpace::init(env)); 41 | napi_set_named_property(env, exports, "getVolumeSpaceSync", getVolumeSpace::init(env, true)); 42 | napi_set_named_property(env, exports, "setVolumeLabel", setVolumeLabel::init(env)); 43 | napi_set_named_property(env, exports, "setVolumeLabelSync", setVolumeLabel::init(env, true)); 44 | napi_set_named_property(env, exports, "getAttributes", getAttributes::init(env)); 45 | napi_set_named_property(env, exports, "getAttributesSync", getAttributes::init(env, true)); 46 | napi_set_named_property(env, exports, "setAttributes", setAttributes::init(env)); 47 | napi_set_named_property(env, exports, "setAttributesSync", setAttributes::init(env, true)); 48 | napi_set_named_property(env, exports, "ejectDrive", ejectDrive::init(env)); 49 | napi_set_named_property(env, exports, "ejectDriveSync", ejectDrive::init(env, true)); 50 | napi_create_object(env, &o); 51 | napi_set_named_property(env, exports, "ntfs", o); 52 | napi_set_named_property(env, o, "setShortName", setShortName::init(env)); 53 | napi_set_named_property(env, o, "setShortNameSync", setShortName::init(env, true)); 54 | napi_set_named_property(env, o, "getCompressedSize", getCompressedSize::init(env)); 55 | napi_set_named_property(env, o, "getCompressedSizeSync", getCompressedSize::init(env, true)); 56 | napi_set_named_property(env, o, "setCompression", setCompression::init(env)); 57 | napi_set_named_property(env, o, "setCompressionSync", setCompression::init(env, true)); 58 | napi_set_named_property(env, o, "setSparse", setSparse::init(env)); 59 | napi_set_named_property(env, o, "setSparseSync", setSparse::init(env, true)); 60 | 61 | return exports; 62 | } -------------------------------------------------------------------------------- /src/getAttributes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class getAttributes { 5 | public: 6 | static napi_value init(napi_env env, bool isSync = false) { 7 | napi_value f; 8 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 9 | return f; 10 | } 11 | private: 12 | const struct cbdata { 13 | napi_async_work work; 14 | napi_ref self; 15 | napi_ref cb; 16 | wchar_t* path; 17 | BY_HANDLE_FILE_INFORMATION* result; 18 | }; 19 | static napi_value sync(napi_env env, napi_callback_info info) { 20 | napi_value result; 21 | napi_get_new_target(env, info, &result); 22 | if (result) { 23 | result = NULL; 24 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 25 | } else { 26 | napi_value argv; 27 | size_t argc = 1; 28 | napi_get_cb_info(env, info, &argc, &argv, NULL, NULL); 29 | if (argc < 1) { 30 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 31 | } else { 32 | size_t str_len; 33 | napi_value tmp; 34 | napi_coerce_to_string(env, argv, &tmp); 35 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 36 | str_len += 1; 37 | wchar_t* str = new wchar_t[str_len]; 38 | napi_get_value_string_utf16(env, tmp, (char16_t*)str, str_len, NULL); 39 | BY_HANDLE_FILE_INFORMATION data; 40 | CHAR bak; 41 | if (RtlSetThreadPlaceholderCompatibilityMode) { 42 | bak = RtlSetThreadPlaceholderCompatibilityMode(2); 43 | } 44 | HANDLE h = CreateFileW(str, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); 45 | if (GetFileInformationByHandle(h, &data)) { 46 | result = convert(env, &data); 47 | } else { 48 | napi_get_null(env, &result); 49 | } 50 | if (h != INVALID_HANDLE_VALUE) { 51 | CloseHandle(h); 52 | } 53 | if (RtlSetThreadPlaceholderCompatibilityMode && bak != 2) { 54 | RtlSetThreadPlaceholderCompatibilityMode(bak); 55 | } 56 | delete[]str; 57 | } 58 | } 59 | return result; 60 | } 61 | static napi_value async(napi_env env, napi_callback_info info) { 62 | napi_value result; 63 | napi_get_new_target(env, info, &result); 64 | if (result) { 65 | result = NULL; 66 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 67 | } else { 68 | napi_value argv[2], self; 69 | size_t argc = 2; 70 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 71 | if (argc >= 2) { 72 | napi_valuetype t; 73 | napi_typeof(env, argv[1], &t); 74 | if (t == napi_function) { 75 | cbdata* data = new cbdata; 76 | size_t str_len; 77 | napi_value tmp; 78 | napi_create_reference(env, argv[1], 1, &data->cb); 79 | napi_create_reference(env, self, 1, &data->self); 80 | napi_coerce_to_string(env, argv[0], &tmp); 81 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 82 | str_len += 1; 83 | data->path = new wchar_t[str_len]; 84 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->path, str_len, NULL); 85 | napi_create_string_latin1(env, "fswin.getAttributes", NAPI_AUTO_LENGTH, &tmp); 86 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 87 | if (napi_queue_async_work(env, data->work) == napi_ok) { 88 | napi_get_boolean(env, true, &result); 89 | } else { 90 | napi_get_boolean(env, false, &result); 91 | napi_delete_reference(env, data->cb); 92 | napi_delete_reference(env, data->self); 93 | napi_delete_async_work(env, data->work); 94 | delete[]data->path; 95 | delete data; 96 | } 97 | } 98 | } 99 | if (!result) { 100 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 101 | } 102 | } 103 | return result; 104 | } 105 | static napi_value convert(napi_env env, BY_HANDLE_FILE_INFORMATION* info) { 106 | napi_value result, tmp, date; 107 | napi_get_global(env, &date); 108 | napi_get_named_property(env, date, "Date", &date); 109 | napi_create_object(env, &result); 110 | napi_create_int64(env, fileTimeToJsDateVal(&info->ftCreationTime), &tmp); 111 | napi_new_instance(env, date, 1, &tmp, &tmp); 112 | napi_set_named_property(env, result, SYB_FILEATTR_CREATIONTIME, tmp); 113 | napi_create_int64(env, fileTimeToJsDateVal(&info->ftLastAccessTime), &tmp); 114 | napi_new_instance(env, date, 1, &tmp, &tmp); 115 | napi_set_named_property(env, result, SYB_FILEATTR_LASTACCESSTIME, tmp); 116 | napi_create_int64(env, fileTimeToJsDateVal(&info->ftLastWriteTime), &tmp); 117 | napi_new_instance(env, date, 1, &tmp, &tmp); 118 | napi_set_named_property(env, result, SYB_FILEATTR_LASTWRITETIME, tmp); 119 | napi_create_int64(env, combineHiLow(info->nFileSizeHigh, info->nFileSizeLow), &tmp); 120 | napi_set_named_property(env, result, SYB_FILEATTR_SIZE, tmp); 121 | napi_create_int32(env, info->nNumberOfLinks, &tmp); 122 | napi_set_named_property(env, result, "LINK_COUNT", tmp); 123 | napi_create_int32(env, info->dwFileAttributes, &tmp); 124 | napi_set_named_property(env, result, SYB_FILEATTR_RAWATTRS, tmp); 125 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE, &tmp); 126 | napi_set_named_property(env, result, SYB_FILEATTR_ISARCHIVED, tmp); 127 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED, &tmp); 128 | napi_set_named_property(env, result, SYB_FILEATTR_ISCOMPRESSED, tmp); 129 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_DEVICE, &tmp); 130 | napi_set_named_property(env, result, SYB_FILEATTR_ISDEVICE, tmp); 131 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY, &tmp); 132 | napi_set_named_property(env, result, SYB_FILEATTR_ISDIRECTORY, tmp); 133 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED, &tmp); 134 | napi_set_named_property(env, result, SYB_FILEATTR_ISENCRYPTED, tmp); 135 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN, &tmp); 136 | napi_set_named_property(env, result, SYB_FILEATTR_ISHIDDEN, tmp); 137 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, &tmp); 138 | napi_set_named_property(env, result, SYB_FILEATTR_ISNOTCONTENTINDEXED, tmp); 139 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_OFFLINE, &tmp); 140 | napi_set_named_property(env, result, SYB_FILEATTR_ISOFFLINE, tmp); 141 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_READONLY, &tmp); 142 | napi_set_named_property(env, result, SYB_FILEATTR_ISREADONLY, tmp); 143 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE, &tmp); 144 | napi_set_named_property(env, result, SYB_FILEATTR_ISSPARSEFILE, tmp); 145 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM, &tmp); 146 | napi_set_named_property(env, result, SYB_FILEATTR_ISSYSTEM, tmp); 147 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY, &tmp); 148 | napi_set_named_property(env, result, SYB_FILEATTR_ISTEMPORARY, tmp); 149 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM, &tmp); 150 | napi_set_named_property(env, result, SYB_FILEATTR_ISINTEGERITYSTREAM, tmp); 151 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA, &tmp); 152 | napi_set_named_property(env, result, SYB_FILEATTR_ISNOSCRUBDATA, tmp); 153 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS, &tmp); 154 | napi_set_named_property(env, result, SYB_FILEATTR_ISRECALLONDATAACCESS, tmp); 155 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_RECALL_ON_OPEN, &tmp); 156 | napi_set_named_property(env, result, SYB_FILEATTR_ISRECALLONOPEN, tmp); 157 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_VIRTUAL, &tmp); 158 | napi_set_named_property(env, result, SYB_FILEATTR_ISVIRTUAL, tmp); 159 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_EA, &tmp); 160 | napi_set_named_property(env, result, SYB_FILEATTR_ISEA, tmp); 161 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_PINNED, &tmp); 162 | napi_set_named_property(env, result, SYB_FILEATTR_ISPINNED, tmp); 163 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_UNPINNED, &tmp); 164 | napi_set_named_property(env, result, SYB_FILEATTR_ISUNPINNED, tmp); 165 | napi_get_boolean(env, info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT, &tmp); 166 | napi_set_named_property(env, result, "IS_REPARSE_POINT", tmp); 167 | return result; 168 | } 169 | static void execute(napi_env env, void* data) { 170 | cbdata* d = (cbdata*)data; 171 | d->result = new BY_HANDLE_FILE_INFORMATION; 172 | CHAR bak; 173 | if (RtlSetThreadPlaceholderCompatibilityMode) { 174 | bak = RtlSetThreadPlaceholderCompatibilityMode(2); 175 | } 176 | HANDLE h = CreateFileW(d->path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); 177 | if (!GetFileInformationByHandle(h, d->result)) { 178 | delete d->result; 179 | d->result = NULL; 180 | } 181 | if (h != INVALID_HANDLE_VALUE) { 182 | CloseHandle(h); 183 | } 184 | if (RtlSetThreadPlaceholderCompatibilityMode && bak != 2) { 185 | RtlSetThreadPlaceholderCompatibilityMode(bak); 186 | } 187 | } 188 | static void complete(napi_env env, napi_status status, void* data) { 189 | cbdata* d = (cbdata*)data; 190 | delete[]d->path; 191 | napi_value cb, self, argv; 192 | napi_get_reference_value(env, d->cb, &cb); 193 | napi_get_reference_value(env, d->self, &self); 194 | if (status == napi_ok && d->result) { 195 | argv = convert(env, d->result); 196 | delete d->result; 197 | } else { 198 | napi_get_null(env, &argv); 199 | } 200 | napi_call_function(env, self, cb, 1, &argv, NULL); 201 | napi_delete_reference(env, d->cb); 202 | napi_delete_reference(env, d->self); 203 | napi_delete_async_work(env, d->work); 204 | delete d; 205 | } 206 | }; -------------------------------------------------------------------------------- /src/getCompressedSize.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class getCompressedSize { 5 | public: 6 | static ULONGLONG func(const wchar_t* path) { 7 | ULARGE_INTEGER u; 8 | u.LowPart = GetCompressedFileSizeW(path, &u.HighPart); 9 | if (u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) { 10 | u.QuadPart = 0; 11 | } 12 | return u.QuadPart; 13 | } 14 | static napi_value init(napi_env env, bool isSync = false) { 15 | napi_value f; 16 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 17 | return f; 18 | } 19 | private: 20 | const struct cbdata { 21 | napi_async_work work; 22 | napi_ref self; 23 | napi_ref cb; 24 | wchar_t* path; 25 | ULONGLONG result; 26 | }; 27 | static napi_value sync(napi_env env, napi_callback_info info) { 28 | napi_value result; 29 | napi_get_new_target(env, info, &result); 30 | if (result) { 31 | result = NULL; 32 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 33 | } else { 34 | napi_value argv; 35 | size_t argc = 1; 36 | napi_get_cb_info(env, info, &argc, &argv, NULL, NULL); 37 | if (argc < 1) { 38 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 39 | } else { 40 | size_t str_len; 41 | napi_value tmp; 42 | napi_coerce_to_string(env, argv, &tmp); 43 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 44 | str_len += 1; 45 | wchar_t* str = new wchar_t[str_len]; 46 | napi_get_value_string_utf16(env, tmp, (char16_t*)str, str_len, NULL); 47 | napi_create_int64(env, func(str), &result); 48 | delete[]str; 49 | } 50 | } 51 | return result; 52 | } 53 | static napi_value async(napi_env env, napi_callback_info info) { 54 | napi_value result; 55 | napi_get_new_target(env, info, &result); 56 | if (result) { 57 | result = NULL; 58 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 59 | } else { 60 | napi_value argv[2], self; 61 | size_t argc = 2; 62 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 63 | if (argc >= 2) { 64 | napi_valuetype t; 65 | napi_typeof(env, argv[1], &t); 66 | if (t == napi_function) { 67 | cbdata* data = new cbdata; 68 | size_t str_len; 69 | napi_value tmp; 70 | napi_create_reference(env, argv[1], 1, &data->cb); 71 | napi_create_reference(env, self, 1, &data->self); 72 | napi_coerce_to_string(env, argv[0], &tmp); 73 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 74 | str_len += 1; 75 | data->path = new wchar_t[str_len]; 76 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->path, str_len, NULL); 77 | napi_create_string_latin1(env, "fswin.ntfs.getCompressedSize", NAPI_AUTO_LENGTH, &tmp); 78 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 79 | if (napi_queue_async_work(env, data->work) == napi_ok) { 80 | napi_get_boolean(env, true, &result); 81 | } else { 82 | napi_get_boolean(env, false, &result); 83 | napi_delete_reference(env, data->cb); 84 | napi_delete_reference(env, data->self); 85 | napi_delete_async_work(env, data->work); 86 | delete[]data->path; 87 | delete data; 88 | } 89 | } 90 | } 91 | if (!result) { 92 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 93 | } 94 | } 95 | return result; 96 | } 97 | static void execute(napi_env env, void* data) { 98 | cbdata* d = (cbdata*)data; 99 | d->result = func(d->path); 100 | delete[]d->path; 101 | } 102 | static void complete(napi_env env, napi_status status, void* data) { 103 | cbdata* d = (cbdata*)data; 104 | napi_value cb, self, argv; 105 | napi_get_reference_value(env, d->cb, &cb); 106 | napi_get_reference_value(env, d->self, &self); 107 | if (status == napi_ok) { 108 | napi_create_int64(env, d->result, &argv); 109 | } else { 110 | napi_get_null(env, &argv); 111 | } 112 | napi_call_function(env, self, cb, 1, &argv, NULL); 113 | napi_delete_reference(env, d->cb); 114 | napi_delete_reference(env, d->self); 115 | napi_delete_async_work(env, d->work); 116 | delete d; 117 | } 118 | }; -------------------------------------------------------------------------------- /src/getDeviceCapabilities.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class getDeviceCapabilities { 5 | public: 6 | static DWORD func(wchar_t* devid) { 7 | HDEVINFO hDevInfo = SetupDiGetClassDevsW(NULL, devid, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES); 8 | DWORD cap = 0; 9 | SP_DEVINFO_DATA spdd; 10 | spdd.cbSize = sizeof(SP_DEVINFO_DATA); 11 | if (SetupDiEnumDeviceInfo(hDevInfo, 0, &spdd)) { 12 | SetupDiGetDeviceRegistryPropertyA(hDevInfo, &spdd, SPDRP_CAPABILITIES, NULL, (BYTE*)&cap, sizeof(DWORD), NULL); 13 | } 14 | SetupDiDestroyDeviceInfoList(hDevInfo); 15 | return cap; 16 | } 17 | static napi_value init(napi_env env, bool isSync = false) { 18 | napi_value f; 19 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 20 | return f; 21 | } 22 | private: 23 | const struct cbdata { 24 | napi_async_work work; 25 | napi_ref self; 26 | napi_ref cb; 27 | wchar_t* DevInstId; 28 | DWORD result; 29 | }; 30 | static napi_value convert(napi_env env, DWORD data) { 31 | napi_value tmp, result; 32 | napi_create_object(env, &result); 33 | napi_get_boolean(env, data & CM_DEVCAP_LOCKSUPPORTED, &tmp); 34 | napi_set_named_property(env, result, "LOCK_SUPPORTED", tmp); 35 | napi_get_boolean(env, data & CM_DEVCAP_EJECTSUPPORTED, &tmp); 36 | napi_set_named_property(env, result, "EJECT_SUPPORTED", tmp); 37 | napi_get_boolean(env, data & CM_DEVCAP_REMOVABLE, &tmp); 38 | napi_set_named_property(env, result, "REMOVABLE", tmp); 39 | napi_get_boolean(env, data & CM_DEVCAP_DOCKDEVICE, &tmp); 40 | napi_set_named_property(env, result, "DOCK_DEVICE", tmp); 41 | napi_get_boolean(env, data & CM_DEVCAP_UNIQUEID, &tmp); 42 | napi_set_named_property(env, result, "UNIQUE_ID", tmp); 43 | napi_get_boolean(env, data & CM_DEVCAP_SILENTINSTALL, &tmp); 44 | napi_set_named_property(env, result, "SILENT_INSTALL", tmp); 45 | napi_get_boolean(env, data & CM_DEVCAP_RAWDEVICEOK, &tmp); 46 | napi_set_named_property(env, result, "RAW_DEVICE_OK", tmp); 47 | napi_get_boolean(env, data & CM_DEVCAP_SURPRISEREMOVALOK, &tmp); 48 | napi_set_named_property(env, result, "SURPRISE_REMOVAL_OK", tmp); 49 | napi_get_boolean(env, data & CM_DEVCAP_HARDWAREDISABLED, &tmp); 50 | napi_set_named_property(env, result, "HARDWARE_DISABLED", tmp); 51 | napi_get_boolean(env, data & CM_DEVCAP_NONDYNAMIC, &tmp); 52 | napi_set_named_property(env, result, "NON_DYNAMIC", tmp); 53 | return result; 54 | } 55 | static napi_value sync(napi_env env, napi_callback_info info) { 56 | napi_value result; 57 | napi_get_new_target(env, info, &result); 58 | if (result) { 59 | result = NULL; 60 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 61 | } else { 62 | napi_value argv; 63 | size_t argc = 1; 64 | napi_get_cb_info(env, info, &argc, &argv, NULL, NULL); 65 | if (argc < 1) { 66 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 67 | } else { 68 | napi_value tmp; 69 | napi_coerce_to_string(env, argv, &tmp); 70 | size_t str_len; 71 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 72 | str_len += 1; 73 | wchar_t* DevInstId = new wchar_t[str_len]; 74 | napi_get_value_string_utf16(env, tmp, (char16_t*)DevInstId, str_len, NULL); 75 | result = convert(env, func(DevInstId)); 76 | } 77 | } 78 | return result; 79 | } 80 | static napi_value async(napi_env env, napi_callback_info info) { 81 | napi_value result; 82 | napi_get_new_target(env, info, &result); 83 | if (result) { 84 | result = NULL; 85 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 86 | } else { 87 | napi_value argv[2], self; 88 | size_t argc = 2; 89 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 90 | if (argc >= 2) { 91 | napi_valuetype t; 92 | napi_typeof(env, argv[1], &t); 93 | if (t == napi_function) { 94 | cbdata* data = new cbdata; 95 | napi_value tmp; 96 | napi_coerce_to_string(env, argv[0], &tmp); 97 | size_t str_len; 98 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 99 | str_len += 1; 100 | data->DevInstId = new wchar_t[str_len]; 101 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->DevInstId, str_len, NULL); 102 | napi_create_reference(env, argv[1], 1, &data->cb); 103 | napi_create_reference(env, self, 1, &data->self); 104 | napi_create_string_latin1(env, "fswin.getDeviceCapabilities", NAPI_AUTO_LENGTH, &tmp); 105 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 106 | if (napi_queue_async_work(env, data->work) == napi_ok) { 107 | napi_get_boolean(env, true, &result); 108 | } else { 109 | napi_get_boolean(env, false, &result); 110 | napi_delete_reference(env, data->cb); 111 | napi_delete_reference(env, data->self); 112 | napi_delete_async_work(env, data->work); 113 | delete[]data->DevInstId; 114 | delete data; 115 | } 116 | } 117 | } 118 | if (!result) { 119 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 120 | } 121 | } 122 | return result; 123 | } 124 | static void execute(napi_env env, void* data) { 125 | cbdata* d = (cbdata*)data; 126 | d->result = func(d->DevInstId); 127 | } 128 | static void complete(napi_env env, napi_status status, void* data) { 129 | cbdata* d = (cbdata*)data; 130 | napi_value cb, self, argv = convert(env, d->result); 131 | napi_get_reference_value(env, d->cb, &cb); 132 | napi_get_reference_value(env, d->self, &self); 133 | napi_call_function(env, self, cb, 1, &argv, NULL); 134 | napi_delete_reference(env, d->cb); 135 | napi_delete_reference(env, d->self); 136 | napi_delete_async_work(env, d->work); 137 | delete[]d->DevInstId; 138 | delete d; 139 | } 140 | }; -------------------------------------------------------------------------------- /src/getDriveDevice.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class getDriveDevice { 5 | public: 6 | struct infor { 7 | DWORD deviceNumber; 8 | SP_DEVICE_INTERFACE_DETAIL_DATA_A* pspdidd; 9 | wchar_t* deviceId; 10 | wchar_t* parentDeviceId; 11 | }; 12 | static DEVINST getParentDevInst(const char l, infor* inf = NULL) { 13 | DEVINST DevInst = 0; 14 | if ((l >= 'A' && l <= 'Z') || (l >= 'a' && l <= 'z')) { 15 | char p1[] = { '\\', '\\', '?', '\\', l, ':', 0 }; 16 | char* p2 = &p1[4]; 17 | HANDLE hVolume = CreateFileA(p1, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 18 | if (hVolume != INVALID_HANDLE_VALUE) { 19 | STORAGE_DEVICE_NUMBER sdn; 20 | DWORD dwBytesReturned = 0; 21 | if (DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(STORAGE_DEVICE_NUMBER), &dwBytesReturned, NULL)) { 22 | CloseHandle(hVolume); 23 | UINT DriveType = GetDriveTypeA(p2); 24 | const GUID* guid = NULL; 25 | if (DriveType == DRIVE_REMOVABLE) { 26 | char szDosDeviceName[MAX_PATH + 1]; 27 | if (QueryDosDeviceA(p2, szDosDeviceName, MAX_PATH + 1)) { 28 | guid = strstr(szDosDeviceName, "\\Floppy") ? &GUID_DEVINTERFACE_FLOPPY : &GUID_DEVINTERFACE_DISK; 29 | } 30 | } else if (DriveType == DRIVE_FIXED) { 31 | guid = &GUID_DEVINTERFACE_DISK; 32 | } else if (DriveType == DRIVE_CDROM) { 33 | guid = &GUID_DEVINTERFACE_CDROM; 34 | } 35 | if (guid) { 36 | HDEVINFO hDevInfo = SetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 37 | if (hDevInfo != INVALID_HANDLE_VALUE) { 38 | DWORD dwSize = 0; 39 | DWORD dwIndex = 0; 40 | SP_DEVICE_INTERFACE_DATA spdid; 41 | SP_DEVINFO_DATA spdd; 42 | spdid.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 43 | spdd.cbSize = sizeof(SP_DEVINFO_DATA); 44 | while (!DevInst && SetupDiEnumDeviceInterfaces(hDevInfo, NULL, guid, dwIndex++, &spdid)) { 45 | SetupDiGetDeviceInterfaceDetailA(hDevInfo, &spdid, NULL, 0, &dwSize, NULL); 46 | if (dwSize > 0) { 47 | SP_DEVICE_INTERFACE_DETAIL_DATA_A* pspdidd = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*)malloc(dwSize); 48 | if (pspdidd) { 49 | pspdidd->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); 50 | if (SetupDiGetDeviceInterfaceDetailA(hDevInfo, &spdid, pspdidd, dwSize, NULL, &spdd)) { 51 | HANDLE hDrive = CreateFileA(pspdidd->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 52 | if (hDrive != INVALID_HANDLE_VALUE) { 53 | STORAGE_DEVICE_NUMBER sdn1; 54 | DWORD dwBytesReturned = 0; 55 | if (DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn1, sizeof(STORAGE_DEVICE_NUMBER), &dwBytesReturned, NULL) && sdn.DeviceNumber == sdn1.DeviceNumber) { 56 | //std::cout << sdn.DeviceNumber << pspdidd->DevicePath << std::endl; 57 | DevInst = spdd.DevInst; 58 | if (inf) { 59 | inf->deviceNumber = sdn.DeviceNumber; 60 | inf->deviceId = getDevInstIdByDevInst(DevInst); 61 | } 62 | } 63 | CloseHandle(hDrive); 64 | } 65 | } 66 | if (inf) { 67 | inf->pspdidd = pspdidd; 68 | } else { 69 | free(pspdidd); 70 | } 71 | } 72 | } 73 | } 74 | SetupDiDestroyDeviceInfoList(hDevInfo); 75 | if (DevInst && CM_Get_Parent(&DevInst, DevInst, 0) != CR_SUCCESS) { 76 | DevInst = 0; 77 | } 78 | } 79 | } 80 | } else { 81 | CloseHandle(hVolume); 82 | } 83 | } 84 | } 85 | return DevInst; 86 | } 87 | static wchar_t* getDevInstIdByDevInst(DEVINST DevInst) {//delete the result yourself if it is not NULL 88 | wchar_t* result = NULL; 89 | ULONG sz; 90 | if (CM_Get_Device_ID_Size(&sz, DevInst, 0) == CR_SUCCESS) { 91 | ++sz; 92 | result = new wchar_t[sz]; 93 | if (CM_Get_Device_IDW(DevInst, result, sz, 0) != CR_SUCCESS) { 94 | delete[]result; 95 | result = NULL; 96 | } 97 | } 98 | return result; 99 | } 100 | static infor* func(const char l) { 101 | infor* inf = new infor({ 0 }); 102 | DEVINST DevInst = getParentDevInst(l, inf); 103 | if (DevInst) { 104 | inf->parentDeviceId = getDevInstIdByDevInst(DevInst); 105 | } else { 106 | if (inf->pspdidd) { 107 | free(inf->pspdidd); 108 | } 109 | if (inf->deviceId) { 110 | delete[]inf->deviceId; 111 | } 112 | delete inf; 113 | inf = NULL; 114 | } 115 | return inf; 116 | } 117 | static napi_value init(napi_env env, bool isSync = false) { 118 | napi_value f; 119 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 120 | return f; 121 | } 122 | private: 123 | const struct cbdata { 124 | napi_async_work work; 125 | napi_ref self; 126 | napi_ref cb; 127 | char l; 128 | infor* result; 129 | }; 130 | static napi_value convert(napi_env env, infor* inf) { 131 | napi_value result, tmp; 132 | napi_create_object(env, &result); 133 | napi_create_uint32(env, inf->deviceNumber, &tmp); 134 | napi_set_named_property(env, result, "deviceNumber", tmp); 135 | napi_create_string_latin1(env, inf->pspdidd->DevicePath, NAPI_AUTO_LENGTH, &tmp); 136 | napi_set_named_property(env, result, "devicePath", tmp); 137 | napi_create_string_utf16(env, (char16_t*)inf->deviceId, NAPI_AUTO_LENGTH, &tmp); 138 | napi_set_named_property(env, result, "deviceId", tmp); 139 | napi_create_string_utf16(env, (char16_t*)inf->parentDeviceId, NAPI_AUTO_LENGTH, &tmp); 140 | napi_set_named_property(env, result, "parentDeviceId", tmp); 141 | return result; 142 | } 143 | static napi_value sync(napi_env env, napi_callback_info info) { 144 | napi_value result; 145 | napi_get_new_target(env, info, &result); 146 | if (result) { 147 | result = NULL; 148 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 149 | } else { 150 | napi_value argv; 151 | size_t argc = 1; 152 | napi_get_cb_info(env, info, &argc, &argv, NULL, NULL); 153 | if (argc < 1) { 154 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 155 | } else { 156 | size_t str_len; 157 | napi_value tmp; 158 | napi_coerce_to_string(env, argv, &tmp); 159 | napi_get_value_string_latin1(env, tmp, NULL, 0, &str_len); 160 | if (str_len > 0) { 161 | char path[2]; 162 | napi_get_value_string_latin1(env, tmp, path, 2, NULL); 163 | infor* inf = func(path[0]); 164 | if (inf) { 165 | result = convert(env, inf); 166 | free(inf->pspdidd); 167 | delete[]inf->deviceId; 168 | delete[]inf->parentDeviceId; 169 | delete inf; 170 | } 171 | } 172 | } 173 | } 174 | return result; 175 | } 176 | static napi_value async(napi_env env, napi_callback_info info) { 177 | napi_value result; 178 | napi_get_new_target(env, info, &result); 179 | if (result) { 180 | result = NULL; 181 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 182 | } else { 183 | napi_value argv[2], self; 184 | size_t argc = 2; 185 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 186 | if (argc >= 2) { 187 | napi_valuetype t; 188 | napi_typeof(env, argv[1], &t); 189 | if (t == napi_function) { 190 | cbdata* data = new cbdata; 191 | size_t str_len; 192 | napi_value tmp; 193 | napi_coerce_to_string(env, argv[0], &tmp); 194 | napi_get_value_string_latin1(env, tmp, NULL, 0, &str_len); 195 | if (str_len > 0) { 196 | char path[2]; 197 | napi_get_value_string_latin1(env, tmp, path, 2, NULL); 198 | data->l = path[0]; 199 | napi_create_reference(env, argv[1], 1, &data->cb); 200 | napi_create_reference(env, self, 1, &data->self); 201 | napi_create_string_latin1(env, "fswin.getDriveDeviceLocation", NAPI_AUTO_LENGTH, &tmp); 202 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 203 | if (napi_queue_async_work(env, data->work) == napi_ok) { 204 | napi_get_boolean(env, true, &result); 205 | } else { 206 | napi_get_boolean(env, false, &result); 207 | napi_delete_reference(env, data->cb); 208 | napi_delete_reference(env, data->self); 209 | napi_delete_async_work(env, data->work); 210 | delete data; 211 | } 212 | } 213 | } 214 | } 215 | if (!result) { 216 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 217 | } 218 | } 219 | return result; 220 | } 221 | static void execute(napi_env env, void* data) { 222 | cbdata* d = (cbdata*)data; 223 | d->result = func(d->l); 224 | } 225 | static void complete(napi_env env, napi_status status, void* data) { 226 | cbdata* d = (cbdata*)data; 227 | napi_value cb, self, argv; 228 | napi_get_reference_value(env, d->cb, &cb); 229 | napi_get_reference_value(env, d->self, &self); 230 | if (d->result) { 231 | argv = convert(env, d->result); 232 | free(d->result->pspdidd); 233 | delete[]d->result->deviceId; 234 | delete[]d->result->parentDeviceId; 235 | delete d->result; 236 | } else { 237 | argv = NULL; 238 | } 239 | napi_call_function(env, self, cb, 1, &argv, NULL); 240 | napi_delete_reference(env, d->cb); 241 | napi_delete_reference(env, d->self); 242 | napi_delete_async_work(env, d->work); 243 | delete d; 244 | } 245 | }; -------------------------------------------------------------------------------- /src/getLogicalDriveList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class getLogicalDriveList { 5 | public: 6 | const struct drive { 7 | char v; 8 | BYTE type; 9 | }; 10 | static drive* func() { // you need to delete the result yourself if it is not NULL 11 | DWORD all = GetLogicalDrives(); 12 | drive* result = NULL; 13 | const char v = 'A'; 14 | char p[4]; 15 | p[1] = ':'; 16 | p[2] = '\\'; 17 | p[3] = 0; 18 | DWORD d = 1; 19 | BYTE c = 0; 20 | for (BYTE i = 0; i < 26; i++) { 21 | if (all & d) { 22 | ++c; 23 | } 24 | d *= 2; 25 | } 26 | if (c > 0) { 27 | result = new drive[c]; 28 | d = 1; 29 | c = 0; 30 | for (BYTE i = 0; i < 26; i++) { 31 | if (all & d) { 32 | result[c].v = p[0] = v + i; 33 | result[c].type = (BYTE)GetDriveTypeA(p); 34 | ++c; 35 | } 36 | d *= 2; 37 | } 38 | } 39 | return result; 40 | } 41 | static napi_value init(napi_env env, bool isSync = false) { 42 | napi_value f; 43 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 44 | return f; 45 | } 46 | private: 47 | const struct cbdata { 48 | napi_async_work work; 49 | napi_ref self; 50 | napi_ref cb; 51 | drive* result; 52 | }; 53 | static napi_value sync(napi_env env, napi_callback_info info) { 54 | napi_value result; 55 | napi_get_new_target(env, info, &result); 56 | if (result) { 57 | result = NULL; 58 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 59 | } else { 60 | drive* r = func(); 61 | result = convert(env, r); 62 | delete[]r; 63 | } 64 | return result; 65 | } 66 | static napi_value async(napi_env env, napi_callback_info info) { 67 | napi_value result; 68 | napi_get_new_target(env, info, &result); 69 | if (result) { 70 | result = NULL; 71 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 72 | } else { 73 | napi_value argv, self; 74 | size_t argc = 1; 75 | napi_get_cb_info(env, info, &argc, &argv, &self, NULL); 76 | if (argc >= 1) { 77 | napi_valuetype t; 78 | napi_typeof(env, argv, &t); 79 | if (t == napi_function) { 80 | cbdata* data = new cbdata; 81 | napi_value tmp; 82 | napi_create_reference(env, argv, 1, &data->cb); 83 | napi_create_reference(env, self, 1, &data->self); 84 | napi_create_string_latin1(env, "fswin.getLogicalDriveList", NAPI_AUTO_LENGTH, &tmp); 85 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 86 | if (napi_queue_async_work(env, data->work) == napi_ok) { 87 | napi_get_boolean(env, true, &result); 88 | } else { 89 | napi_get_boolean(env, false, &result); 90 | napi_delete_reference(env, data->cb); 91 | napi_delete_reference(env, data->self); 92 | napi_delete_async_work(env, data->work); 93 | delete data; 94 | } 95 | } 96 | } 97 | if (!result) { 98 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 99 | } 100 | } 101 | return result; 102 | } 103 | static napi_value convert(napi_env env, drive* data) { 104 | napi_value name, value, result; 105 | napi_create_object(env, &result); 106 | if (data) { 107 | size_t c = _msize(data) / sizeof(drive); 108 | for (BYTE i = 0; i < c; i++) { 109 | const char* type; 110 | if (data[i].type == DRIVE_NO_ROOT_DIR) { 111 | type = "NO_ROOT_DIR"; 112 | } else if (data[i].type == DRIVE_REMOVABLE) { 113 | type = "REMOVABLE"; 114 | } else if (data[i].type == DRIVE_FIXED) { 115 | type = "FIXED"; 116 | } else if (data[i].type == DRIVE_REMOTE) { 117 | type = "REMOTE"; 118 | } else if (data[i].type == DRIVE_CDROM) { 119 | type = "CDROM"; 120 | } else if (data[i].type == DRIVE_RAMDISK) { 121 | type = "RAMDISK"; 122 | } else { 123 | type = "UNKNOWN"; 124 | } 125 | napi_create_string_latin1(env, &data[i].v, 1, &name); 126 | napi_create_string_latin1(env, type, NAPI_AUTO_LENGTH, &value); 127 | napi_set_property(env, result, name, value); 128 | } 129 | } 130 | return result; 131 | } 132 | static void execute(napi_env env, void* data) { 133 | cbdata* d = (cbdata*)data; 134 | d->result = func(); 135 | } 136 | static void complete(napi_env env, napi_status status, void* data) { 137 | cbdata* d = (cbdata*)data; 138 | napi_value cb, self, argv; 139 | napi_get_reference_value(env, d->cb, &cb); 140 | napi_get_reference_value(env, d->self, &self); 141 | if (status == napi_ok && d->result) { 142 | argv = convert(env, d->result); 143 | delete[]d->result; 144 | } else { 145 | napi_get_null(env, &argv); 146 | } 147 | napi_call_function(env, self, cb, 1, &argv, NULL); 148 | napi_delete_reference(env, d->cb); 149 | napi_delete_reference(env, d->self); 150 | napi_delete_async_work(env, d->work); 151 | delete d; 152 | } 153 | }; -------------------------------------------------------------------------------- /src/getStorageProperties.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class getStorageProperties { 5 | public: 6 | const struct infor { 7 | STORAGE_DEVICE_DESCRIPTOR* DP; 8 | STORAGE_ADAPTER_DESCRIPTOR* AP; 9 | STORAGE_WRITE_CACHE_PROPERTY* DWCP; 10 | STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR* AAP; 11 | DEVICE_SEEK_PENALTY_DESCRIPTOR* DSP; 12 | DEVICE_TRIM_DESCRIPTOR* DT; 13 | DEVICE_LB_PROVISIONING_DESCRIPTOR* DLBPP; 14 | DEVICE_POWER_DESCRIPTOR* DPP; 15 | DEVICE_COPY_OFFLOAD_DESCRIPTOR* DCOP; 16 | STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR* DMPT; 17 | STORAGE_RPMB_DESCRIPTOR* ARP; 18 | STORAGE_DEVICE_IO_CAPABILITY_DESCRIPTOR* DICP; 19 | STORAGE_TEMPERATURE_DATA_DESCRIPTOR* ATP; 20 | STORAGE_TEMPERATURE_DATA_DESCRIPTOR* DTP; 21 | STORAGE_ADAPTER_SERIAL_NUMBER* ASN; 22 | }; 23 | static infor* func(const char* path, bool params[]) {//you need to delete the result yourself if it is not NULL 24 | infor* result = NULL; 25 | HANDLE hDrive = CreateFileA(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 26 | if (hDrive != INVALID_HANDLE_VALUE) { 27 | result = new infor({ 0 }); 28 | DWORD bytes = 0; 29 | STORAGE_PROPERTY_QUERY q; 30 | q.QueryType = PropertyStandardQuery; 31 | if (params[0]) { 32 | q.PropertyId = StorageDeviceProperty; 33 | STORAGE_DESCRIPTOR_HEADER hd; 34 | if (DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), &hd, sizeof(STORAGE_DESCRIPTOR_HEADER), &bytes, NULL)) { 35 | result->DP = (STORAGE_DEVICE_DESCRIPTOR*)malloc(hd.Size); 36 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->DP, hd.Size, &bytes, NULL)) { 37 | free(result->DP); 38 | result->DP = NULL; 39 | } 40 | } 41 | } 42 | if (params[1]) { 43 | q.PropertyId = StorageAdapterProperty; 44 | result->AP = new STORAGE_ADAPTER_DESCRIPTOR; 45 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->AP, sizeof(STORAGE_ADAPTER_DESCRIPTOR), &bytes, NULL)) { 46 | delete result->AP; 47 | result->AP = NULL; 48 | } 49 | } 50 | if (params[2]) { 51 | q.PropertyId = StorageDeviceWriteCacheProperty; 52 | result->DWCP = new STORAGE_WRITE_CACHE_PROPERTY; 53 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->DWCP, sizeof(STORAGE_WRITE_CACHE_PROPERTY), &bytes, NULL)) { 54 | delete result->DWCP; 55 | result->DWCP = NULL; 56 | } 57 | } 58 | if (params[3]) { 59 | q.PropertyId = StorageAccessAlignmentProperty; 60 | result->AAP = new STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR; 61 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->AAP, sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR), &bytes, NULL)) { 62 | delete result->AAP; 63 | result->AAP = NULL; 64 | } 65 | } 66 | if (params[4]) { 67 | q.PropertyId = StorageDeviceSeekPenaltyProperty; 68 | result->DSP = new DEVICE_SEEK_PENALTY_DESCRIPTOR; 69 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->DSP, sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR), &bytes, NULL)) { 70 | delete result->DSP; 71 | result->DSP = NULL; 72 | } 73 | } 74 | if (params[5]) { 75 | q.PropertyId = StorageDeviceTrimProperty; 76 | result->DT = new DEVICE_TRIM_DESCRIPTOR; 77 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->DT, sizeof(DEVICE_TRIM_DESCRIPTOR), &bytes, NULL)) { 78 | delete result->DT; 79 | result->DT = NULL; 80 | } 81 | } 82 | if (params[6]) { 83 | q.PropertyId = StorageDeviceLBProvisioningProperty; 84 | result->DLBPP = new DEVICE_LB_PROVISIONING_DESCRIPTOR; 85 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->DLBPP, sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR), &bytes, NULL)) { 86 | delete result->DLBPP; 87 | result->DLBPP = NULL; 88 | } 89 | } 90 | if (params[7]) { 91 | q.PropertyId = StorageDevicePowerProperty; 92 | result->DPP = new DEVICE_POWER_DESCRIPTOR; 93 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->DPP, sizeof(DEVICE_POWER_DESCRIPTOR), &bytes, NULL)) { 94 | delete result->DPP; 95 | result->DPP = NULL; 96 | } 97 | } 98 | if (params[8]) { 99 | q.PropertyId = StorageDevicePowerProperty; 100 | result->DCOP = new DEVICE_COPY_OFFLOAD_DESCRIPTOR; 101 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->DCOP, sizeof(DEVICE_COPY_OFFLOAD_DESCRIPTOR), &bytes, NULL)) { 102 | delete result->DCOP; 103 | result->DCOP = NULL; 104 | } 105 | } 106 | if (params[9]) { 107 | q.PropertyId = StorageDeviceMediumProductType; 108 | result->DMPT = new STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR; 109 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->DMPT, sizeof(STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR), &bytes, NULL)) { 110 | delete result->DMPT; 111 | result->DMPT = NULL; 112 | } 113 | } 114 | if (params[10]) { 115 | q.PropertyId = StorageAdapterRpmbProperty; 116 | result->ARP = new STORAGE_RPMB_DESCRIPTOR; 117 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->ARP, sizeof(STORAGE_RPMB_DESCRIPTOR), &bytes, NULL)) { 118 | delete result->ARP; 119 | result->ARP = NULL; 120 | } 121 | } 122 | if (params[11]) { 123 | q.PropertyId = StorageDeviceIoCapabilityProperty; 124 | result->DICP = new STORAGE_DEVICE_IO_CAPABILITY_DESCRIPTOR; 125 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->DICP, sizeof(STORAGE_DEVICE_IO_CAPABILITY_DESCRIPTOR), &bytes, NULL)) { 126 | delete result->DICP; 127 | result->DICP = NULL; 128 | } 129 | } 130 | if (params[12]) { 131 | q.PropertyId = StorageAdapterTemperatureProperty; 132 | STORAGE_DESCRIPTOR_HEADER hd; 133 | if (DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), &hd, sizeof(STORAGE_DESCRIPTOR_HEADER), &bytes, NULL)) { 134 | result->ATP = (STORAGE_TEMPERATURE_DATA_DESCRIPTOR*)malloc(hd.Size); 135 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->ATP, hd.Size, &bytes, NULL)) { 136 | free(result->ATP); 137 | result->ATP = NULL; 138 | } 139 | } 140 | } 141 | if (params[13]) { 142 | q.PropertyId = StorageDeviceTemperatureProperty; 143 | STORAGE_DESCRIPTOR_HEADER hd; 144 | if (DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), &hd, sizeof(STORAGE_DESCRIPTOR_HEADER), &bytes, NULL)) { 145 | result->DTP = (STORAGE_TEMPERATURE_DATA_DESCRIPTOR*)malloc(hd.Size); 146 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->DTP, hd.Size, &bytes, NULL)) { 147 | free(result->DTP); 148 | result->DTP = NULL; 149 | } 150 | } 151 | } 152 | if (params[14]) { 153 | q.PropertyId = StorageAdapterSerialNumberProperty; 154 | STORAGE_DESCRIPTOR_HEADER hd; 155 | if (DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), &hd, sizeof(STORAGE_DESCRIPTOR_HEADER), &bytes, NULL) && hd.Size >= sizeof(STORAGE_ADAPTER_SERIAL_NUMBER)) { 156 | result->ASN = (STORAGE_ADAPTER_SERIAL_NUMBER*)malloc(hd.Size); 157 | if (!DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &q, sizeof(STORAGE_PROPERTY_QUERY), result->ASN, hd.Size, &bytes, NULL)) { 158 | free(result->ASN); 159 | result->ASN = NULL; 160 | } 161 | } 162 | } 163 | CloseHandle(hDrive); 164 | } 165 | return result; 166 | } 167 | static napi_value init(napi_env env, bool isSync = false) { 168 | napi_value f; 169 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 170 | return f; 171 | } 172 | private: 173 | static inline const char* names[] = { "deviceProperty", "adapterProperty", "deviceWriteCacheProperty", "accessAlignmentProperty", "deviceSeekPenalty", "deviceTrim", "deviceLBProvisioningProperty", "devicePowerProperty", "deviceCopyOffloadProperty", "deviceMediumProductType", "adapterRpmbProperty", "deviceIoCapabilityProperty", "adapterTemperatureProperty", "deviceTemperatureProperty", "adapterSerialNumber" }; 174 | const struct cbdata { 175 | napi_async_work work; 176 | napi_ref self; 177 | napi_ref cb; 178 | char* path; 179 | bool params[15]; 180 | infor* result; 181 | }; 182 | 183 | static napi_value getBusType(napi_env env, BYTE type) { 184 | napi_value result; 185 | const char* bustype; 186 | if (type == BusTypeScsi) { 187 | bustype = "SCSI"; 188 | } else if (type == BusTypeAtapi) { 189 | bustype = "ATAPI"; 190 | } else if (type == BusTypeAta) { 191 | bustype = "ATA"; 192 | } else if (type == BusType1394) { 193 | bustype = "1394"; 194 | } else if (type == BusTypeSsa) { 195 | bustype = "SSA"; 196 | } else if (type == BusTypeFibre) { 197 | bustype = "Fibre"; 198 | } else if (type == BusTypeUsb) { 199 | bustype = "USB"; 200 | } else if (type == BusTypeRAID) { 201 | bustype = "RAID"; 202 | } else if (type == BusTypeiScsi) { 203 | bustype = "iSCSI"; 204 | } else if (type == BusTypeSas) { 205 | bustype = "SAS"; 206 | } else if (type == BusTypeSata) { 207 | bustype = "SATA"; 208 | } else if (type == BusTypeSd) { 209 | bustype = "SD"; 210 | } else if (type == BusTypeMmc) { 211 | bustype = "MMC"; 212 | } else if (type == BusTypeVirtual) { 213 | bustype = "Virtual"; 214 | } else if (type == BusTypeFileBackedVirtual) { 215 | bustype = "FileBackedVirtual"; 216 | } else if (type == BusTypeSpaces) { 217 | bustype = "Spaces"; 218 | } else if (type == BusTypeNvme) { 219 | bustype = "NVMe"; 220 | } else if (type == BusTypeSCM) { 221 | bustype = "SCM"; 222 | } else if (type == BusTypeUfs) { 223 | bustype = "UFS"; 224 | } else { 225 | bustype = NULL; 226 | } 227 | if (bustype) { 228 | napi_create_string_latin1(env, bustype, NAPI_AUTO_LENGTH, &result); 229 | } else { 230 | napi_create_uint32(env, type, &result); 231 | } 232 | return result; 233 | } 234 | static napi_value convert(napi_env env, infor* data) { 235 | napi_value o, tmp, result; 236 | napi_create_object(env, &result); 237 | if (data->DP) { 238 | napi_create_object(env, &o); 239 | napi_set_named_property(env, result, names[0], o); 240 | napi_create_uint32(env, data->DP->DeviceType, &tmp); 241 | napi_set_named_property(env, o, "deviceType", tmp); 242 | napi_create_uint32(env, data->DP->DeviceTypeModifier, &tmp); 243 | napi_set_named_property(env, o, "deviceTypeModifier", tmp); 244 | tmp = getBusType(env, data->DP->BusType); 245 | napi_set_named_property(env, o, "busType", tmp); 246 | napi_get_boolean(env, data->DP->CommandQueueing, &tmp); 247 | napi_set_named_property(env, o, "commandQueueing", tmp); 248 | napi_get_boolean(env, data->DP->RemovableMedia, &tmp); 249 | napi_set_named_property(env, o, "removableMedia", tmp); 250 | if (data->DP->VendorIdOffset) { 251 | char* txt = (char*)((ULONG_PTR)data->DP + data->DP->VendorIdOffset); 252 | napi_create_string_latin1(env, txt, NAPI_AUTO_LENGTH, &tmp); 253 | napi_set_named_property(env, o, "vendorId", tmp); 254 | } 255 | if (data->DP->ProductIdOffset) { 256 | char* txt = (char*)((ULONG_PTR)data->DP + data->DP->ProductIdOffset); 257 | napi_create_string_latin1(env, txt, NAPI_AUTO_LENGTH, &tmp); 258 | napi_set_named_property(env, o, "productId", tmp); 259 | } 260 | if (data->DP->ProductRevisionOffset) { 261 | char* txt = (char*)((ULONG_PTR)data->DP + data->DP->ProductRevisionOffset); 262 | napi_create_string_latin1(env, txt, NAPI_AUTO_LENGTH, &tmp); 263 | napi_set_named_property(env, o, "productRevision", tmp); 264 | } 265 | if (data->DP->SerialNumberOffset) { 266 | char* txt = (char*)((ULONG_PTR)data->DP + data->DP->SerialNumberOffset); 267 | napi_create_string_latin1(env, txt, NAPI_AUTO_LENGTH, &tmp); 268 | napi_set_named_property(env, o, "serialNumber", tmp); 269 | } 270 | free(data->DP); 271 | } 272 | 273 | if (data->AP) { 274 | napi_create_object(env, &o); 275 | napi_set_named_property(env, result, names[1], o); 276 | napi_create_uint32(env, data->AP->MaximumTransferLength, &tmp); 277 | napi_set_named_property(env, o, "maximumTransferLength", tmp); 278 | napi_create_uint32(env, data->AP->MaximumPhysicalPages, &tmp); 279 | napi_set_named_property(env, o, "maximumPhysicalPages", tmp); 280 | napi_create_uint32(env, data->AP->AlignmentMask, &tmp); 281 | napi_set_named_property(env, o, "alignmentMask", tmp); 282 | napi_get_boolean(env, data->AP->AdapterUsesPio, &tmp); 283 | napi_set_named_property(env, o, "adapterUsesPio", tmp); 284 | napi_get_boolean(env, data->AP->AdapterScansDown, &tmp); 285 | napi_set_named_property(env, o, "adapterScansDown", tmp); 286 | napi_get_boolean(env, data->AP->CommandQueueing, &tmp); 287 | napi_set_named_property(env, o, "commandQueueing", tmp); 288 | napi_get_boolean(env, data->AP->AcceleratedTransfer, &tmp); 289 | napi_set_named_property(env, o, "acceleratedTransfer", tmp); 290 | napi_create_uint32(env, data->AP->BusMajorVersion, &tmp); 291 | napi_set_named_property(env, o, "busMajorVersion", tmp); 292 | napi_create_uint32(env, data->AP->BusMinorVersion, &tmp); 293 | napi_set_named_property(env, o, "busMinorVersion", tmp); 294 | tmp = getBusType(env, data->AP->BusType); 295 | napi_set_named_property(env, o, "busType", tmp); 296 | const char* SrbType; 297 | if (data->AP->SrbType == SRB_TYPE_SCSI_REQUEST_BLOCK) { 298 | SrbType = "SCSIRequestBlock"; 299 | } else if (data->AP->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) { 300 | SrbType = "StorageRequestBlock"; 301 | } else { 302 | SrbType = NULL; 303 | } 304 | if (SrbType) { 305 | napi_create_string_latin1(env, SrbType, NAPI_AUTO_LENGTH, &tmp); 306 | } else { 307 | napi_create_uint32(env, data->AP->SrbType, &tmp); 308 | } 309 | napi_set_named_property(env, o, "srbType", tmp); 310 | const char* AddressType; 311 | if (data->AP->AddressType == STORAGE_ADDRESS_TYPE_BTL8) { 312 | AddressType = "BTL8"; 313 | } else { 314 | AddressType = NULL; 315 | } 316 | if (AddressType) { 317 | napi_create_string_latin1(env, AddressType, NAPI_AUTO_LENGTH, &tmp); 318 | } else { 319 | napi_create_uint32(env, data->AP->AddressType, &tmp); 320 | } 321 | napi_set_named_property(env, o, "addressType", tmp); 322 | delete data->AP; 323 | } 324 | 325 | if (data->DWCP) { 326 | napi_create_object(env, &o); 327 | napi_set_named_property(env, result, names[2], o); 328 | const char* WriteCacheType; 329 | if (data->DWCP->WriteCacheType == WriteCacheTypeUnknown) { 330 | WriteCacheType = "unknown"; 331 | } else if (data->DWCP->WriteCacheType == WriteCacheTypeNone) { 332 | WriteCacheType = "none"; 333 | } else if (data->DWCP->WriteCacheType == WriteCacheTypeWriteBack) { 334 | WriteCacheType = "writeBack"; 335 | } else if (data->DWCP->WriteCacheType == WriteCacheTypeWriteThrough) { 336 | WriteCacheType = "writeThrough"; 337 | } else { 338 | WriteCacheType = NULL; 339 | } 340 | if (WriteCacheType) { 341 | napi_create_string_latin1(env, WriteCacheType, NAPI_AUTO_LENGTH, &tmp); 342 | } else { 343 | napi_create_uint32(env, data->DWCP->WriteCacheType, &tmp); 344 | } 345 | napi_set_named_property(env, o, "type", tmp); 346 | const char* status; 347 | if (data->DWCP->WriteCacheEnabled == WriteCacheEnableUnknown) { 348 | status = "unknown"; 349 | } else if (data->DWCP->WriteCacheEnabled == WriteCacheDisabled) { 350 | status = "disabled"; 351 | } else if (data->DWCP->WriteCacheEnabled == WriteCacheEnabled) { 352 | status = "enabled"; 353 | } else { 354 | status = NULL; 355 | } 356 | if (status) { 357 | napi_create_string_latin1(env, status, NAPI_AUTO_LENGTH, &tmp); 358 | } else { 359 | napi_create_uint32(env, data->DWCP->WriteCacheEnabled, &tmp); 360 | } 361 | napi_set_named_property(env, o, "isEnabled", tmp); 362 | const char* isChangeable; 363 | if (data->DWCP->WriteCacheChangeable == WriteCacheChangeUnknown) { 364 | isChangeable = "unknown"; 365 | } else if (data->DWCP->WriteCacheChangeable == WriteCacheNotChangeable) { 366 | isChangeable = "notChangeable"; 367 | } else if (data->DWCP->WriteCacheChangeable == WriteCacheChangeable) { 368 | isChangeable = "changeable"; 369 | } else { 370 | isChangeable = NULL; 371 | } 372 | if (isChangeable) { 373 | napi_create_string_latin1(env, isChangeable, NAPI_AUTO_LENGTH, &tmp); 374 | } else { 375 | napi_create_uint32(env, data->DWCP->WriteCacheEnabled, &tmp); 376 | } 377 | napi_set_named_property(env, o, "isChangeable", tmp); 378 | const char* isWriteThroughSupported; 379 | if (data->DWCP->WriteThroughSupported == WriteThroughUnknown) { 380 | isWriteThroughSupported = "unknown"; 381 | } else if (data->DWCP->WriteThroughSupported == WriteThroughNotSupported) { 382 | isWriteThroughSupported = "notSupported"; 383 | } else if (data->DWCP->WriteThroughSupported == WriteThroughSupported) { 384 | isWriteThroughSupported = "supported"; 385 | } else { 386 | isWriteThroughSupported = NULL; 387 | } 388 | if (isWriteThroughSupported) { 389 | napi_create_string_latin1(env, isWriteThroughSupported, NAPI_AUTO_LENGTH, &tmp); 390 | } else { 391 | napi_create_uint32(env, data->DWCP->WriteThroughSupported, &tmp); 392 | } 393 | napi_set_named_property(env, o, "isWriteThroughSupported", tmp); 394 | napi_get_boolean(env, data->DWCP->FlushCacheSupported, &tmp); 395 | napi_set_named_property(env, o, "flushCacheSupported", tmp); 396 | napi_get_boolean(env, data->DWCP->UserDefinedPowerProtection, &tmp); 397 | napi_set_named_property(env, o, "userDefinedPowerProtection", tmp); 398 | napi_get_boolean(env, data->DWCP->NVCacheEnabled, &tmp); 399 | napi_set_named_property(env, o, "NVCacheEnabled", tmp); 400 | delete data->DWCP; 401 | } 402 | 403 | if (data->AAP) { 404 | napi_create_object(env, &o); 405 | napi_set_named_property(env, result, names[3], o); 406 | napi_create_uint32(env, data->AAP->BytesPerCacheLine, &tmp); 407 | napi_set_named_property(env, o, "bytesPerCacheLine", tmp); 408 | napi_create_uint32(env, data->AAP->BytesOffsetForCacheAlignment, &tmp); 409 | napi_set_named_property(env, o, "bytesOffsetForCacheAlignment", tmp); 410 | napi_create_uint32(env, data->AAP->BytesPerLogicalSector, &tmp); 411 | napi_set_named_property(env, o, "bytesPerLogicalSector", tmp); 412 | napi_create_uint32(env, data->AAP->BytesPerPhysicalSector, &tmp); 413 | napi_set_named_property(env, o, "bytesPerPhysicalSector", tmp); 414 | napi_create_uint32(env, data->AAP->BytesOffsetForSectorAlignment, &tmp); 415 | napi_set_named_property(env, o, "bytesOffsetForSectorAlignment", tmp); 416 | delete data->AAP; 417 | } 418 | if (data->DSP) { 419 | napi_get_boolean(env, data->DSP->IncursSeekPenalty, &tmp); 420 | napi_set_named_property(env, result, names[4], tmp); 421 | delete data->DSP; 422 | } 423 | if (data->DT) { 424 | napi_get_boolean(env, data->DT->TrimEnabled, &tmp); 425 | napi_set_named_property(env, result, names[5], tmp); 426 | delete data->DT; 427 | } 428 | if (data->DLBPP) { 429 | napi_create_object(env, &o); 430 | napi_set_named_property(env, result, names[6], o); 431 | napi_get_boolean(env, data->DLBPP->ThinProvisioningEnabled, &tmp); 432 | napi_set_named_property(env, o, "thinProvisioningEnabled", tmp); 433 | napi_get_boolean(env, data->DLBPP->ThinProvisioningReadZeros, &tmp); 434 | napi_set_named_property(env, o, "thinProvisioningReadZeros", tmp); 435 | napi_get_boolean(env, data->DLBPP->AnchorSupported, &tmp); 436 | napi_set_named_property(env, o, "anchorSupported", tmp); 437 | napi_get_boolean(env, data->DLBPP->UnmapGranularityAlignmentValid, &tmp); 438 | napi_set_named_property(env, o, "unmapGranularityAlignmentValid", tmp); 439 | napi_get_boolean(env, data->DLBPP->GetFreeSpaceSupported, &tmp); 440 | napi_set_named_property(env, o, "getFreeSpaceSupported", tmp); 441 | napi_get_boolean(env, data->DLBPP->MapSupported, &tmp); 442 | napi_set_named_property(env, o, "mapSupported", tmp); 443 | napi_create_bigint_uint64(env, data->DLBPP->OptimalUnmapGranularity, &tmp); 444 | napi_set_named_property(env, o, "optimalUnmapGranularity", tmp); 445 | napi_create_bigint_uint64(env, data->DLBPP->UnmapGranularityAlignment, &tmp); 446 | napi_set_named_property(env, o, "unmapGranularityAlignment", tmp); 447 | napi_create_uint32(env, data->DLBPP->MaxUnmapLbaCount, &tmp); 448 | napi_set_named_property(env, o, "maxUnmapLbaCount", tmp); 449 | napi_create_uint32(env, data->DLBPP->MaxUnmapBlockDescriptorCount, &tmp); 450 | napi_set_named_property(env, o, "maxUnmapBlockDescriptorCount", tmp); 451 | delete data->DLBPP; 452 | } 453 | if (data->DPP) { 454 | napi_create_object(env, &o); 455 | napi_set_named_property(env, result, names[7], o); 456 | napi_get_boolean(env, data->DPP->DeviceAttentionSupported, &tmp); 457 | napi_set_named_property(env, o, "deviceAttentionSupported", tmp); 458 | napi_get_boolean(env, data->DPP->AsynchronousNotificationSupported, &tmp); 459 | napi_set_named_property(env, o, "asynchronousNotificationSupported", tmp); 460 | napi_get_boolean(env, data->DPP->IdlePowerManagementEnabled, &tmp); 461 | napi_set_named_property(env, o, "idlePowerManagementEnabled", tmp); 462 | napi_get_boolean(env, data->DPP->D3ColdEnabled, &tmp); 463 | napi_set_named_property(env, o, "d3ColdEnabled", tmp); 464 | napi_get_boolean(env, data->DPP->D3ColdSupported, &tmp); 465 | napi_set_named_property(env, o, "d3ColdSupported", tmp); 466 | napi_get_boolean(env, data->DPP->NoVerifyDuringIdlePower, &tmp); 467 | napi_set_named_property(env, o, "noVerifyDuringIdlePower", tmp); 468 | napi_create_uint32(env, data->DPP->IdleTimeoutInMS, &tmp); 469 | napi_set_named_property(env, o, "idleTimeoutInMS", tmp); 470 | delete data->DPP; 471 | } 472 | if (data->DCOP) { 473 | napi_create_object(env, &o); 474 | napi_set_named_property(env, result, names[8], o); 475 | napi_create_uint32(env, data->DCOP->MaximumTokenLifetime, &tmp); 476 | napi_set_named_property(env, o, "maximumTokenLifetime", tmp); 477 | napi_create_uint32(env, data->DCOP->DefaultTokenLifetime, &tmp); 478 | napi_set_named_property(env, o, "defaultTokenLifetime", tmp); 479 | napi_create_bigint_uint64(env, data->DCOP->MaximumTransferSize, &tmp); 480 | napi_set_named_property(env, o, "maximumTransferSize", tmp); 481 | napi_create_bigint_uint64(env, data->DCOP->OptimalTransferCount, &tmp); 482 | napi_set_named_property(env, o, "optimalTransferCount", tmp); 483 | napi_create_uint32(env, data->DCOP->MaximumDataDescriptors, &tmp); 484 | napi_set_named_property(env, o, "maximumDataDescriptors", tmp); 485 | napi_create_uint32(env, data->DCOP->MaximumTransferLengthPerDescriptor, &tmp); 486 | napi_set_named_property(env, o, "maximumTransferLengthPerDescriptor", tmp); 487 | napi_create_uint32(env, data->DCOP->OptimalTransferLengthPerDescriptor, &tmp); 488 | napi_set_named_property(env, o, "optimalTransferLengthPerDescriptor", tmp); 489 | napi_create_uint32(env, data->DCOP->OptimalTransferLengthGranularity, &tmp); 490 | napi_set_named_property(env, o, "optimalTransferLengthGranularity", tmp); 491 | delete data->DCOP; 492 | } 493 | if (data->DMPT) { 494 | const char* MediumProductType; 495 | if (data->DMPT->MediumProductType == 1) { 496 | MediumProductType = "CFast"; 497 | } else if (data->DMPT->MediumProductType == 2) { 498 | MediumProductType = "CompactFlash"; 499 | } else if (data->DMPT->MediumProductType == 3) { 500 | MediumProductType = "MemoryStick"; 501 | } else if (data->DMPT->MediumProductType == 4) { 502 | MediumProductType = "MultiMediaCard"; 503 | } else if (data->DMPT->MediumProductType == 5) { 504 | MediumProductType = "SecureDigitalCard"; 505 | } else if (data->DMPT->MediumProductType == 6) { 506 | MediumProductType = "QXD"; 507 | } else if (data->DMPT->MediumProductType == 7) { 508 | MediumProductType = "UniversalFlashStorage"; 509 | } else { 510 | MediumProductType = NULL; 511 | } 512 | if (MediumProductType) { 513 | napi_create_string_latin1(env, MediumProductType, NAPI_AUTO_LENGTH, &tmp); 514 | } else { 515 | napi_create_uint32(env, data->DMPT->MediumProductType, &tmp); 516 | } 517 | napi_set_named_property(env, result, names[9], tmp); 518 | delete data->DMPT; 519 | } 520 | if (data->ARP) { 521 | napi_create_object(env, &o); 522 | napi_set_named_property(env, result, names[10], o); 523 | napi_create_uint32(env, data->ARP->SizeInBytes, &tmp); 524 | napi_set_named_property(env, o, "sizeInBytes", tmp); 525 | napi_create_uint32(env, data->ARP->MaxReliableWriteSizeInBytes, &tmp); 526 | napi_set_named_property(env, o, "maxReliableWriteSizeInBytes", tmp); 527 | const char* FrameFormat; 528 | if (data->ARP->FrameFormat == StorageRpmbFrameTypeStandard) { 529 | FrameFormat = "standard"; 530 | } else { 531 | FrameFormat = NULL; 532 | } 533 | if (FrameFormat) { 534 | napi_create_string_latin1(env, FrameFormat, NAPI_AUTO_LENGTH, &tmp); 535 | } else { 536 | napi_create_uint32(env, data->ARP->FrameFormat, &tmp); 537 | } 538 | napi_set_named_property(env, o, "frameFormat", tmp); 539 | delete data->ARP; 540 | } 541 | if (data->DICP) { 542 | napi_create_object(env, &o); 543 | napi_set_named_property(env, result, names[11], o); 544 | napi_create_uint32(env, data->DICP->LunMaxIoCount, &tmp); 545 | napi_set_named_property(env, o, "lunMaxIoCount", tmp); 546 | napi_create_uint32(env, data->DICP->AdapterMaxIoCount, &tmp); 547 | napi_set_named_property(env, o, "adapterMaxIoCount", tmp); 548 | delete data->DICP; 549 | } 550 | if (data->ATP) { 551 | napi_set_named_property(env, result, names[12], convertTemperature(env, data->ATP)); 552 | free(data->ATP); 553 | } 554 | if (data->DTP) { 555 | napi_set_named_property(env, result, names[13], convertTemperature(env, data->DTP)); 556 | free(data->DTP); 557 | } 558 | if (data->ASN) { 559 | napi_create_string_utf16(env, (char16_t*)data->ASN->SerialNumber, NAPI_AUTO_LENGTH, &tmp); 560 | napi_set_named_property(env, result, names[14], tmp); 561 | free(data->ASN); 562 | } 563 | delete data; 564 | return result; 565 | } 566 | static napi_value convertTemperature(napi_env env, STORAGE_TEMPERATURE_DATA_DESCRIPTOR* data) { 567 | napi_value o, tmp; 568 | napi_create_object(env, &o); 569 | napi_create_int32(env, data->CriticalTemperature, &tmp); 570 | napi_set_named_property(env, o, "criticalTemperature", tmp); 571 | napi_create_int32(env, data->WarningTemperature, &tmp); 572 | napi_set_named_property(env, o, "warningTemperature", tmp); 573 | napi_create_array(env, &tmp); 574 | napi_set_named_property(env, o, "temperatureInfo", tmp); 575 | napi_value t, p, push; 576 | napi_get_named_property(env, tmp, "push", &push); 577 | for (WORD i = 0; i < data->InfoCount; ++i) { 578 | napi_create_object(env, &t); 579 | napi_call_function(env, tmp, push, 1, &t, NULL); 580 | napi_create_uint32(env, data->TemperatureInfo[i].Index, &p); 581 | napi_set_named_property(env, t, "index", p); 582 | napi_create_int32(env, data->TemperatureInfo[i].Temperature, &p); 583 | napi_set_named_property(env, t, "temperature", p); 584 | napi_create_int32(env, data->TemperatureInfo[i].OverThreshold, &p); 585 | napi_set_named_property(env, t, "overThreshold", p); 586 | napi_create_int32(env, data->TemperatureInfo[i].UnderThreshold, &p); 587 | napi_set_named_property(env, t, "underThreshold", p); 588 | napi_get_boolean(env, data->TemperatureInfo[i].OverThresholdChangable, &p); 589 | napi_set_named_property(env, t, "overThresholdChangable", p); 590 | napi_get_boolean(env, data->TemperatureInfo[i].UnderThresholdChangable, &p); 591 | napi_set_named_property(env, t, "underThresholdChangable", p); 592 | napi_get_boolean(env, data->TemperatureInfo[i].EventGenerated, &p); 593 | napi_set_named_property(env, t, "eventGenerated", p); 594 | } 595 | return o; 596 | } 597 | static void getParams(napi_env env, napi_value argv, bool params[]) { 598 | napi_value tmp; 599 | for (BYTE i = 0; i < _countof(names); ++i) { 600 | napi_get_named_property(env, argv, names[i], &tmp); 601 | napi_coerce_to_bool(env, tmp, &tmp); 602 | napi_get_value_bool(env, tmp, ¶ms[i]); 603 | } 604 | } 605 | static napi_value sync(napi_env env, napi_callback_info info) { 606 | napi_value result; 607 | napi_get_new_target(env, info, &result); 608 | if (result) { 609 | result = NULL; 610 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 611 | } else { 612 | napi_value argv[2]; 613 | size_t argc = 2; 614 | napi_get_cb_info(env, info, &argc, argv, NULL, NULL); 615 | if (argc >= 2) { 616 | napi_valuetype t; 617 | napi_typeof(env, argv[1], &t); 618 | if (t == napi_object) { 619 | size_t str_len; 620 | napi_value tmp; 621 | napi_coerce_to_string(env, argv[0], &tmp); 622 | napi_get_value_string_latin1(env, tmp, NULL, 0, &str_len); 623 | str_len += 1; 624 | char* str = new char[str_len]; 625 | napi_get_value_string_latin1(env, tmp, str, str_len, NULL); 626 | bool params[15]; 627 | getParams(env, argv[1], params); 628 | infor* r = func(str, params); 629 | delete[]str; 630 | if (r) { 631 | result = convert(env, r); 632 | } else { 633 | napi_get_null(env, &result); 634 | } 635 | } 636 | } 637 | if (!result) { 638 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 639 | } 640 | } 641 | return result; 642 | } 643 | static napi_value async(napi_env env, napi_callback_info info) { 644 | napi_value result; 645 | napi_get_new_target(env, info, &result); 646 | if (result) { 647 | result = NULL; 648 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 649 | } else { 650 | napi_value argv[3], self; 651 | size_t argc = 3; 652 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 653 | if (argc >= 3) { 654 | napi_valuetype t; 655 | napi_typeof(env, argv[2], &t); 656 | if (t == napi_function) { 657 | napi_typeof(env, argv[1], &t); 658 | if (t == napi_object) { 659 | cbdata* data = new cbdata; 660 | size_t str_len; 661 | napi_value tmp; 662 | napi_create_reference(env, argv[2], 1, &data->cb); 663 | napi_create_reference(env, self, 1, &data->self); 664 | napi_coerce_to_string(env, argv[0], &tmp); 665 | napi_get_value_string_latin1(env, tmp, NULL, 0, &str_len); 666 | str_len += 1; 667 | data->path = new char[str_len]; 668 | napi_get_value_string_latin1(env, tmp, data->path, str_len, NULL); 669 | getParams(env, argv[1], data->params); 670 | napi_create_string_latin1(env, "fswin.getStroageProperties", NAPI_AUTO_LENGTH, &tmp); 671 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 672 | if (napi_queue_async_work(env, data->work) == napi_ok) { 673 | napi_get_boolean(env, true, &result); 674 | } else { 675 | napi_get_boolean(env, false, &result); 676 | napi_delete_reference(env, data->cb); 677 | napi_delete_reference(env, data->self); 678 | napi_delete_async_work(env, data->work); 679 | delete[]data->path; 680 | delete data; 681 | } 682 | } 683 | } 684 | } 685 | if (!result) { 686 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 687 | } 688 | } 689 | return result; 690 | } 691 | static void execute(napi_env env, void* data) { 692 | cbdata* d = (cbdata*)data; 693 | d->result = func(d->path, d->params); 694 | } 695 | static void complete(napi_env env, napi_status status, void* data) { 696 | cbdata* d = (cbdata*)data; 697 | delete[]d->path; 698 | napi_value cb, self, argv; 699 | napi_get_reference_value(env, d->cb, &cb); 700 | napi_get_reference_value(env, d->self, &self); 701 | if (status == napi_ok && d->result) { 702 | argv = convert(env, d->result); 703 | } else { 704 | napi_get_null(env, &argv); 705 | } 706 | napi_call_function(env, self, cb, 1, &argv, NULL); 707 | napi_delete_reference(env, d->cb); 708 | napi_delete_reference(env, d->self); 709 | napi_delete_async_work(env, d->work); 710 | delete d; 711 | } 712 | }; -------------------------------------------------------------------------------- /src/getVolumeInformation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class getVolumeInformation { 5 | public: 6 | const struct infor { 7 | wchar_t label[MAX_PATH + 1]; 8 | wchar_t fileSystem[MAX_PATH + 1]; 9 | DWORD serialNumber; 10 | DWORD maximumComponentLength; 11 | DWORD fileSystemFlags; 12 | }; 13 | static infor* func(const wchar_t* path) {//you need to delete the result yourself if it is not NULL 14 | infor* result = new infor; 15 | if (!GetVolumeInformationW(path, result->label, MAX_PATH + 1, &result->serialNumber, &result->maximumComponentLength, &result->fileSystemFlags, result->fileSystem, MAX_PATH + 1)) { 16 | delete result; 17 | result = NULL; 18 | } 19 | return result; 20 | } 21 | static napi_value init(napi_env env, bool isSync = false) { 22 | napi_value f; 23 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 24 | return f; 25 | } 26 | private: 27 | const struct cbdata { 28 | napi_async_work work; 29 | napi_ref self; 30 | napi_ref cb; 31 | wchar_t* path; 32 | infor* result; 33 | }; 34 | static napi_value sync(napi_env env, napi_callback_info info) { 35 | napi_value result; 36 | napi_get_new_target(env, info, &result); 37 | if (result) { 38 | result = NULL; 39 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 40 | } else { 41 | napi_value argv; 42 | size_t argc = 1; 43 | napi_get_cb_info(env, info, &argc, &argv, NULL, NULL); 44 | if (argc < 1) { 45 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 46 | } else { 47 | size_t str_len; 48 | napi_value tmp; 49 | napi_coerce_to_string(env, argv, &tmp); 50 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 51 | str_len += 1; 52 | wchar_t* str = new wchar_t[str_len]; 53 | napi_get_value_string_utf16(env, tmp, (char16_t*)str, str_len, NULL); 54 | infor* r = func(str); 55 | delete[]str; 56 | if (r) { 57 | result = convert(env, r); 58 | delete r; 59 | } else { 60 | napi_get_null(env, &result); 61 | } 62 | } 63 | } 64 | return result; 65 | } 66 | static napi_value async(napi_env env, napi_callback_info info) { 67 | napi_value result; 68 | napi_get_new_target(env, info, &result); 69 | if (result) { 70 | result = NULL; 71 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 72 | } else { 73 | napi_value argv[2], self; 74 | size_t argc = 2; 75 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 76 | if (argc >= 2) { 77 | napi_valuetype t; 78 | napi_typeof(env, argv[1], &t); 79 | if (t == napi_function) { 80 | cbdata* data = new cbdata; 81 | size_t str_len; 82 | napi_value tmp; 83 | napi_create_reference(env, argv[1], 1, &data->cb); 84 | napi_create_reference(env, self, 1, &data->self); 85 | napi_coerce_to_string(env, argv[0], &tmp); 86 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 87 | str_len += 1; 88 | data->path = new wchar_t[str_len]; 89 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->path, str_len, NULL); 90 | napi_create_string_latin1(env, "fswin.getVolumeInformation", NAPI_AUTO_LENGTH, &tmp); 91 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 92 | if (napi_queue_async_work(env, data->work) == napi_ok) { 93 | napi_get_boolean(env, true, &result); 94 | } else { 95 | napi_get_boolean(env, false, &result); 96 | napi_delete_reference(env, data->cb); 97 | napi_delete_reference(env, data->self); 98 | napi_delete_async_work(env, data->work); 99 | delete[]data->path; 100 | delete data; 101 | } 102 | } 103 | } 104 | if (!result) { 105 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 106 | } 107 | } 108 | return result; 109 | } 110 | static napi_value convert(napi_env env, infor* data) { 111 | napi_value tmp, result; 112 | napi_create_object(env, &result); 113 | napi_create_string_utf16(env, (char16_t*)data->label, NAPI_AUTO_LENGTH, &tmp); 114 | napi_set_named_property(env, result, "LABEL", tmp); 115 | napi_create_string_utf16(env, (char16_t*)data->fileSystem, NAPI_AUTO_LENGTH, &tmp); 116 | napi_set_named_property(env, result, "FILESYSTEM", tmp); 117 | napi_create_uint32(env, data->serialNumber, &tmp); 118 | napi_set_named_property(env, result, "SERIALNUMBER", tmp); 119 | napi_create_uint32(env, data->fileSystemFlags, &tmp); 120 | napi_set_named_property(env, result, "RAW_FLAGS", tmp); 121 | napi_get_boolean(env, data->fileSystemFlags & FILE_CASE_SENSITIVE_SEARCH, &tmp); 122 | napi_set_named_property(env, result, "CASE_SENSITIVE_SEARCH", tmp); 123 | napi_get_boolean(env, data->fileSystemFlags & FILE_CASE_PRESERVED_NAMES, &tmp); 124 | napi_set_named_property(env, result, "CASE_PRESERVED_NAMES", tmp); 125 | napi_get_boolean(env, data->fileSystemFlags & FILE_UNICODE_ON_DISK, &tmp); 126 | napi_set_named_property(env, result, "UNICODE_ON_DISK", tmp); 127 | napi_get_boolean(env, data->fileSystemFlags & FILE_PERSISTENT_ACLS, &tmp); 128 | napi_set_named_property(env, result, "PERSISTENT_ACLS", tmp); 129 | napi_get_boolean(env, data->fileSystemFlags & FILE_FILE_COMPRESSION, &tmp); 130 | napi_set_named_property(env, result, "FILE_COMPRESSION", tmp); 131 | napi_get_boolean(env, data->fileSystemFlags & FILE_VOLUME_QUOTAS, &tmp); 132 | napi_set_named_property(env, result, "VOLUME_QUOTAS", tmp); 133 | napi_get_boolean(env, data->fileSystemFlags & FILE_RETURNS_CLEANUP_RESULT_INFO, &tmp); 134 | napi_set_named_property(env, result, "RETURNS_CLEANUP_RESULT_INFO", tmp); 135 | napi_get_boolean(env, data->fileSystemFlags & FILE_VOLUME_IS_COMPRESSED, &tmp); 136 | napi_set_named_property(env, result, "VOLUME_IS_COMPRESSED", tmp); 137 | napi_get_boolean(env, data->fileSystemFlags & FILE_NAMED_STREAMS, &tmp); 138 | napi_set_named_property(env, result, "NAMED_STREAMS", tmp); 139 | napi_get_boolean(env, data->fileSystemFlags & FILE_READ_ONLY_VOLUME, &tmp); 140 | napi_set_named_property(env, result, "READ_ONLY_VOLUME", tmp); 141 | napi_get_boolean(env, data->fileSystemFlags & FILE_SEQUENTIAL_WRITE_ONCE, &tmp); 142 | napi_set_named_property(env, result, "SEQUENTIAL_WRITE_ONCE", tmp); 143 | napi_get_boolean(env, data->fileSystemFlags & FILE_DAX_VOLUME, &tmp); 144 | napi_set_named_property(env, result, "DAX_VOLUME", tmp); 145 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_SPARSE_FILES, &tmp); 146 | napi_set_named_property(env, result, "SUPPORTS_SPARSE_FILES", tmp); 147 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_REPARSE_POINTS, &tmp); 148 | napi_set_named_property(env, result, "SUPPORTS_REPARSE_POINTS", tmp); 149 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_REMOTE_STORAGE, &tmp); 150 | napi_set_named_property(env, result, "SUPPORTS_REMOTE_STORAGE", tmp); 151 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_POSIX_UNLINK_RENAME, &tmp); 152 | napi_set_named_property(env, result, "SUPPORTS_POSIX_UNLINK_RENAME", tmp); 153 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_BYPASS_IO, &tmp); 154 | napi_set_named_property(env, result, "SUPPORTS_BYPASS_IO", tmp); 155 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_OBJECT_IDS, &tmp); 156 | napi_set_named_property(env, result, "SUPPORTS_OBJECT_IDS", tmp); 157 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_ENCRYPTION, &tmp); 158 | napi_set_named_property(env, result, "SUPPORTS_ENCRYPTION", tmp); 159 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_TRANSACTIONS, &tmp); 160 | napi_set_named_property(env, result, "SUPPORTS_TRANSACTIONS", tmp); 161 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_HARD_LINKS, &tmp); 162 | napi_set_named_property(env, result, "SUPPORTS_HARD_LINKS", tmp); 163 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_EXTENDED_ATTRIBUTES, &tmp); 164 | napi_set_named_property(env, result, "SUPPORTS_EXTENDED_ATTRIBUTES", tmp); 165 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_OPEN_BY_FILE_ID, &tmp); 166 | napi_set_named_property(env, result, "SUPPORTS_OPEN_BY_FILE_ID", tmp); 167 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_USN_JOURNAL, &tmp); 168 | napi_set_named_property(env, result, "SUPPORTS_USN_JOURNAL", tmp); 169 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_INTEGRITY_STREAMS, &tmp); 170 | napi_set_named_property(env, result, "SUPPORTS_INTEGRITY_STREAMS", tmp); 171 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_BLOCK_REFCOUNTING, &tmp); 172 | napi_set_named_property(env, result, "SUPPORTS_BLOCK_REFCOUNTING", tmp); 173 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_SPARSE_VDL, &tmp); 174 | napi_set_named_property(env, result, "SUPPORTS_SPARSE_VDL", tmp); 175 | napi_get_boolean(env, data->fileSystemFlags & FILE_SUPPORTS_GHOSTING, &tmp); 176 | napi_set_named_property(env, result, "SUPPORTS_GHOSTING", tmp); 177 | 178 | return result; 179 | } 180 | static void execute(napi_env env, void* data) { 181 | cbdata* d = (cbdata*)data; 182 | d->result = func(d->path); 183 | } 184 | static void complete(napi_env env, napi_status status, void* data) { 185 | cbdata* d = (cbdata*)data; 186 | delete[]d->path; 187 | napi_value cb, self, argv; 188 | napi_get_reference_value(env, d->cb, &cb); 189 | napi_get_reference_value(env, d->self, &self); 190 | if (status == napi_ok && d->result) { 191 | argv = convert(env, d->result); 192 | delete d->result; 193 | } else { 194 | napi_get_null(env, &argv); 195 | } 196 | napi_call_function(env, self, cb, 1, &argv, NULL); 197 | napi_delete_reference(env, d->cb); 198 | napi_delete_reference(env, d->self); 199 | napi_delete_async_work(env, d->work); 200 | delete d; 201 | } 202 | }; -------------------------------------------------------------------------------- /src/getVolumeSpace.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class getVolumeSpace { 5 | public: 6 | const struct spaces { 7 | ULONGLONG totalSpace; 8 | ULONGLONG freeSpace; 9 | }; 10 | static spaces* func(const wchar_t* path) {//you need to delete the result yourself if it is not NULL 11 | ULARGE_INTEGER u1; 12 | ULARGE_INTEGER u2; 13 | spaces* result; 14 | if (GetDiskFreeSpaceExW(path, &u1, &u2, NULL)) { 15 | result = new spaces; 16 | result->freeSpace = u1.QuadPart; 17 | result->totalSpace = u2.QuadPart; 18 | } else { 19 | result = NULL; 20 | } 21 | return result; 22 | } 23 | static napi_value init(napi_env env, bool isSync = false) { 24 | napi_value f; 25 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 26 | return f; 27 | } 28 | private: 29 | const struct cbdata { 30 | napi_async_work work; 31 | napi_ref self; 32 | napi_ref cb; 33 | wchar_t* path; 34 | spaces* result; 35 | }; 36 | static napi_value sync(napi_env env, napi_callback_info info) { 37 | napi_value result; 38 | napi_get_new_target(env, info, &result); 39 | if (result) { 40 | result = NULL; 41 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 42 | } else { 43 | napi_value argv; 44 | size_t argc = 1; 45 | napi_get_cb_info(env, info, &argc, &argv, NULL, NULL); 46 | if (argc < 1) { 47 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 48 | } else { 49 | size_t str_len; 50 | napi_value tmp; 51 | napi_coerce_to_string(env, argv, &tmp); 52 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 53 | str_len += 1; 54 | wchar_t* str = new wchar_t[str_len]; 55 | napi_get_value_string_utf16(env, tmp, (char16_t*)str, str_len, NULL); 56 | spaces* r = func(str); 57 | delete[]str; 58 | if (r) { 59 | result = convert(env, r); 60 | delete r; 61 | } else { 62 | napi_get_null(env, &result); 63 | } 64 | } 65 | } 66 | return result; 67 | } 68 | static napi_value async(napi_env env, napi_callback_info info) { 69 | napi_value result; 70 | napi_get_new_target(env, info, &result); 71 | if (result) { 72 | result = NULL; 73 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 74 | } else { 75 | napi_value argv[2], self; 76 | size_t argc = 2; 77 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 78 | if (argc >= 2) { 79 | napi_valuetype t; 80 | napi_typeof(env, argv[1], &t); 81 | if (t == napi_function) { 82 | cbdata* data = new cbdata; 83 | size_t str_len; 84 | napi_value tmp; 85 | napi_create_reference(env, argv[1], 1, &data->cb); 86 | napi_create_reference(env, self, 1, &data->self); 87 | napi_coerce_to_string(env, argv[0], &tmp); 88 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 89 | str_len += 1; 90 | data->path = new wchar_t[str_len]; 91 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->path, str_len, NULL); 92 | napi_create_string_latin1(env, "fswin.getVolumeSize", NAPI_AUTO_LENGTH, &tmp); 93 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 94 | if (napi_queue_async_work(env, data->work) == napi_ok) { 95 | napi_get_boolean(env, true, &result); 96 | } else { 97 | napi_get_boolean(env, false, &result); 98 | napi_delete_reference(env, data->cb); 99 | napi_delete_reference(env, data->self); 100 | napi_delete_async_work(env, data->work); 101 | delete[]data->path; 102 | delete data; 103 | } 104 | } 105 | } 106 | if (!result) { 107 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 108 | } 109 | } 110 | return result; 111 | } 112 | static napi_value convert(napi_env env, spaces* data) { 113 | napi_value tmp, result; 114 | napi_create_object(env, &result); 115 | napi_create_int64(env, data->freeSpace, &tmp); 116 | napi_set_named_property(env, result, "FREE", tmp); 117 | napi_create_int64(env, data->totalSpace, &tmp); 118 | napi_set_named_property(env, result, "TOTAL", tmp); 119 | return result; 120 | } 121 | static void execute(napi_env env, void* data) { 122 | cbdata* d = (cbdata*)data; 123 | d->result = func(d->path); 124 | } 125 | static void complete(napi_env env, napi_status status, void* data) { 126 | cbdata* d = (cbdata*)data; 127 | delete[]d->path; 128 | napi_value cb, self, argv; 129 | napi_get_reference_value(env, d->cb, &cb); 130 | napi_get_reference_value(env, d->self, &self); 131 | if (status == napi_ok && d->result) { 132 | argv = convert(env, d->result); 133 | delete d->result; 134 | } else { 135 | napi_get_null(env, &argv); 136 | } 137 | napi_call_function(env, self, cb, 1, &argv, NULL); 138 | napi_delete_reference(env, d->cb); 139 | napi_delete_reference(env, d->self); 140 | napi_delete_async_work(env, d->work); 141 | delete d; 142 | } 143 | }; -------------------------------------------------------------------------------- /src/setAttributes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class setAttributes { 5 | public: 6 | const struct attrVal {//0=keep,1=yes,-1=no 7 | char archive; 8 | char hidden; 9 | char notContentIndexed; 10 | char offline; 11 | char readonly; 12 | char system; 13 | char temporary; 14 | char unpinned; 15 | char pinned; 16 | }; 17 | static bool func(const wchar_t* file, const attrVal* attr) { 18 | bool result; 19 | DWORD oldattr = GetFileAttributesW(file); 20 | if (oldattr == INVALID_FILE_ATTRIBUTES) { 21 | result = false; 22 | } else { 23 | DWORD newattr = oldattr; 24 | if (attr->archive < 0 && newattr & FILE_ATTRIBUTE_ARCHIVE) { 25 | newattr ^= FILE_ATTRIBUTE_ARCHIVE; 26 | } else if (attr->archive > 0 && !(newattr & FILE_ATTRIBUTE_ARCHIVE)) { 27 | newattr |= FILE_ATTRIBUTE_ARCHIVE; 28 | } 29 | if (attr->hidden < 0 && newattr & FILE_ATTRIBUTE_HIDDEN) { 30 | newattr ^= FILE_ATTRIBUTE_HIDDEN; 31 | } else if (attr->hidden > 0 && !(newattr & FILE_ATTRIBUTE_HIDDEN)) { 32 | newattr |= FILE_ATTRIBUTE_HIDDEN; 33 | } 34 | if (attr->notContentIndexed < 0 && newattr & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) { 35 | newattr ^= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; 36 | } else if (attr->notContentIndexed > 0 && !(newattr & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) { 37 | newattr |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; 38 | } 39 | if (attr->offline < 0 && newattr & FILE_ATTRIBUTE_OFFLINE) { 40 | newattr ^= FILE_ATTRIBUTE_OFFLINE; 41 | } else if (attr->offline > 0 && !(newattr & FILE_ATTRIBUTE_OFFLINE)) { 42 | newattr |= FILE_ATTRIBUTE_OFFLINE; 43 | } 44 | if (attr->readonly < 0 && newattr & FILE_ATTRIBUTE_READONLY) { 45 | newattr ^= FILE_ATTRIBUTE_READONLY; 46 | } else if (attr->readonly > 0 && !(newattr & FILE_ATTRIBUTE_READONLY)) { 47 | newattr |= FILE_ATTRIBUTE_READONLY; 48 | } 49 | if (attr->system < 0 && newattr&FILE_ATTRIBUTE_SYSTEM) { 50 | newattr ^= FILE_ATTRIBUTE_SYSTEM; 51 | } else if (attr->system > 0 && !(newattr & FILE_ATTRIBUTE_SYSTEM)) { 52 | newattr |= FILE_ATTRIBUTE_SYSTEM; 53 | } 54 | if (attr->temporary < 0 && newattr & FILE_ATTRIBUTE_TEMPORARY) { 55 | newattr ^= FILE_ATTRIBUTE_TEMPORARY; 56 | } else if (attr->temporary > 0 && !(newattr & FILE_ATTRIBUTE_TEMPORARY)) { 57 | newattr |= FILE_ATTRIBUTE_TEMPORARY; 58 | } 59 | if (attr->unpinned < 0 && newattr & FILE_ATTRIBUTE_UNPINNED) { 60 | newattr ^= FILE_ATTRIBUTE_UNPINNED; 61 | } else if (attr->unpinned > 0 && !(newattr & FILE_ATTRIBUTE_UNPINNED)) { 62 | newattr |= FILE_ATTRIBUTE_UNPINNED; 63 | } 64 | if (attr->pinned < 0 && newattr & FILE_ATTRIBUTE_PINNED) { 65 | newattr ^= FILE_ATTRIBUTE_PINNED; 66 | } else if (attr->pinned > 0 && !(newattr & FILE_ATTRIBUTE_PINNED)) { 67 | newattr |= FILE_ATTRIBUTE_PINNED; 68 | } 69 | if (newattr == oldattr) { 70 | result = true; 71 | } else { 72 | result = SetFileAttributesW(file, newattr) ? true : false; 73 | } 74 | } 75 | return result; 76 | } 77 | static napi_value init(napi_env env, bool isSync = false) { 78 | napi_value f; 79 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 80 | return f; 81 | } 82 | private: 83 | const struct cbdata { 84 | napi_async_work work; 85 | napi_ref self; 86 | napi_ref cb; 87 | wchar_t* path; 88 | attrVal* attr; 89 | bool result; 90 | }; 91 | static napi_value sync(napi_env env, napi_callback_info info) { 92 | napi_value result; 93 | napi_get_new_target(env, info, &result); 94 | if (result) { 95 | result = NULL; 96 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 97 | } else { 98 | napi_value argv[2]; 99 | size_t argc = 2; 100 | napi_get_cb_info(env, info, &argc, argv, NULL, NULL); 101 | if (argc >= 2) { 102 | napi_valuetype t; 103 | napi_typeof(env, argv[1], &t); 104 | if (t == napi_object) { 105 | size_t str_len; 106 | napi_value tmp; 107 | napi_coerce_to_string(env, argv[0], &tmp); 108 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 109 | str_len += 1; 110 | wchar_t* str = new wchar_t[str_len]; 111 | napi_get_value_string_utf16(env, tmp, (char16_t*)str, str_len, NULL); 112 | attrVal* attr = convert(env, argv[1]); 113 | napi_get_boolean(env, func(str, attr), &result); 114 | delete attr; 115 | delete[]str; 116 | } 117 | } 118 | if (!result) { 119 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 120 | } 121 | } 122 | return result; 123 | } 124 | static napi_value async(napi_env env, napi_callback_info info) { 125 | napi_value result; 126 | napi_get_new_target(env, info, &result); 127 | if (result) { 128 | result = NULL; 129 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 130 | } else { 131 | napi_value argv[3], self; 132 | size_t argc = 3; 133 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 134 | if (argc >= 3) { 135 | napi_valuetype t, t1; 136 | napi_typeof(env, argv[1], &t1); 137 | napi_typeof(env, argv[2], &t); 138 | if (t1 == napi_object && t == napi_function) { 139 | cbdata* data = new cbdata; 140 | size_t str_len; 141 | napi_value tmp; 142 | data->attr = convert(env, argv[1]); 143 | napi_create_reference(env, argv[2], 1, &data->cb); 144 | napi_create_reference(env, self, 1, &data->self); 145 | napi_coerce_to_string(env, argv[0], &tmp); 146 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 147 | str_len += 1; 148 | data->path = new wchar_t[str_len]; 149 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->path, str_len, NULL); 150 | napi_create_string_latin1(env, "fswin.setAttrubutes", NAPI_AUTO_LENGTH, &tmp); 151 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 152 | if (napi_queue_async_work(env, data->work) == napi_ok) { 153 | napi_get_boolean(env, true, &result); 154 | } else { 155 | napi_get_boolean(env, false, &result); 156 | napi_delete_reference(env, data->cb); 157 | napi_delete_reference(env, data->self); 158 | napi_delete_async_work(env, data->work); 159 | delete[]data->path; 160 | delete data; 161 | } 162 | } 163 | } 164 | if (!result) { 165 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 166 | } 167 | } 168 | return result; 169 | } 170 | static attrVal* convert(napi_env env, napi_value attr) {//delete the result yourself 171 | attrVal* result = new attrVal; 172 | bool tmp; 173 | napi_value val; 174 | napi_has_named_property(env, attr, SYB_FILEATTR_ISARCHIVED, &tmp); 175 | if (tmp) { 176 | napi_get_named_property(env, attr, SYB_FILEATTR_ISARCHIVED, &val); 177 | napi_get_value_bool(env, val, &tmp); 178 | result->archive = tmp ? 1 : -1; 179 | } else { 180 | result->archive = 0; 181 | } 182 | napi_has_named_property(env, attr, SYB_FILEATTR_ISHIDDEN, &tmp); 183 | if (tmp) { 184 | napi_get_named_property(env, attr, SYB_FILEATTR_ISHIDDEN, &val); 185 | napi_get_value_bool(env, val, &tmp); 186 | result->hidden = tmp ? 1 : -1; 187 | } else { 188 | result->hidden = 0; 189 | } 190 | napi_has_named_property(env, attr, SYB_FILEATTR_ISNOTCONTENTINDEXED, &tmp); 191 | if (tmp) { 192 | napi_get_named_property(env, attr, SYB_FILEATTR_ISNOTCONTENTINDEXED, &val); 193 | napi_get_value_bool(env, val, &tmp); 194 | result->notContentIndexed = tmp ? 1 : -1; 195 | } else { 196 | result->notContentIndexed = 0; 197 | } 198 | napi_has_named_property(env, attr, SYB_FILEATTR_ISOFFLINE, &tmp); 199 | if (tmp) { 200 | napi_get_named_property(env, attr, SYB_FILEATTR_ISOFFLINE, &val); 201 | napi_get_value_bool(env, val, &tmp); 202 | result->offline = tmp ? 1 : -1; 203 | } else { 204 | result->offline = 0; 205 | } 206 | napi_has_named_property(env, attr, SYB_FILEATTR_ISREADONLY, &tmp); 207 | if (tmp) { 208 | napi_get_named_property(env, attr, SYB_FILEATTR_ISREADONLY, &val); 209 | napi_get_value_bool(env, val, &tmp); 210 | result->readonly = tmp ? 1 : -1; 211 | } else { 212 | result->readonly = 0; 213 | } 214 | napi_has_named_property(env, attr, SYB_FILEATTR_ISSYSTEM, &tmp); 215 | if (tmp) { 216 | napi_get_named_property(env, attr, SYB_FILEATTR_ISSYSTEM, &val); 217 | napi_get_value_bool(env, val, &tmp); 218 | result->system = tmp ? 1 : -1; 219 | } else { 220 | result->system = 0; 221 | } 222 | napi_has_named_property(env, attr, SYB_FILEATTR_ISTEMPORARY, &tmp); 223 | if (tmp) { 224 | napi_get_named_property(env, attr, SYB_FILEATTR_ISTEMPORARY, &val); 225 | napi_get_value_bool(env, val, &tmp); 226 | result->temporary = tmp ? 1 : -1; 227 | } else { 228 | result->temporary = 0; 229 | } 230 | napi_has_named_property(env, attr, SYB_FILEATTR_ISUNPINNED, &tmp); 231 | if (tmp) { 232 | napi_get_named_property(env, attr, SYB_FILEATTR_ISUNPINNED, &val); 233 | napi_get_value_bool(env, val, &tmp); 234 | result->unpinned = tmp ? 1 : -1; 235 | } else { 236 | result->unpinned = 0; 237 | } 238 | napi_has_named_property(env, attr, SYB_FILEATTR_ISPINNED, &tmp); 239 | if (tmp) { 240 | napi_get_named_property(env, attr, SYB_FILEATTR_ISPINNED, &val); 241 | napi_get_value_bool(env, val, &tmp); 242 | result->pinned = tmp ? 1 : -1; 243 | } else { 244 | result->pinned = 0; 245 | } 246 | return result; 247 | } 248 | static void execute(napi_env env, void* data) { 249 | cbdata* d = (cbdata*)data; 250 | d->result = func(d->path, d->attr); 251 | } 252 | static void complete(napi_env env, napi_status status, void* data) { 253 | cbdata* d = (cbdata*)data; 254 | delete[]d->path; 255 | delete d->attr; 256 | napi_value cb, self, argv; 257 | napi_get_reference_value(env, d->cb, &cb); 258 | napi_get_reference_value(env, d->self, &self); 259 | napi_get_boolean(env, status == napi_ok && d->result, &argv); 260 | napi_call_function(env, self, cb, 1, &argv, NULL); 261 | napi_delete_reference(env, d->cb); 262 | napi_delete_reference(env, d->self); 263 | napi_delete_async_work(env, d->work); 264 | delete d; 265 | } 266 | }; -------------------------------------------------------------------------------- /src/setCompression.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class setCompression { 5 | public: 6 | static bool func(const wchar_t* path, const bool compress, const bool create) { 7 | bool result = false; 8 | HANDLE hnd = CreateFileW(path, FILE_GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, create ? OPEN_ALWAYS : OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 9 | if (hnd != INVALID_HANDLE_VALUE) { 10 | USHORT c = compress ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE; 11 | DWORD d; 12 | if (DeviceIoControl(hnd, FSCTL_SET_COMPRESSION, &c, sizeof(USHORT), NULL, 0, &d, NULL)) { 13 | result = true; 14 | } 15 | CloseHandle(hnd); 16 | } 17 | return result; 18 | } 19 | static napi_value init(napi_env env, bool isSync = false) { 20 | napi_value f; 21 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 22 | return f; 23 | } 24 | private: 25 | const struct cbdata { 26 | napi_async_work work; 27 | napi_ref self; 28 | napi_ref cb; 29 | wchar_t* path; 30 | bool compress; 31 | bool create; 32 | bool result; 33 | }; 34 | static napi_value sync(napi_env env, napi_callback_info info) { 35 | napi_value result; 36 | napi_get_new_target(env, info, &result); 37 | if (result) { 38 | result = NULL; 39 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 40 | } else { 41 | napi_value argv[3]; 42 | size_t argc = 3; 43 | napi_get_cb_info(env, info, &argc, argv, NULL, NULL); 44 | if (argc < 1) { 45 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 46 | } else { 47 | size_t str_len; 48 | napi_value tmp; 49 | napi_coerce_to_string(env, argv[0], &tmp); 50 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 51 | str_len += 1; 52 | wchar_t* path = new wchar_t[str_len]; 53 | napi_get_value_string_utf16(env, tmp, (char16_t*)path, str_len, NULL); 54 | bool compress = false, create = false; 55 | if (argc > 1) { 56 | napi_coerce_to_bool(env, argv[1], &tmp); 57 | napi_get_value_bool(env, tmp, &compress); 58 | if (argc > 2) { 59 | napi_coerce_to_bool(env, argv[2], &tmp); 60 | napi_get_value_bool(env, argv[2], &create); 61 | } 62 | } 63 | napi_get_boolean(env, func(path, compress, create), &result); 64 | delete[]path; 65 | } 66 | } 67 | return result; 68 | } 69 | static napi_value async(napi_env env, napi_callback_info info) { 70 | napi_value result; 71 | napi_get_new_target(env, info, &result); 72 | if (result) { 73 | result = NULL; 74 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 75 | } else { 76 | napi_value argv[4], self; 77 | size_t argc = 4; 78 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 79 | if (argc >= 2) { 80 | napi_valuetype t; 81 | napi_typeof(env, argv[1], &t); 82 | if (t == napi_function) { 83 | cbdata* data = new cbdata; 84 | data->compress = data->create = false; 85 | size_t str_len; 86 | napi_value tmp; 87 | napi_create_reference(env, argv[1], 1, &data->cb); 88 | napi_create_reference(env, self, 1, &data->self); 89 | napi_coerce_to_string(env, argv[0], &tmp); 90 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 91 | str_len += 1; 92 | data->path = new wchar_t[str_len]; 93 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->path, str_len, NULL); 94 | if (argc > 2) { 95 | napi_coerce_to_bool(env, argv[2], &tmp); 96 | napi_get_value_bool(env, tmp, &data->compress); 97 | if (argc > 3) { 98 | napi_coerce_to_bool(env, argv[3], &tmp); 99 | napi_get_value_bool(env, tmp, &data->create); 100 | } 101 | } 102 | napi_create_string_latin1(env, "fswin.ntfs.setCompression", NAPI_AUTO_LENGTH, &tmp); 103 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 104 | if (napi_queue_async_work(env, data->work) == napi_ok) { 105 | napi_get_boolean(env, true, &result); 106 | } else { 107 | napi_get_boolean(env, false, &result); 108 | napi_delete_reference(env, data->cb); 109 | napi_delete_reference(env, data->self); 110 | napi_delete_async_work(env, data->work); 111 | delete[]data->path; 112 | delete data; 113 | } 114 | } 115 | } 116 | if (!result) { 117 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 118 | } 119 | } 120 | return result; 121 | } 122 | static void execute(napi_env env, void* data) { 123 | cbdata* d = (cbdata*)data; 124 | d->result = func(d->path, d->compress, d->create); 125 | } 126 | static void complete(napi_env env, napi_status status, void* data) { 127 | cbdata* d = (cbdata*)data; 128 | delete[]d->path; 129 | napi_value cb, self, argv; 130 | napi_get_reference_value(env, d->cb, &cb); 131 | napi_get_reference_value(env, d->self, &self); 132 | napi_get_boolean(env, status == napi_ok && d->result, &argv); 133 | napi_call_function(env, self, cb, 1, &argv, NULL); 134 | napi_delete_reference(env, d->cb); 135 | napi_delete_reference(env, d->self); 136 | napi_delete_async_work(env, d->work); 137 | delete d; 138 | } 139 | }; -------------------------------------------------------------------------------- /src/setShortName.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class setShortName { 5 | public: 6 | static bool func(const wchar_t* path, const wchar_t* newname) { 7 | bool result; 8 | if (ensurePrivilege("SeRestorePrivilege")) {//make sure the process has SE_RESTORE_NAME privilege 9 | HANDLE hnd = CreateFileW(path, GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 10 | if (hnd == INVALID_HANDLE_VALUE) { 11 | result = false; 12 | } else { 13 | result = SetFileShortNameW(hnd, newname ? newname : L"") ? true : false; 14 | CloseHandle(hnd); 15 | } 16 | } else { 17 | result = false; 18 | } 19 | return result; 20 | } 21 | static napi_value init(napi_env env, bool isSync = false) { 22 | napi_value f; 23 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 24 | return f; 25 | } 26 | private: 27 | const struct cbdata { 28 | napi_async_work work; 29 | napi_ref self; 30 | napi_ref cb; 31 | wchar_t* path; 32 | wchar_t* newname; 33 | bool result; 34 | }; 35 | static napi_value sync(napi_env env, napi_callback_info info) { 36 | napi_value result; 37 | napi_get_new_target(env, info, &result); 38 | if (result) { 39 | result = NULL; 40 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 41 | } else { 42 | napi_value argv[2]; 43 | size_t argc = 2; 44 | napi_get_cb_info(env, info, &argc, argv, NULL, NULL); 45 | if (argc < 2) { 46 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 47 | } else { 48 | size_t str_len; 49 | napi_value tmp; 50 | napi_coerce_to_string(env, argv[0], &tmp); 51 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 52 | str_len += 1; 53 | wchar_t* path = new wchar_t[str_len]; 54 | napi_get_value_string_utf16(env, tmp, (char16_t*)path, str_len, NULL); 55 | napi_coerce_to_string(env, argv[1], &tmp); 56 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 57 | str_len += 1; 58 | wchar_t* newname = new wchar_t[str_len]; 59 | napi_get_value_string_utf16(env, tmp, (char16_t*)path, str_len, NULL); 60 | napi_get_boolean(env, func(path, newname), &result); 61 | delete[]path; 62 | delete[]newname; 63 | } 64 | } 65 | return result; 66 | } 67 | static napi_value async(napi_env env, napi_callback_info info) { 68 | napi_value result; 69 | napi_get_new_target(env, info, &result); 70 | if (result) { 71 | result = NULL; 72 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 73 | } else { 74 | napi_value argv[3], self; 75 | size_t argc = 3; 76 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 77 | if (argc >= 3) { 78 | napi_valuetype t; 79 | napi_typeof(env, argv[2], &t); 80 | if (t == napi_function) { 81 | cbdata* data = new cbdata; 82 | size_t str_len; 83 | napi_value tmp; 84 | napi_create_reference(env, argv[2], 1, &data->cb); 85 | napi_create_reference(env, self, 1, &data->self); 86 | napi_coerce_to_string(env, argv[0], &tmp); 87 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 88 | str_len += 1; 89 | data->path = new wchar_t[str_len]; 90 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->path, str_len, NULL); 91 | napi_coerce_to_string(env, argv[1], &tmp); 92 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 93 | str_len += 1; 94 | data->newname = new wchar_t[str_len]; 95 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->newname, str_len, NULL); 96 | napi_create_string_latin1(env, "fswin.ntfs.setShortName", NAPI_AUTO_LENGTH, &tmp); 97 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 98 | if (napi_queue_async_work(env, data->work) == napi_ok) { 99 | napi_get_boolean(env, true, &result); 100 | } else { 101 | napi_get_boolean(env, false, &result); 102 | napi_delete_reference(env, data->cb); 103 | napi_delete_reference(env, data->self); 104 | napi_delete_async_work(env, data->work); 105 | delete[]data->path; 106 | delete[]data->newname; 107 | delete data; 108 | } 109 | } 110 | } 111 | if (!result) { 112 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 113 | } 114 | } 115 | return result; 116 | } 117 | static void execute(napi_env env, void* data) { 118 | cbdata* d = (cbdata*)data; 119 | d->result = func(d->path, d->newname); 120 | } 121 | static void complete(napi_env env, napi_status status, void* data) { 122 | cbdata* d = (cbdata*)data; 123 | delete[]d->path; 124 | delete[]d->newname; 125 | napi_value cb, self, argv; 126 | napi_get_reference_value(env, d->cb, &cb); 127 | napi_get_reference_value(env, d->self, &self); 128 | napi_get_boolean(env, status == napi_ok && d->result, &argv); 129 | napi_call_function(env, self, cb, 1, &argv, NULL); 130 | napi_delete_reference(env, d->cb); 131 | napi_delete_reference(env, d->self); 132 | napi_delete_async_work(env, d->work); 133 | delete d; 134 | } 135 | }; -------------------------------------------------------------------------------- /src/setSparse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class setSparse { 5 | public: 6 | static bool func(const wchar_t* path, const bool create) { 7 | bool result = false; 8 | HANDLE hnd = CreateFileW(path, FILE_GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, create ? OPEN_ALWAYS : OPEN_EXISTING, 0, NULL); 9 | if (hnd != INVALID_HANDLE_VALUE) { 10 | DWORD d; 11 | if (DeviceIoControl(hnd, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &d, NULL)) { 12 | result = true; 13 | } 14 | CloseHandle(hnd); 15 | } 16 | return result; 17 | } 18 | static napi_value init(napi_env env, bool isSync = false) { 19 | napi_value f; 20 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 21 | return f; 22 | } 23 | private: 24 | const struct cbdata { 25 | napi_async_work work; 26 | napi_ref self; 27 | napi_ref cb; 28 | wchar_t* path; 29 | bool create; 30 | bool result; 31 | }; 32 | static napi_value sync(napi_env env, napi_callback_info info) { 33 | napi_value result; 34 | napi_get_new_target(env, info, &result); 35 | if (result) { 36 | result = NULL; 37 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 38 | } else { 39 | napi_value argv[2]; 40 | size_t argc = 2; 41 | napi_get_cb_info(env, info, &argc, argv, NULL, NULL); 42 | if (argc < 1) { 43 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 44 | } else { 45 | size_t str_len; 46 | napi_value tmp; 47 | napi_coerce_to_string(env, argv[0], &tmp); 48 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 49 | str_len += 1; 50 | wchar_t* path = new wchar_t[str_len]; 51 | napi_get_value_string_utf16(env, tmp, (char16_t*)path, str_len, NULL); 52 | bool create = false; 53 | if (argc > 1) { 54 | napi_coerce_to_bool(env, argv[1], &tmp); 55 | napi_get_value_bool(env, tmp, &create); 56 | } 57 | napi_get_boolean(env, func(path, create), &result); 58 | delete[]path; 59 | } 60 | } 61 | return result; 62 | } 63 | static napi_value async(napi_env env, napi_callback_info info) { 64 | napi_value result; 65 | napi_get_new_target(env, info, &result); 66 | if (result) { 67 | result = NULL; 68 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 69 | } else { 70 | napi_value argv[3], self; 71 | size_t argc = 3; 72 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 73 | if (argc >= 2) { 74 | napi_valuetype t; 75 | napi_typeof(env, argv[1], &t); 76 | if (t == napi_function) { 77 | cbdata* data = new cbdata; 78 | data->create = false; 79 | size_t str_len; 80 | napi_value tmp; 81 | napi_create_reference(env, argv[1], 1, &data->cb); 82 | napi_create_reference(env, self, 1, &data->self); 83 | napi_coerce_to_string(env, argv[0], &tmp); 84 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 85 | str_len += 1; 86 | data->path = new wchar_t[str_len]; 87 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->path, str_len, NULL); 88 | if (argc > 2) { 89 | napi_coerce_to_bool(env, argv[2], &tmp); 90 | napi_get_value_bool(env, tmp, &data->create); 91 | } 92 | napi_create_string_latin1(env, "fswin.ntfs.setSparse", NAPI_AUTO_LENGTH, &tmp); 93 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 94 | if (napi_queue_async_work(env, data->work) == napi_ok) { 95 | napi_get_boolean(env, true, &result); 96 | } else { 97 | napi_get_boolean(env, false, &result); 98 | napi_delete_reference(env, data->cb); 99 | napi_delete_reference(env, data->self); 100 | napi_delete_async_work(env, data->work); 101 | delete[]data->path; 102 | delete data; 103 | } 104 | } 105 | } 106 | if (!result) { 107 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 108 | } 109 | } 110 | return result; 111 | } 112 | static void execute(napi_env env, void* data) { 113 | cbdata* d = (cbdata*)data; 114 | d->result = func(d->path, d->create); 115 | } 116 | static void complete(napi_env env, napi_status status, void* data) { 117 | cbdata* d = (cbdata*)data; 118 | delete[]d->path; 119 | napi_value cb, self, argv; 120 | napi_get_reference_value(env, d->cb, &cb); 121 | napi_get_reference_value(env, d->self, &self); 122 | napi_get_boolean(env, status == napi_ok && d->result, &argv); 123 | napi_call_function(env, self, cb, 1, &argv, NULL); 124 | napi_delete_reference(env, d->cb); 125 | napi_delete_reference(env, d->self); 126 | napi_delete_async_work(env, d->work); 127 | delete d; 128 | } 129 | }; -------------------------------------------------------------------------------- /src/setVolumeLabel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class setVolumeLabel { 5 | public: 6 | static napi_value init(napi_env env, bool isSync = false) { 7 | napi_value f; 8 | napi_create_function(env, NULL, 0, isSync ? sync : async, NULL, &f); 9 | return f; 10 | } 11 | private: 12 | const struct cbdata { 13 | napi_async_work work; 14 | napi_ref self; 15 | napi_ref cb; 16 | wchar_t* path; 17 | wchar_t* label; 18 | BOOL result; 19 | }; 20 | static napi_value sync(napi_env env, napi_callback_info info) { 21 | napi_value result; 22 | napi_get_new_target(env, info, &result); 23 | if (result) { 24 | result = NULL; 25 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 26 | } else { 27 | napi_value argv[2]; 28 | size_t argc = 2; 29 | napi_get_cb_info(env, info, &argc, argv, NULL, NULL); 30 | if (argc < 2) { 31 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 32 | } else { 33 | size_t str_len; 34 | napi_value tmp; 35 | napi_coerce_to_string(env, argv[0], &tmp); 36 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 37 | str_len += 1; 38 | wchar_t* path = new wchar_t[str_len]; 39 | napi_get_value_string_utf16(env, tmp, (char16_t*)path, str_len, NULL); 40 | napi_coerce_to_string(env, argv[1], &tmp); 41 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 42 | str_len += 1; 43 | wchar_t* label = new wchar_t[str_len]; 44 | napi_get_value_string_utf16(env, tmp, (char16_t*)label, str_len, NULL); 45 | napi_get_boolean(env, SetVolumeLabelW(path, label), &result); 46 | delete[]path; 47 | delete[]label; 48 | } 49 | } 50 | return result; 51 | } 52 | static napi_value async(napi_env env, napi_callback_info info) { 53 | napi_value result; 54 | napi_get_new_target(env, info, &result); 55 | if (result) { 56 | result = NULL; 57 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 58 | } else { 59 | napi_value argv[3], self; 60 | size_t argc = 3; 61 | napi_get_cb_info(env, info, &argc, argv, &self, NULL); 62 | if (argc >= 3) { 63 | napi_valuetype t; 64 | napi_typeof(env, argv[2], &t); 65 | if (t == napi_function) { 66 | cbdata* data = new cbdata; 67 | size_t str_len; 68 | napi_value tmp; 69 | napi_create_reference(env, argv[2], 1, &data->cb); 70 | napi_create_reference(env, self, 1, &data->self); 71 | napi_coerce_to_string(env, argv[0], &tmp); 72 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 73 | str_len += 1; 74 | data->path = new wchar_t[str_len]; 75 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->path, str_len, NULL); 76 | napi_coerce_to_string(env, argv[1], &tmp); 77 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 78 | str_len += 1; 79 | data->label = new wchar_t[str_len]; 80 | napi_get_value_string_utf16(env, tmp, (char16_t*)data->label, str_len, NULL); 81 | napi_create_string_latin1(env, "fswin.setVolumeLabel", NAPI_AUTO_LENGTH, &tmp); 82 | napi_create_async_work(env, NULL, tmp, execute, complete, data, &data->work); 83 | if (napi_queue_async_work(env, data->work) == napi_ok) { 84 | napi_get_boolean(env, true, &result); 85 | } else { 86 | napi_get_boolean(env, false, &result); 87 | napi_delete_reference(env, data->cb); 88 | napi_delete_reference(env, data->self); 89 | napi_delete_async_work(env, data->work); 90 | delete[]data->path; 91 | delete[]data->label; 92 | delete data; 93 | } 94 | } 95 | } 96 | if (!result) { 97 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 98 | } 99 | } 100 | return result; 101 | } 102 | static void execute(napi_env env, void* data) { 103 | cbdata* d = (cbdata*)data; 104 | d->result = SetVolumeLabelW(d->path, d->label); 105 | } 106 | static void complete(napi_env env, napi_status status, void* data) { 107 | cbdata* d = (cbdata*)data; 108 | delete[]d->path; 109 | delete[]d->label; 110 | napi_value cb, self, argv; 111 | napi_get_reference_value(env, d->cb, &cb); 112 | napi_get_reference_value(env, d->self, &self); 113 | napi_get_boolean(env, status == napi_ok && d->result, &argv); 114 | napi_call_function(env, self, cb, 1, &argv, NULL); 115 | napi_delete_reference(env, d->cb); 116 | napi_delete_reference(env, d->self); 117 | napi_delete_async_work(env, d->work); 118 | delete d; 119 | } 120 | }; -------------------------------------------------------------------------------- /src/splitPath.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.h" 3 | 4 | class splitPath { 5 | public: 6 | const struct splitedPath { 7 | DWORD parentLen;//the length of the parent 8 | const wchar_t* name;//this could also be considered as the start position of the name 9 | }; 10 | static splitedPath* func(const wchar_t* path) {//you need to delete the return value your self 11 | const wchar_t* s = L"\\\\", s1 = L'\\'; 12 | DWORD i, j = 0, k = 0, l = (DWORD)wcslen(path), m = (DWORD)wcslen(s); 13 | if (wcsncmp(s, path, m) == 0) {//is network path 14 | for (i = m + 1; i < l - 1; i++) { 15 | if (path[i] == s1) { 16 | if (++k == 2) { 17 | j = i + 1; 18 | break; 19 | } 20 | } 21 | } 22 | if (k == 2) { 23 | k = 0; 24 | for (i = l - 2; i > j + 1; i--) { 25 | if (path[i] == s1) { 26 | j = i; 27 | k = 1; 28 | break; 29 | } 30 | } 31 | } else { 32 | k = 0; 33 | } 34 | } else {//is local path 35 | for (i = l - 2; i > 1; i--) { 36 | if (path[i] == s1) { 37 | if (j == 0) {//perhaps it's a rootdir 38 | j = i + 1; 39 | } else {//it's not a rootdir 40 | j -= 1; 41 | k = 1; 42 | break; 43 | } 44 | } 45 | } 46 | } 47 | splitedPath* r = new splitedPath; 48 | r->parentLen = j; 49 | r->name = &path[j > 0 ? j + k : j]; 50 | return r; 51 | } 52 | static napi_value init(napi_env env) { 53 | napi_value f; 54 | napi_create_function(env, NULL, 0, sync, NULL, &f); 55 | return f; 56 | } 57 | private: 58 | static napi_value sync(napi_env env, napi_callback_info info) { 59 | napi_value result; 60 | napi_get_new_target(env, info, &result); 61 | if (result) { 62 | result = NULL; 63 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_NOT_A_CONSTRUCTOR); 64 | } else { 65 | napi_value argv; 66 | size_t argc = 1; 67 | napi_get_cb_info(env, info, &argc, &argv, NULL, NULL); 68 | if (argc < 1) { 69 | napi_throw_error(env, SYB_EXP_INVAL, SYB_ERR_WRONG_ARGUMENTS); 70 | } else { 71 | size_t str_len; 72 | napi_value tmp; 73 | napi_coerce_to_string(env, argv, &tmp); 74 | napi_get_value_string_utf16(env, tmp, NULL, 0, &str_len); 75 | str_len += 1; 76 | wchar_t* str = new wchar_t[str_len]; 77 | napi_get_value_string_utf16(env, tmp, (char16_t*)str, str_len, NULL); 78 | splitedPath* s = func(str); 79 | napi_create_object(env, &result); 80 | napi_create_string_utf16(env, (char16_t*)str, s->parentLen, &tmp); 81 | napi_set_named_property(env, result, "PARENT", tmp); 82 | napi_create_string_utf16(env, (char16_t*)s->name, wcslen(s->name), &tmp); 83 | napi_set_named_property(env, result, "NAME", tmp); 84 | delete s; 85 | delete[]str; 86 | } 87 | } 88 | return result; 89 | } 90 | }; --------------------------------------------------------------------------------