Нужно проверить функционирование следующего кода: Если ФС.СуществуетФайл("C:\WINDOWS\")=0 тогда Сообщить("Нет каталога"); шначе Сообщить("Есть каталог"); КонецЕсли;
Тестирование показало разлисие в результатах функции ФС.СуществуетФайл: Строка Windows Wine C: Есть Нет C:\ Нет Нет С Нет Нет COM Нет Нет COM: Нет Нет COM1 Есть Нет NUL Есть Нет PRN Есть Нет AUX Есть Нет COM9 Есть Нет C:\Program files Есть Есть C:\Program files\ Есть Нет C:\windows\ Есть Нет C:\windows Есть Есть C:\windows\notepad.exe Есть Есть C:\windows\notepad.exe\ Есть Нет AUX\ Есть Нет LPT9\ Есть Нет
тестирование функции FindFirstFileExW с параметром fSearchOp=FindSearchNameMatch обнаружило только различия в: Путь Windows Wine С: есть (в cFileName нет показывает название текущего каталога) NUL есть нет PRN есть нет AUX есть нет COM9 есть нет + остальные Дос-устройства
При тестированиии была допущена ошибка. Конфигурация Wine соответствовала Windows 98, а сравнение проводилось с Windows 2003. После нахождения этой ошибки выяснилось, что в Windows 95\98\Me пути со знаком '\' на конце не определялись. В последующих версиях Windows (NT4 SP5, 2000, XP, 2003) такие пути определяются и аналогичны пути без '\' на конце. Поскольку для нужд 1С в Wine используется режим Windows 98 -> выше указанная ситуация является корректной
Принято решение изменить поведение WINE на соответствующее W2K.
Отредактирован тестировщик для функции FindFirstFileExW. Конечное тестирование показало несоответствие определения путей: Путь В Win98 существует (1), не существует (0) {"C:", 1}, {"NUL", 1}, {"AUX", 1}, {"PRN", 1}, {"NUL: ", 1}, {"NUL:.txt", 1}, {"NUL:357jhg", 1}, {"NUL:::", 1}, {"lpt1:", 1}, {"CoM4:", 1}, {"lpt9:", 1}, {"con:", 1}, {"\\LPT8", 1}, {"\\NUL", 1}, {"/NUL::::.. 11,,354--++", 1}, {"\\NUL::::.. 11,,354--++", 1}, {"e:NUl", 1}, {"e:nuL::::.. 11,,354--++", 1}, {"c:nuL::::.. 11,,354--++", 1}, {"C:///nuL::::.. 11,,354--++", 1}, {"c:///prograM fIleS\\LpT8::::.. 11,,354--++", 1}, {"c:NUL", 1}, {"c:\\nul::", 1}, {"c:prn:aaa", 1}, {"c:PRN:.txt", 1}, {"c:aux:.txt...", 1}, {"c:prn:.txt:", 1}, {"c:nul:aaa", 1}, {"c:com5:", 1}, {"c:nul. . . :", 1}, {"c:nul . . :", 1}, {"c:prn ", 1}, {"c:prn.......", 1}, {"c:prn... ...", 1}, {"c:NUL .... ", 1} К сожалению не была проверена функция RtlIsDosDeviceName_U на распознавание Dos- путей и все исправления вводились исходя из выше приведенного теста. Патч для ntdll/path.c: Index: path.c =================================================================== RCS file: /home/wine/wine/dlls/ntdll/path.c,v retrieving revision 1.37 diff -u -u -r1.37 path.c --- path.c 28 Nov 2005 20:10:41 -0000 1.37 +++ path.c 11 May 2006 08:37:39 -0000 @@ -339,27 +339,25 @@ } end = dos_name + strlenW(dos_name) - 1; - if (end >= dos_name && *end == ':') end--; /* remove trailing ':' */ /* find start of file name */ for (start = end; start >= dos_name; start--) { if (IS_SEPARATOR(start[0])) break; /* check for ':' but ignore if before extension (for things like NUL:. txt) */ - if (start[0] == ':' && start[1] != '.') break; } start++; /* remove extension */ - if ((p = strchrW( start, '.' ))) + if ((p = strchrW( start, ':' ))) { end = p - 1; - if (end >= dos_name && *end == ':') end--; /* remove trailing ':' before extension */ + while (end >= dos_name && ((*end == ' ') || (*end == '.') || (*end == ':'))) end--; /* remove trailing ':', ' ', '.' before extension */ } else { - /* no extension, remove trailing spaces */ - while (end >= dos_name && *end == ' ') end--; + /* no extension, remove trailing ' ', '.' */ + while (end >= dos_name && ((*end == ' ') || (*end == '.'))) end--; } /* now we have a potential device name between start and end, check it */ ш патч для kernel/file.c: Index: file.c =================================================================== RCS file: /home/wine/wine/dlls/kernel/file.c,v retrieving revision 1.51 diff -u -u -r1.51 file.c --- file.c 3 Apr 2006 19:46:58 -0000 1.51 +++ file.c 11 May 2006 08:54:19 -0000 @@ -1546,12 +1546,14 @@ LPVOID data, FINDEX_SEARCH_OPS search_op, LPVOID filter, DWORD flags) { - WCHAR *mask, *p; + WCHAR *mask, *p, *tmp; FIND_FIRST_INFO *info = NULL; UNICODE_STRING nt_name; OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK io; NTSTATUS status; + ULONG dos_device; + DWORD sz; TRACE("%s %d %p %d %p %lx\n", debugstr_w(filename), level, data, search_op, filter, flags); @@ -1573,38 +1575,136 @@ return INVALID_HANDLE_VALUE; } - if (!mask || !*mask) + if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info)))) { - SetLastError( ERROR_FILE_NOT_FOUND ); - goto error; + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + RtlFreeUnicodeString( &nt_name ); + return INVALID_HANDLE_VALUE; } - if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info)))) + if(RtlDetermineDosPathNameType_U( filename ) == RELATIVE_DRIVE_PATH ) { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - goto error; + if (!(tmp = RtlAllocateHeap(GetProcessHeap(), 0, MAX_PATH*sizeof(WCHAR )))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto error; + } + if(!(sz = GetCurrentDirectoryW(MAX_PATH, tmp))) + { + RtlFreeHeap( GetProcessHeap(), 0, tmp ); + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto error; + } + /* for current DRIVE or NOT */ + if( nt_name.Buffer[4] == tmp[0]) + { + tmp[sz] = '\\'; + sz++; + memcpy((void *)(tmp + sz), (void *)(filename + 2), (strlenW(filename)-1)*sizeof(WCHAR) ); + } + else + { + memcpy((void *)tmp, (void *)filename, 2*sizeof(WCHAR) ); + tmp[2] = '\\'; + memcpy((void *)(tmp + 3), (void *)(filename + 2), (strlenW(filename)-1)*sizeof(WCHAR) ); + } + dos_device = RtlIsDosDeviceName_U( tmp ); + } + else + { + if (!(tmp = RtlAllocateHeap(GetProcessHeap(), 0, (strlenW(filename) + 1)*sizeof(WCHAR)))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto error; + } + memcpy(tmp, filename, (strlenW(filename) + 1)*sizeof(WCHAR) ); } - if (!RtlCreateUnicodeString( &info->mask, mask )) + + if ((!mask && !dos_device) || (mask && !*mask)) { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + SetLastError( ERROR_FILE_NOT_FOUND ); goto error; } - /* truncate dir name before mask */ - *mask = 0; - nt_name.Length = (mask - nt_name.Buffer) * sizeof(WCHAR); - - /* check if path is the root of the drive */ - info->is_root = FALSE; - p = nt_name.Buffer + 4; /* skip \ш\ prefix */ - if (p[0] && p[1] == ':') - { - p += 2; - while (*p == '\\') p++; - info->is_root = (*p == 0); + /*If there is a DOS device*/ + if(!mask && dos_device) + { + WIN32_FIND_DATAW *d = data; + + /*LOWORD(dos_device) - quantity of symbols in the DOS device name (NUL, LPT1,...) + filename + HIWORD(dos_device) - points to the Dos device name's beginning */ + /*Initialize info.mask with NULL*/ + if (!( RtlInitUnicodeStringEx( &info->mask, NULL ) == STATUS_SUCCESS) ) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto error; + } + /* Free old nt_name */ + RtlFreeUnicodeString( &nt_name ); + + info->is_root = FALSE; + /*Initialize nt_name*/ + if(HIWORD(dos_device)) + { + nt_name.Length = 4*sizeof(WCHAR) + HIWORD(dos_device); + nt_name.MaximumLength = nt_name.Length + sizeof(WCHAR); + } + /* for "PRN", "NUL" & etc. */ + else + { + nt_name.Length = 5*sizeof(WCHAR); + nt_name.MaximumLength = nt_name.Length + sizeof(WCHAR); + } + + if (!(nt_name.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, nt_name. MaximumLength))) + { + RtlFreeUnicodeString( &info->mask ); + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + HeapFree( GetProcessHeap(), 0, info ); + return INVALID_HANDLE_VALUE; + } + /*convert to NT' style path*/ + nt_name.Buffer[0] = '\\'; + nt_name.Buffer[1] = nt_name.Buffer[2] = '?'; + nt_name.Buffer[3] = '\\'; + memcpy( (void *)(nt_name.Buffer + 4), tmp, HIWORD(dos_device)); + nt_name.Buffer[nt_name.Length/sizeof(WCHAR)] = 0; + /* for "\NUL" & for "PRN", "NUL" & etc. */ + if(!HIWORD(dos_device) || nt_name.Buffer[4] == '\\') + { + nt_name.Buffer[4] = '/'; + }; + + memset(d, 0, sizeof(*d)); + d->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE; + memcpy( d->cFileName, tmp + HIWORD(dos_device)/sizeof(WCHAR), LOWORD(dos_device)); + + RtlFreeHeap( GetProcessHeap(), 0, tmp ); } + else + { + if (!RtlCreateUnicodeString( &info->mask, mask )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto error; + } + /* truncate dir name before mask */ + *mask = 0; + nt_name.Length = (mask - nt_name.Buffer) * sizeof(WCHAR); + + /* check if path is the root of the drive */ + info->is_root = FALSE; + p = nt_name.Buffer + 4; /* skip \ш\ prefix */ + if (p[0] && p[1] == ':') + { + p += 2; + while (*p == '\\') p++; + info->is_root = (*p == 0); + } + } + /*Initialize attr*/ attr.Length = sizeof(attr); attr.RootDirectory = 0; attr.Attributes = OBJ_CASE_INSENSITIVE; @@ -1612,6 +1712,7 @@ attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; + /* check if nt_name is existing directory */ status = NtOpenFile( &info->handle, GENERIC_READ, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); @@ -1630,12 +1731,12 @@ info->data_len = 0; info->search_op = search_op; - if (!FindNextFileW( (HANDLE)info, data )) + if (!dos_device && !FindNextFileW( (HANDLE)info, data )) { TRACE( "%s not found\n", debugstr_w(filename) ); FindClose( (HANDLE)info ); SetLastError( ERROR_FILE_NOT_FOUND ); - return INVALID_HANDLE_VALUE; + goto error; } return (HANDLE)info; Но поскольку не была оттестирована функция RtlIsDosDeviceName_U, принимать эти исправления в счет нельзя.
1. Так и нет и намёка на исправление проблемы с C:\WINDOWS\ 2. Тест не приложен к баге (через Создать приложение - не надо вписывать патчи в комментарии)
Проблема в том, что 1С на NT системе убирает слэш в конце строки, если он есть, а в Win9X - не убирает. Вывод - указание \ в конце пути неверно и не должно использоваться. Выставить версию WINE в Win2000 не представляется возможным из-за баги #129.
К сборке добавлен патч, убирающий последний '\' при вызове FindFirstFileExA(). Пока что убирает для всех версий windows, и это неправильно, потому что в NT-системах файл с именем "c:\windows\" не будет найден.
Добавил проверку версии Windows. Теперь при вызове в режиме WinXP функциональность идентична windows. Осталось проверить для win98.
Тест запущенный на windows 98 показал Target file is c:\windows. The first file found is windows Target file is c:\windows\. Invalid File Handle
Тест показывает, что и в Windows 98 "c:\windows\" не понимается. Если нужно, можно оставить хак, добавляющий эту "лишнюю" функциональность. Только нужно ли?
Created attachment 176 [details] 1C обработка Эта обработка просто проверяет существует или нет указанный каталог. Проверяемый каталог возможно указать при работе с обработкой.
Бага в непонятном состоянии. Добавление / приводит к зависанию установщика конфигурации 1С 8.1 на FindFirstFile("C:\WINDOWS\TEMP") - оно бесконечно вызывается, с FindNextFile...
Решено проверкой на etersoft_1version() Теперь преобразование только для 1С 7.7
К 1.0.6-eter11 патч восстановлен: Created commit 15d89e9: remove last slash from the path (fix eterbug#106)
Начиная с 1.0.11 включили режим Windows XP для 1С 7.7 по умолчанию.