Pasar puntero a una función a dll

Tus preguntas. Algoritmos o Grupos de Comandos formando Programas Escripts.
Responder
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Pasar puntero a una función a dll

Mensaje por Jonny »

Hola

Necesito pasar un puntero a una función, a una función de la dll de este post
:)
http://www.emesn.com/autoitforum/viewto ... f=3&t=2285

que registrará la función que le pase como callback.

En la documentación dice, que hay que pasar un puntero a la función.
¿Como se hace esto?

Imagino que pasándola por referencia ¿No?, pero no tengo muy claro como...

Imagino, que si la función de la dll registra el callback, no tendré que hacerlo yo con AutoIt...

Acias,

Salu2!
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Pasar puntero a una función a dll

Mensaje por Ximorro »

¿A una función hecha en QUÉ LENGUAJE?

Me parece que estás usando una DLL con una API pensada para linkar con C. Si estás pensando en pasar un puntero a una función de AutoIT no sólo no se puede sino que no tiene sentido, ¡la dll no sabría qué hacer con esa función! ¡no puede ejecutarla aunque tuviera el puntero a la misma!
Si alguna DLL te devuelve un puntero a función quizás puedas pasarlo a otra DLL usando un parámetro "ptr"...

Pero si tienes que programar funciones que hagan de callback a una librería en C... tendrás que pasarte a C/C++ o como mínimo a un lenguaje compilado...
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Pasar puntero a una función a dll

Mensaje por Jonny »

Hola
¿A una función hecha en QUÉ LENGUAJE?]
Escrita en AutoIt.

La función "eciRegisterCallback" de ECI.dll, necesita que se le pase la función que hará de callback:
eciRegisterCallback

Registers your callback function with an ECI instance.

Syntax

void eciRegisterCallback(
ECIHand hEngine,
ECICallback *pCallback
void* data


);


Parameters

hEngine

Handle to the speech synthesis engine instance. This is the value returned by eciNew or eciNewEx.

pCallback

Pointer to the ECICallback function. Can be NULL.

data

Pointer to an arbitrary value, or a key to the size of a void pointer. All values are allowed.

Return Values

None.

Remarks

This function registers your callback function with an ECI instance. If pCallback is NULL, the current
callback is removed. The supplied data pointer is associated with your callback. It is passed back to
your callback function on entry, so that your function can use it, if desired, for identification purposes,
such as a class pointer or an instance reference. Only one callback function can be registered at a time
with each ECI instance. ECI functions may not be called from within a callback.

Your callback must be registered with eciRegisterCallback before any function that creates messages
is called. The functions that cause messages to be sent to your callback are eciGeneratePhonemes, eciInsertIndex, and eciSetOutputBuffer, and setting eciWantPhonemeIndices to 1 with
eciSetParam.

For any given ECI instance, your callback will be called from the same thread on which your
application calls ECI. This is achieved by passing control from your application to ECI. When you call
eciSynchronize, ECI will retrieve all messages and execute your callback for each one, until synthesis
is complete. If you call eciSpeaking, ECI will retrieve just those messages that are ready, execute your
callback for each one, and then return. If you choose to use eciSpeaking, instead of eciSynchronize,
you must keep calling it until it returns false.

eciRegisterCallback may not be called while synthesis is in progress.

The syntax of your callback is as follows:

ECICallbackReturn callback(
ECIHand hEngine,
ECIMessage msg,
long lparam,
void* data


);


Callback Function Parameters

hEngine

Handle to the speech synthesis engine instance. This is the value returned by eciNew or eciNewEx.

msg

Enumeration indicating the type of message (see Data Types):

eciWaveformBuffer
eciPhonemeBuffer
eciIndexReply
eciPhonemeIndexReply


eciWordIndexReply

lparam

A long whose value and interpretation depends on the ECIMessage type. See discussion below.

data

An arbitrary value which is the size of a void pointer. You specify this value in your call to
eciRegisterCallback. All values are allowed, including pointers.

Callback Function Return Values

eciDataProcessed

You have processed the message and any associated data in your output buffer. Subsequent

messages may be sent to your callback.

eciDataNotProcessed

You could not process the message or associated data in your output buffer. The same message will

be sent to your callback later.

If your callback processes the ECIMessage, and does not wish to see that same message again, it
should return eciDataProcessed. If your callback function cannot process the message, and would like
to see the same message again, it should return eciDataNotProcessed; your callback will be called
with the same message at a later time. This is particularly useful if an eciWaveformBuffer message
cannot be processed because the buffer you are writing to is temporarily full. No new ECIMessage
will be sent if eciDataNotProcessed is returned. If your application continues to return
eciDataNotProcessed, synthesis will stop, aslo, eciDataAbort will stop sythesis and clear the text
buffer.

