2010-03-19 15 views
7

La mayor parte de mi trabajo diario de programación en Windows se realiza actualmente en torno a operaciones de E/S de todo tipo (tuberías, consolas, archivos, enchufes, ...). Soy muy consciente de los diferentes métodos de lectura y escritura desde y hacia diferentes tipos de manejadores (sincrónico, asíncrono esperando compleción en eventos, esperando en HANDLEs de archivo, puertos de E/S de finalización y E/S alertable). Usamos muchos de esos.¿Es posible cambiar el HANDLE que se ha abierto para que las E/S síncronas se abran para la E/S asíncrona durante su vida útil?

Para algunas de nuestras aplicaciones, sería muy útil tener una sola forma de tratar todas las asas. Quiero decir, el programa puede no saber qué tipo de identificador ha recibido y nos gustaría utilizar, digamos, puertos de terminación de E/S para todos.

Así que primero le pediría:

Supongamos que tengo un mango:

HANDLE h; 

que ha sido recibida por mi proceso de E/S de alguna parte. ¿Hay alguna manera fácil y confiable de averiguar con qué banderas se ha creado? La bandera principal en cuestión es FILE_FLAG_OVERLAPPED.

La única forma que conozco hasta ahora es tratar de registrar dicho identificador en el puerto de E/S de finalización (usando CreateIoCompletionPort()). Si eso sucede, el identificador se ha creado con FILE_FLAG_OVERLAPPED. Pero solo debe usarse el puerto de finalización de E/S, ya que no se puede anular el registro del identificador sin cerrar el HANDLEh.

siempre que exista una manera fácil de determinar la presencia de FILE_FLAG_OVERLAPPED, no habría llegado a mi segunda pregunta:

¿Hay alguna manera cómo agregar como bandera para identificador ya existente? Eso haría que un asa que se haya abierto originalmente para operaciones sincrónicas esté abierta para asíncrona. ¿Habría una manera de cómo crear opuesto (eliminar el FILE_FLAG_OVERLAPPED para crear el identificador síncrono de asincrónico)?

No he encontrado ninguna manera directa después de leer a través de MSDN y buscar en Google mucho. ¿Habría al menos algún truco que podría hacer lo mismo? Al igual que volver a crear el mango de la misma manera utilizando la función CreateFile() o algo similar? Algo incluso parcialmente documentado o no documentado en absoluto?

El lugar principal donde necesitaría esto, es determinar la forma en que (o cambiar el camino) el proceso debe leer/escribir desde identificadores enviados por aplicaciones de terceros. No podemos controlar cómo los productos de terceros crean sus identificadores.

Estimados gurús de Windows: ayuda, por favor!

Con respecto

Martin

+0

Tenga en cuenta que el truco 'CreateIoCompletionPort()' es en realidad bastante ordenado suponiendo que realmente desea utilizar el identificador con ese puerto específico. ¡No había pensado en eso! –

Respuesta

2

3 años pasaron y Windows 8 ha sido puesto en libertad. Gracias a la regresión introducida en la implementación de la consola en Windows 8, tuve que hacer algo sobre el problema que desencadenó esta pregunta. Así que finalmente he intentado utilizar la llamada a la función ReOpenFile().

En una frase: para mis propósitos es inútil.

La API ReOpenFile() se utiliza para "tomar un identificador de archivo existente y obtener otro manejador que tenga un conjunto diferente de derechos de acceso". Al menos eso se establece en el original article.

He intentado utilizar el ReOpenFile() en el mango de entrada de la consola:

stdin_in = GetStdHandle(STD_INPUT_HANDLE); 
    stdin_in_operlapped = ReOpenFile(stdin_in, GENERIC_READ | GENERIC_WRITE, 
            FILE_SHARE_READ, FILE_FLAG_OVERLAPPED); 
    if (stdin_in_operlapped == INVALID_HANDLE_VALUE) 
    { 
     my_debug("failed to ReOpen stdin handle with OVERLAPPED flag: %d", GetLastError()); 
     exit(1); 
    } 

Y lo que consigo es: Error 1168: “Elemento no encontrado”. "Gracias Microsoft". Ni siquiera intentaré usarlo para tuberías anónimas, ya que la documentación dice:

"Las operaciones de lectura y escritura asincrónicas (superpuestas) no son compatibles con tuberías anónimas. Esto significa que no puede usar las funciones ReadFileEx y WriteFileEx con pipes anónimos. Además, el parámetro lpOverlapped de ReadFile y WriteFile se ignora cuando estas funciones se utilizan con tuberías anónimas. "

