问题
I'm trying to implement the answer to this question about monitoring a Windows filesystem asynchronously. I'm using js ctypes within a ChomeWorker as part of a XULRunner application but I assume this would be the same if I implemented as a Firefox add-on.
As part of the task, I have tried to declare the function ReadDirectoryChangesW as follows (based on my limited knowledge of js ctypes and the MSDN documentation).
const BOOL = ctypes.bool;
const DWORD = ctypes.uint32_t;
const LPDWORD = ctypes.uint32_t.ptr;
const HANDLE = ctypes.int32_t;
const LPVOID = ctypes.voidptr_t;
var library = self.library = ctypes.open("Kernel32.dll");
ReadDirectoryChangesW = library.declare(
"ReadDirectoryChangesW"
, ctypes.winapi_abi
, BOOL // return type
, HANDLE // hDirectory
, LPVOID // lpBuffer
, DWORD // nBufferLength
, BOOL // bWatchSubtree
, DWORD // dwNotifyFilter
, LPDWORD // lpBytesReturned
);
In addition (not featured here), I have declared function mappings for FindFirstChangeNotification()
and WaitForSingleObject()
which seem to work fine.
The problem I have is that when a filesystem event occurs, I have no idea what I'm supposed to pass in to the lpBuffer argument, or how to interpret the result.
All of the C++ examples seem to use a DWORD
array and then cast out the results. My attempt at that is as follows:
const DWORD_ARRAY = new ctypes.ArrayType(DWORD);
var lBuffer = new DWORD_ARRAY(4000);
var lBufferSize = DWORD.size * 4000;
var lBytesOut = new LPDWORD();
ReadDirectoryChangesW(lHandle, lBuffer.address(), lBufferSize, true, WATCH_ALL, lBytesOut)
This seems to just crash XULRunner every time.
Can anyone suggest what I should pass in for the lpBuffer argument and/or how to get results back from ReadDirectoryChangesW()
? All I can find online is C++ examples and they're not a lot of help. Thanks.
回答1:
Here is a cleaner solution: read the comments, lots of learning there. For type definitions, see here.
var path = OS.Constants.Path.desktopDir; // path to monitor
var hDirectory = ostypes.API('CreateFile')(path, ostypes.CONST.FILE_LIST_DIRECTORY | ostypes.CONST.GENERIC_READ, ostypes.CONST.FILE_SHARE_READ | ostypes.CONST.FILE_SHARE_WRITE, null, ostypes.CONST.OPEN_EXISTING, ostypes.CONST.FILE_FLAG_BACKUP_SEMANTICS | ostypes.CONST.FILE_FLAG_OVERLAPPED, null);
console.info('hDirectory:', hDirectory.toString(), uneval(hDirectory));
if (ctypes.winLastError != 0) { //cutils.jscEqual(hDirectory, ostypes.CONST.INVALID_HANDLE_VALUE)) { // commented this out cuz hDirectory is returned as `ctypes.voidptr_t(ctypes.UInt64("0xb18"))` and i dont know what it will be when it returns -1 but the returend when put through jscEqual gives `"breaking as no targetType.size on obj level:" "ctypes.voidptr_t(ctypes.UInt64("0xb18"))"`
console.error('Failed hDirectory, winLastError:', ctypes.winLastError);
throw new Error({
name: 'os-api-error',
message: 'Failed to CreateFile',
});
}
var dummyForSize = ostypes.TYPE.FILE_NOTIFY_INFORMATION.array(1)(); // accept max of 1 notifications at once (in application you should set this to like 50 or something higher as its very possible for more then 1 notification to be reported in one read/call to ReadDirectoryChangesW)
console.log('dummyForSize.constructor.size:', dummyForSize.constructor.size);
console.log('ostypes.TYPE.DWORD.size:', ostypes.TYPE.DWORD.size);
var dummyForSize_DIVIDED_BY_DwordSize = dummyForSize.constructor.size / ostypes.TYPE.DWORD.size;
console.log('dummyForSize.constructor.size / ostypes.TYPE.DWORD.size:', dummyForSize_DIVIDED_BY_DwordSize, Math.ceil(dummyForSize_DIVIDED_BY_DwordSize)); // should be whole int but lets round up with Math.ceil just in case
var temp_buffer = ostypes.TYPE.DWORD.array(Math.ceil(dummyForSize_DIVIDED_BY_DwordSize))();
var temp_buffer_size = temp_buffer.constructor.size; // obeys length of .array
console.info('temp_buffer.constructor.size:', temp_buffer.constructor.size); // will be Math.ceil(dummyForSize_DIVIDED_BY_DwordSize)
var bytes_returned = ostypes.TYPE.DWORD();
var changes_to_watch = ostypes.CONST.FILE_NOTIFY_CHANGE_LAST_WRITE | ostypes.CONST.FILE_NOTIFY_CHANGE_FILE_NAME | ostypes.CONST.FILE_NOTIFY_CHANGE_DIR_NAME; //ostypes.TYPE.DWORD(ostypes.CONST.FILE_NOTIFY_CHANGE_LAST_WRITE | ostypes.CONST.FILE_NOTIFY_CHANGE_FILE_NAME | ostypes.CONST.FILE_NOTIFY_CHANGE_DIR_NAME);
console.error('start hang');
var rez_RDC = ostypes.API('ReadDirectoryChanges')(hDirectory, temp_buffer.address(), temp_buffer_size, true, changes_to_watch, bytes_returned.address(), null, null);
var cntNotfications = 0;
var cOffset = 0;
while (cOffset < bytes_returned) {
cntNotfications++;
var cNotif = ctypes.cast(temp_buffer.addressOfElement(cOffset), ostypes.TYPE.FILE_NOTIFY_INFORMATION.ptr).contents; // cannot use `temp_buffer[cOffset]` here as this is equivlaent of `temp_buffer.addressOfElement(cOffset).contents` and cast needs a ptr
console.info('cNotif:', cNotif.toString());
cOffset += cNotif.NextEntryOffset; // same as doing cNotif.getAddressOfField('NextEntryoffset').contents // also note that .contents getter makes it get a primaive value so DWORD defined as ctypes.unsigned_long will not be returned as expected ctypes.UInt64 it will be primative (due to the .contents getter), so no need to do the typical stuff with a `var blah = ctypes.unsigned_long(10); var number = blah.value.toString();`
}
console.info('total notifications:', cntNotifications);
I'm working on getting the async version working but having a tricky time.
回答2:
Here's what I learned as I'm working on doing the same now, still in progress
- You have to create a buffer of DWORD so
var buf = ctypes.ArrayType(DWORD, BUFSIZE)
as it needs to be aligned on DWORD boundary, whatever this means - I don't know what
BUFSIZE
should be exactly but i have seen 2048 and 4096, I don't know why. I have also seen BUFSIZE of 1024*64, no idea why - Then after succesfully running
ReadDirectoryChangesW
cast this buffer toFILE_NOTIFY_INFORMATION
and then read its contents - Pass
null
to the final 2 arguments only if you don't want async, we want async so we are going to use theLPOVERLAPPED
struct and pass it there.
Edit
Here's solution for sync: This successfully reads one event. If have more you have to move over in temp_buff by next_entry_offset and cast, see here. Install that addon and make a new folder on your desktop or something, and it will log in browser console.
I'm working on async version, having some trouble with that.
来源:https://stackoverflow.com/questions/23478502/how-to-use-readdirectorychangesw-in-xulrunner-js-ctypes