All callbacks should return quickly to ensure that there is no interruption of output. The value and
interpretation of lparam is dependent on ECIMessage.

ECIMessage eciWaveformBuffer

lparam indicates the number of samples (not bytes) that have just been added to your output buffer.
Your output buffer is specified in a call to eciSetOutputBuffer.

When phoneme indices are also being generated, this message is sent for the samples for each
phoneme.

Samples are 16-bit signed PCM values and are centered at 0.

Once your callback returns eciDataProcessed, the data in your output buffer is no longer protected;
therefore, your callback should only return eciDataProcessed when it has processed all the data in your
buffer. No more data will be added to your buffer until eciDataProcessed is returned.

ECIMessage eciPhonemeBuffer

The lparam parameter indicates the number of characters (bytes) that have just been added to your
phoneme buffer. Your phoneme buffer address was given to the ECI instance in your call to
eciGeneratePhonemes.

Once your callback returns eciDataProcessed, the data in your phoneme buffer is no longer protected;
therefore, your callback should only return eciDataProcessed when it has processed all the data in the
buffer. More data will not be added to your phoneme buffer until eciDataProcessed is returned.

ECIMessage eciIndexReply

lparam is an index that was reached during synthesis and playback of the input text buffer. Indices are
inserted into the input text buffer with eciInsertIndex.

Your callback should return immediately to ensure that there is no interruption of output.

Receiving index notifications is useful for synchronizing text with user-defined events, for example,
word highlighting or simultaneous display of related graphics in a slideshow-style presentation.

ECIMessage eciPhonemeIndexReply
lparam is a pointer to an ECIMouthData structure.

This message is sent only when the eciWantPhonemeIndices environment parameter is set to 1. One of
these messages is sent for each phoneme spoken, just before the phoneme starts playing on the audio
device (or just before the associated waveform audio is placed in your output buffer, if you have called
eciSetOutputBuffer).

In addition to the language-specific phoneme symbols, the symbol ¤ (0xA4) is used to indicate the end
of an utterance, and is sent with a set of neutral mouth position parameters.

Receiving phoneme notifications this way is appropriate for synchronizing facial animation or other
graphics with the speech output. If your application only needs to synchronize with individual words
or larger units, use word indices (eciIndexReply messages). To convert text to phonemes, use the
eciGeneratePhonemes function and the eciPhonemeBuffer message will be sent.

As with other messages, your callback function must return quickly. If significant processing is
required, as with complex graphics, your application should spawn a new thread, and marshal the

callback messages to the new thread. Your application is responsible for skipping messages, if it
receives them faster than it can process them.
Me parece que estás usando una DLL con una API pensada para linkar con C. Si estás pensando en pasar un puntero a una función de AutoIT no sólo no se puede sino que no tiene sentido, ¡la dll no sabría qué hacer con esa función! ¡no puede ejecutarla aunque tuviera el puntero a la misma!
Yo pensaba que sí, al menos con las apis de Windows sí se puede, y están más pensadas para usarse en C u otro lenguaje compilado que en AutoIt. Por ejemplo, las funciones de la librería Timers.au3, que hacen uso de las apis de timers de Windows...
(Claro, que quizá no funciona así exactamente).

además, la función "DllCallbackRegister"
hace lo mismo ¿no? (registrar un callback) que digo yo, que se usará normalmente en apis...
Si alguna DLL te devuelve un puntero a función quizás puedas pasarlo a otra DLL usando un parámetro "ptr"...
¿El puntero no podría obtenerse con "DllCallbackGetPtr" ¿verdad??
Porque la ayuda dice que hay que pasar "un apuntador DllCallback devuelto a partir de DllCallbackRegister".


Salu2!
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Pasar puntero a una función a dll

Mensaje por Jonny »

Hola

Pues así he hecho la función (como creo que debería hacerse).
Parece que no devuelve errores, pero no la he probado todavía para ver que funcione correctamente (que reciba las llamadas de ECI).
A ver si termino más funciones para poder hacer que ECI llame la función callback y poder ver si envía bien los parámetros etc:

Código: Seleccionar todo

Global $ECI