Gracias, gente, todo por sus sugerencias. Cuando se lee asincrónicamente del asa, también se tiene que estar preparado para el hecho de que la operación se complete sincrónicamente. Eso lo sé. La razón principal por la que formulé esta pregunta es:

cuando se ha emitido una lectura síncrona en algunos objetos (al menos, tuberías anónimas y entrada de consola en Windows 8) y luego se llama a CloseHandle() desde otro hilo en el mismo identificador fallará o se bloqueará, hasta que se complete el archivo de lectura (ReadFile); eso significa que colgará indefinidamente en muchos casos. Esa es la razón por la que quería reemplazar el asa sincrónica con asíncrona.

Ahora es claro para mí que simplemente no es posible en los sistemas operativos Windows cancelar algunas operaciones de lectura de manera directa. Al leer desde identificadores síncronos, uno solo tiene que salir de la aplicación incluso si ReadFile() todavía está leyendo desde un manejador de algún hilo, porque simplemente es imposible desencadenar de manera confiable una operación de lectura de este tipo. En saber ... En un sistema operativo más nuevo, es posible cancelar esa operación. Sin embargo, no hay forma de saber si el hilo ya está en la llamada a ReadFile(), o no. Si ReadFile() aún no se ha llamado, no hay ninguna operación para cancelar y la lectura subsiguiente se bloqueará. La única forma sería cerrar el controlador, pero esa operación se bloquea o falla en algunos objetos y en algunos sistemas operativos. La única solución adecuada para esto es la E/S asíncrona.Pero, como mencioné al principio, nuestra aplicación se inicia con aplicaciones de terceros y no podemos obligarlos a todos a crear siempre canalizaciones con nombre con el marcador superpuesto establecido para el stdio.

estoy darse por vencido y va a aplicar cortes feos desagradables ... tendremos que seguir leyendo sin estructura OVERLAPPED de mangos que se han creado con la bandera OVERLAPPED, y fugas de asas y los hilos ....

+0

Mi consejo sería: ¡nunca use tuberías anónimas! La función API de Win32 tiene un comportamiento esencialmente idéntico a la creación de un detector de canalización con un nombre aleatorio ('CreateNamedPipe') seguido de' ConnectNamedPipe' y 'CreateFile' (por seguridad, debe pasar un nonce sobre el conducto para confirmar que otro proceso no conectar en). El uso de su propio contenedor 'CreatePipe2' le da el control que necesita sobre si las tuberías están superpuestas o no. Y, si estoy leyendo los documentos correctamente, los identificadores de tubería resultantes _ pueden_ reabrirse con 'ReOpenFile' para cambiar el modo superpuesto. –

+0

Esto falló porque está utilizando un controlador CONSOLA, no un identificador de archivo. – Demi

+0

@NicholasWilson Esto se debe a que las tuberías * anónimas se implementan como tuberías con nombre debajo del capó. – Demi

1

banderas mango de prueba probablemente debería hacerse del mismo modo que las pruebas de los permisos del mango fue creado con. Pruébalo. Si la API falla, prueba la alternativa. Si eso falla, devuelve un error.

Creo que lo realmente revelador es la forma en que la documentación de ReadFile dice "Si hFile se abre con FILE_FLAG_OVERLAPPED, ... la función puede informar incorrectamente que la operación de lectura se ha completado."

Mi interpretación del error es (y la pregunta que debe hacerse) es: si fue posible verificar el estado superpuesto de un manejador de archivo, ¿por qué ReadFile no haría esa comprobación y luego validaría el OVERLAPPED? estructura en consecuencia, para fallar explícitamente si se llama de una manera no superpuesta con un controlador superpuesto?

+0

Estoy haciendo esa pregunta y muchas similares. desafortunadamente, no hay respuestas directas proporcionadas por Microsoft. Esa es la razón por la que estoy preguntando aquí. No he encontrado ninguna otra forma de probar la presencia de la bandera OVERLAPPED, luego la descrita anteriormente. –

4

Veo que era un mal lector de MSDN:/Eché de menos la función ReOpenFile() que se ha introducido probablemente ya en junio de 2003 en Windows Server 2003 (según this article). Para defenderme al menos un poco: esperaría que la descripción CreateFile() hiciera una referencia a la descripción ReOpenFile(). Hay una referencia en ReOpenFile() página a CreateFile() página, pero no al revés.

