如何在XULRunner中使用ReadDirectoryChangesW(js-ctypes)

时间:2014-05-05 17:44:52

标签: javascript firefox firefox-addon xulrunner jsctypes

我试图实现answer to this question异步监视Windows文件系统。我在ChomeWorker中使用js ctypes作为XULRunner应用程序的一部分,但我认为如果我实现为Firefox附加组件,这将是相同的。

作为任务的一部分,我试图按如下方式声明函数ReadDirectoryChangesW(基于我对js ctypes和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
);

另外(这里没有特色),我已声明FindFirstChangeNotification()WaitForSingleObject()的函数映射似乎工作正常。

我遇到的问题是,当文件系统事件发生时,我不知道我应该传递给lpBuffer参数,或者如何解释结果。

所有C ++示例似乎都使用DWORD数组,然后转出结果。我的尝试如下:

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)

这似乎每次都会让XULRunner崩溃。

任何人都可以建议我应该传递lpBuffer参数和/或如何从ReadDirectoryChangesW()获取结果吗?我在网上找到的所有内容都是C ++示例,它们并没有太多帮助。感谢。

2 个答案:

答案 0 :(得分:1)

这是一个更清洁的解决方案:阅读评论,在那里学习很多。对于类型定义,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);

我正在努力让异步版本正常工作,但却有一段棘手的时间。

答案 1 :(得分:0)

这是我所学到的,因为我现在正在做同样的工作,仍在进行中

  • 你必须创建一个DWORD缓冲区var buf = ctypes.ArrayType(DWORD, BUFSIZE),因为它需要在DWORD边界上对齐,无论这意味着什么
  • 我不知道BUFSIZE应该是什么,但我看过2048和4096,我不知道为什么。我也看到BUFSIZE为1024 * 64,不知道为什么
  • 然后在成功运行ReadDirectoryChangesW后将此缓冲区强制转换为FILE_NOTIFY_INFORMATION,然后阅读其内容
  • 只有在您不想要异步时才将null传递给最后的2个参数,我们需要异步,因此我们将使用LPOVERLAPPED结构并将其传递给那里。

修改

这里的同步解决方案:这成功读取了一个事件。如果有更多,你必须通过next_entry_offset和cast see here在temp_buff中移动。安装该插件并在桌面上创建一个新文件夹,它将登录浏览器控制台。

我正在处理异步版本,但遇到了一些麻烦。