Func ECIRegisterCallback($HEngine, $ECIFunc)
  Local $FuncCallback, $CallbackPointer, $ECICall
 If ($HEngine="" Or $ECIFunc="") Then
  SetError(-1)
  Return -1
 EndIf
  $FuncCallback=DllCallbackRegister($ECIFunc, "int", "hwnd;int;long;ptr*")
 If $FuncCallback=0 Then
  SetError(-1)
  Return -1
 EndIf
  $CallbackPointer=DllCallbackGetPtr($FuncCallback)
 If $CallbackPointer=0 Then
  SetError(-1)
  Return -1
 EndIf
  $ECICall=DllCall("ECI.dll", "none", "eciRegisterCallback", "hwnd", $HEngine, "ptr", $CallbackPointer, "ptr", $ECIHand)
 If @Error=0 Then
  SetError(0)
  Return $ECICall[0]
   Else
    SetError(-1)
    Return -1
 EndIf
EndFunc

Func ECINew()
  Local $ECICall
 $ECICall=DllCall("ECI.dll", "hwnd", "eciNew")
 If @Error=0 Then
  SetError(0)
  Return $ECICall[0]
   Else
    SetError(-1)
    Return -1
 EndIf
EndFunc

Func _Mi_Funcion($HEngine, $Msg, $Lparam, $Data)
  Msgbox(0, "Mi función", "Callback de prueba")
EndFunc

$ECI=ECINew()
ECIRegisterCallback($ECI, "_Mi_Funcion")
Con lo que no me aclaro mucho, es con la forma de pasar los punteros a eciRegisterCallback. Me hago un poco de lío con eso de pasar por referencia o no... En principio en la documentación no pone nada de eso, así que no creo que tenga que hacerlo.

Salu2!
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Pasar puntero a una función a dll

Mensaje por Ximorro »

Pues sinceramente, no sé si el grupo DLLCallBack... sirve para registrar CallBaks genéricamente o sólo está programado para que vaya con la API de Windows. Desde luego en la ayuda de DllCallbackGetPtr dice claramente: "Returns the pointer to a callback function that can be passed to the Win32 API".

Pero es posible que con otras DLL que usen el mismo mecanismo funcione. Pues es cosa de probarlo, ¡ya nos dirás!

El otro problema que veo que te decía en el otro post... me parece que la API no sólo se usa a base de llamadas a funciones publicadas, también se puede enlazar y usar el objeto ECI directamente, y luego llamar a sus métodos... Eso en principio no se puede hacer sólo con llamadas a DLL (¡y es un fastidio!), en realidad... ¿Ese EciNEW es por casualidad un intento de implementar el constructor del objeto? porque no sé si será posible hacerlo así...
Anda, pásame los otros archivos que voy un poco a ciegas, en concreto para mirar esto vendrá bien el .h y el .lib. Y los OCX, porque...

Otra cosa es que se pueda hacer con los OCX, a partir de ellos igual sí se puede acceder al objecto ECI con ObjCreate y desde ahí a sus métodos y propiedades...
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Pasar puntero a una función a dll

Mensaje por Jonny »

Hola
El otro problema que veo que te decía en el otro post... me parece que la API no sólo se usa a base de llamadas a funciones publicadas, también se puede enlazar y usar el objeto ECI directamente, y luego llamar a sus métodos... Eso en principio no se puede hacer sólo con llamadas a DLL (¡y es un fastidio!),
Ni idea de eso. No me sonaba de nada. Quizá, pueda hacerse como dices, con el objeto ECI, pero imagino que hará lo mismo que las funciones...
¿Ese EciNEW es por casualidad un intento de implementar el constructor del objeto? porque no sé si será posible hacerlo así...
No, ECINew es la función que crea un handle para utilizar en practicamente todas las demás funciones del api.
Anda, pásame los otros archivos que voy un poco a ciegas, en concreto para mirar esto vendrá bien el .h y el .lib. Y los OCX, porque...

Otra cosa es que se pueda hacer con los OCX, a partir de ellos igual sí se puede acceder al objecto ECI con ObjCreate y desde ahí a sus métodos y propiedades...
Ummm, solo me sonaba el .h, que por cierto llevo días buscando (desde que me puse a programar las funciones en autoit para usar la dll), porque en la documentación hacen referencia muchas veces a enumeraciones que están ahí, pero nada. no doy con él por ningún sitio.
Lo tuve hace mucho (cuando posteé el primer post hablando de esta dll) allá por ¿Julio del año pasado?... creo que he visto mirándolo ahora, pero como en principio no hacía falta porque con la documentación bastaba, pues lo borré
:).

A ver si lo encuentro y lo cuelgo.