Esta función parece habilitar precisamente lo que necesito: agregar o quitar FILE_FLAG_OVELRAPPED a/desde identificadores ya existentes mediante la creación de un nuevo identificador con las propiedades deseadas. :-D No lo he probado todavía, sin embargo. Desafortunadamente, solo está disponible en Windows 2003 Server, Windows Vista y otros. La pregunta sobre las versiones anteriores del sistema operativo se ha respondido here. La función no existe en la API pública en el sistema operativo anterior a Windows 2003 Server. Es utilizado por la implementación subyacente, pero no está disponible para los desarrolladores en esos sistemas (No compatible).

Eso prácticamente significa que no hay ninguna esperanza para mí, al menos en los próximos años, hasta que dejemos de apoyar a las plataformas anteriores de Windows. También significa que la situación con respecto a la E/S ha sido REALMENTE mala en el sistema operativo anterior a Windows Vista. Otra parte dolorosa que se ha perdido por completo fue la posibilidad de cancelar la E/S síncrona y asíncrona en esos sistemas más antiguos.

Además, todavía echo de menos una parte de la respuesta: ¿se puede probar la presencia de banderas por cualquier medio? No he encontrado la función para hacer eso. Eso significa que si queremos garantizar la presencia de algún indicador en el objeto de archivo, entonces el archivo siempre debe ser reabierto.

+0

De hecho, si busco en mi copia local de MSDN, veo que la función no se menciona (hace referencia) absolutamente en ninguna parte. Solo aparece en la lista de las funciones de administración de archivos. Además, Google ofrece sorprendentemente pocos éxitos. Plantea algunas dudas en su fiabilidad. Estoy ansioso por hacer algunas primeras pruebas. Me gustaría ver el identificador de la consola con la bandera OVERLAPPED incluida, por ejemplo. –

+0

"¿se puede probar la presencia de banderas por cualquier medio?" buena pregunta, ¿alguna respuesta? – elmarco

+1

Nota para los lectores: 'ReOpenFile()' funciona perfectamente para los archivos ya que puede reabrir una secuencia de archivos y agregar soporte asincrónico de E/S al nuevo manejador, pero eso no funciona en las tuberías anónimas (siempre devuelve 'ERROR_PIPE_BUSY'). –

2

Si entiendo lo que está buscando, me gustaría sugerir que no importa si se abrió con la bandera superpuesta o no. Creo que puede pasar con seguridad en una estructura OVERLAPPED en los casos síncrono y asíncrono. Su código necesita ser capaz de manejar ReadFile() devolviendo false y GetLastError() devolviendo ERROR_IO_PENDING. También tendrá que añadir las llamadas apropiadas a GetOverlappedResult(), WaitForSingleObject(), etc.

El artículo de MSDN sobre ReadFile() tiene buena información al respecto en "Consideraciones para trabajar con los identificadores de archivo sincrónicos", y "Consideraciones para trabajar con archivos asincrónica maneja "en la sección" Sincronización y posición del archivo ".

+0

"Creo", hmm ... ¿puedes confirmarlo? :) – elmarco

1

no sé una manera de determinar la bandera de un mango y los efectos secundarios del uso de ReOpen api, pero dado que su objetivo era

sería muy útil tener una sola forma de tratar a todos los mangos

Si desea el comportamiento síncrono (me refiero a usar API síncrono para no solapada asas y API asíncrona alimentados con estructura OVERLAPPED con la posterior espera en caso de solapado) siempre se puede utilizar la API asíncrona también si el mango fue abierto en modo no solapado como ya se ha dicho por @Brett
puedo confirmar que esto funciona (al menos para canalizaciones con nombre) es:

void connectSynchronous(HANDLE hPipeThatWeDontKnowItsFlag){ 
    ... 
    BOOL bRet = ::ConnectNamedPipe(hPipeThatWeDontKnowItsFlag, pOverlapped); 

    if(bRet == FALSE){ 
     DWORD dwLastErr = ::GetLastError(); 

     if(dwLastErr == ERROR_IO_PENDING){ 
      //The handle was opened for asynchronous IO so we have to wait for the operation 
      ...waitFor on the overlapped hEvent; 

     }else if(dwLastErr == ERROR_PIPE_CONNECTED){ 
      //The handle was opened for synchronous IO and the client was already connected before this call: that's OK! 
      return; 
     }else{ 
      throw Error(dwLastErr); 
     } 
    }/*else{ 
     //The handle was opened for synchronous IO and the client has connected: all OK 
    }*/ 
} 
0

Una alternativa al hack CreateIoCompletionPort es hacer un ReadFile de cero bytes con un NULL lpOverlapped. Si falla con ERROR_INVALID_PARAMETER suponga que se abrió con FILE_FLAG_OVERLAPPED.

Cuestiones relacionadas