Editado: Al final lo he encontrado, y lo he adjuntado en el post.
Espero que ese sea todo el contenido del archivo. Lo he encontrado aquí:

[ul]
http://www.hackchina.com/en/cont/129399
[/ul]

donde parece que hay librerías para tratar la dll en varios lenguajes...

Salu2!
Adjuntos
ECI.rar
(3.7 KiB) Descargado 108 veces
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Pasar puntero a una función a dll

Mensaje por Jonny »

Hola
Pues sinceramente, no sé si el grupo DLLCallBack... sirve para registrar CallBaks genéricamente o sólo está programado para que vaya con la API de Windows. Desde luego en la ayuda de DllCallbackGetPtr dice claramente: "Returns the pointer to a callback function that can be passed to the Win32 API".

Pero es posible que con otras DLL que usen el mismo mecanismo funcione. Pues es cosa de probarlo, ¡ya nos dirás!
Pues parece que la función ECIRegisterCallback() que puse ayer funciona, y que se registra bien la función en la dll.

No he probado si envía bien los parámetros, porque aún no he escrito la función que ejecuta la síntesis ni otra que haga que se ejecute el callback, pero sí la función "ECISetOutputBuffer", que requiere que haya una función callback registrada y devuelve "True", que según la documentación quiere decir que todo ha ido bien y el buffer de salida se ha establecido correctamente. Si no invoco antes la función ECIRegisterCallback(), devuelve "False", que según la documentación quiere decir que no hay ningún callback registrado, por lo que entiendo que tiene que estar funcionando bien.

Ahora el problema me lo está dando ECISetOutputBuffer, que no entiendo uno de los argumentos que hay que pasarle.

Por lo que entiendo de la documentación, el segundo (que es el que me hace un poco de lío) es el tamaño del buffer que se va a establecer, pero dice que en samples...
¿Que significa eso?
Ya he leído por ahí, que samples es como un tipo de voz inglesa (así lo define la wikypedia), pero lo del tamaño en samples...

Por eso, de momento he hecho la función de forma que el usuario tenga que pasarle los parámetros que requiere la dll, para no hacerlo mal, aunque supongo que lo suyo sería que la función se encargara de tratar el buffer y el tamaño de este, que requiere la dll:

Código: Seleccionar todo

Func ECISetOutputBuffer($HEngine, $BuffSize, $BuffOutput)
		;----------
		;Descripción: Fija un buffer como destino de la síntesis.
			;-Fija un buffer de salida para recibir samples PCM de audio de 16 bits.
			;-Los samples PCM se centran en 0
			;-Indicando 0 en $BuffSize o un valor nulo ("") en $BuffOutput se redirecciona el destino de la síntesis al destino por defecto y cancela los callback del waveform del buffer.
			;-Si no hay ninguna función callback registrada, "ECISetOutputBuffer" devuelve 0.
			;-Esta función no puede ser invocada mientras la síntesis está en curso.
		;Parámetros: $HEngine: Handle de la instancia del motor de la síntesis de voz, devuelto por ECINew o ECINewEx.
			;-$BuffSize: Tamaño del buffer de destino (en samples). 0 cancela los callback del waveform del buffer.
			;-$BuffOutput: Puntero al buffer donde se recibirán samples PCM de audio. Un valor vacío cancela los callback del waveform del buffer.
		;Valor(es) de retorno: Al fallar: -1: $HEngine no es un handle de ventana válido, $BuffSize no recibió un valor esperado u ocurrió un error en la comunicación con la dll.
			;-0: No hay ninguna función callback registrada. Consultar "ECIErrorMessage" y/o "ECIProgStatus" para ver si hay información adicional de errores.
		; - Con éxito:
			;-1: Se fijó correctamente el buffer de salida.
		;Autor: Jonny.
		;----------
	Local $ECICall
			If (($HEngine="") Or (StringIsDigit($BuffSize)=0))  Then Return -1
		$ECICall=DllCall($ECIHand, "bool", "eciSetOutputBuffer", "hwnd", $HEngine, "int", $BuffSize, "ptr*", $BuffOutput)
			If @Error=0 Then
				Return $ECICall[0]
			Else
				Return -1
			EndIf
EndFunc
Seguro que la documentación podría traducirse mejor ¡sorry por los fallos que puedan haber!

Por cierto, que en la documentación, el último parámetro dice que es short* (el * supongo que se referirá a pasarlo por referencia). Yo lo he pasado como "ptr*" ya qué es un puntero¡Espero que esté bien! :).

¿Teneis idea de lo del tamaño en samples?...

Salu2!
Responder