2011-09-28 17 views
12

Estoy tratando de acceder a algunas funciones en un dll (nss3.dll) que se envía con el navegador web Firefox. Para manejar esta tarea he usado ctypes en Python. El problema es que falla en el punto inicial que es cuando se carga el dll en la memoria.Python | accediendo a dll usando ctypes

Este es el fragmento de código que tengo que hacer.

>>> from ctypes import * 
>>> windll.LoadLibrary("E:\\nss3.dll") 

La excepción es que estoy recibiendo

Traceback (most recent call last): 
    File "<pyshell#2>", line 1, in <module> 
    windll.LoadLibrary("E:\\nss3.dll") 
    File "C:\Python26\lib\ctypes\__init__.py", line 431, in LoadLibrary 
    return self._dlltype(name) 
    File "C:\Python26\lib\ctypes\__init__.py", line 353, in __init__ 
    self._handle = _dlopen(self._name, mode) 
WindowsError: [Error 126] The specified module could not be found 

También probé cargarlo desde la ruta de instalación de Firefox asumiendo que hay dependencias tal vez.

>>> windll.LoadLibrary("F:\\Softwares\\Mozilla Firefox\\nss3.dll") 

Pero obtengo la misma excepción mencionada anteriormente.

Gracias.

+2

¿Estás seguro de que es una DLL de Windows y no un archivo DLL de C? ¿Has probado 'cdll.LoadLibrary' de la biblioteca ctypes? –

+0

Sí, lo olvidé por completo. – Switch

Respuesta

15

nss3.dll está vinculado a las siguientes DLL, que están todas ubicadas en el directorio de Firefox: nssutil3.dll, plc4.dll, plds4.dll, nspr4.dll y mozcrt19.dll. El cargador de la biblioteca del sistema busca estos archivos en la ruta de búsqueda de DLL del proceso, que incluye el directorio de la aplicación, los directorios del sistema, el directorio actual y cada uno de los directorios enumerados en la variable de entorno PATH.

La solución más simple es cambiar el directorio actual al directorio DLL Firefox. Sin embargo, eso no es seguro para subprocesos, por lo que no confiaría en él en general. Otra opción es agregar el directorio de Firefox a la variable de entorno PATH, que es lo que sugerí en mi versión original de esta respuesta. Sin embargo, eso no es mucho mejor que modificar el directorio actual.

versiones más recientes de Windows NT 6.0 o superior (con la actualización KB2533623) permiten la ruta de búsqueda de DLL que se actualicen de forma segura para los subprocesos a través de SetDefaultDllDirectories, AddDllDirectory y RemoveDllDirectory. Pero ese enfoque sería exagerado aquí.

En este caso, por razones de simplicidad y compatibilidad con las versiones anteriores de Windows, basta con llamar al LoadLibraryEx con la bandera LOAD_WITH_ALTERED_SEARCH_PATH. Debe cargar el archivo DLL con una ruta absoluta, de lo contrario, el comportamiento no está definido. Para mayor comodidad, podemos crear la subclase ctypes.CDLL y ctypes.WinDLL para llamar al LoadLibraryEx en lugar de LoadLibrary.

import os 
import ctypes 

if os.name == 'nt': 
    from ctypes import wintypes 

    kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) 

    def check_bool(result, func, args): 
     if not result: 
      raise ctypes.WinError(ctypes.get_last_error()) 
     return args 

    kernel32.LoadLibraryExW.errcheck = check_bool 
    kernel32.LoadLibraryExW.restype = wintypes.HMODULE 
    kernel32.LoadLibraryExW.argtypes = (wintypes.LPCWSTR, 
             wintypes.HANDLE, 
             wintypes.DWORD) 

class CDLLEx(ctypes.CDLL): 
    def __init__(self, name, mode=0, handle=None, 
       use_errno=True, use_last_error=False): 
     if os.name == 'nt' and handle is None: 
      handle = kernel32.LoadLibraryExW(name, None, mode) 
     super(CDLLEx, self).__init__(name, mode, handle, 
            use_errno, use_last_error) 

class WinDLLEx(ctypes.WinDLL): 
    def __init__(self, name, mode=0, handle=None, 
       use_errno=False, use_last_error=True): 
     if os.name == 'nt' and handle is None: 
      handle = kernel32.LoadLibraryExW(name, None, mode) 
     super(WinDLLEx, self).__init__(name, mode, handle, 
             use_errno, use_last_error) 

Aquí están todas las banderas LoadLibraryEx disponibles:

DONT_RESOLVE_DLL_REFERENCES   = 0x00000001 
LOAD_LIBRARY_AS_DATAFILE   = 0x00000002 
LOAD_WITH_ALTERED_SEARCH_PATH  = 0x00000008 
LOAD_IGNORE_CODE_AUTHZ_LEVEL  = 0x00000010 # NT 6.1 
LOAD_LIBRARY_AS_IMAGE_RESOURCE  = 0x00000020 # NT 6.0 
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040 # NT 6.0 

# These cannot be combined with LOAD_WITH_ALTERED_SEARCH_PATH. 
# Install update KB2533623 for NT 6.0 & 6.1. 
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100 
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200 
LOAD_LIBRARY_SEARCH_USER_DIRS  = 0x00000400 
LOAD_LIBRARY_SEARCH_SYSTEM32  = 0x00000800 
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000 

Por ejemplo:

firefox_path = r'F:\Softwares\Mozilla Firefox' 
nss3 = CDLLEx(os.path.join(firefox_path, 'nss3.dll'), 
       LOAD_WITH_ALTERED_SEARCH_PATH) 

nss3.NSS_GetVersion.restype = c_char_p 

>>> nss3.NSS_GetVersion()     
'3.13.5.0 Basic ECC' 
9

Tenga en cuenta que los ctypes módulo funciona con extensiones C; si quiere escribir código en C++, puede hacer lo siguiente (el código C es el mismo):

Su fuente dll.c: (puede usar el código C++ con.extensión cpp sin ningún problema) pronta

#include <math.h> 

#ifdef __cplusplus 
extern "C" { 
#endif 

__declspec(dllexport) double _sin(double x) 
{ 
    return sin(x) 
} 

#ifdef __cplusplus 
} 
#endif 

de comandos con la autenticación de administrador:

Con fuente de C:

C:\Windows\system32>cl /LD "your_source_path\dll.c" /I "c:\Python33 \include" "c:\Python33\libs\python33.lib" /link/out:dll.dll 

Con C++ fuente:

C:\Windows\system32>cl /LD "your_source_path\dll.cpp" /I "c:\Python33 \include" "c:\Python33\libs\python33.lib" /link/out:dll.dll 

compilador genera un archivo DLL:

Microsoft (R) C/C++ Optimizing Compiler Version 17.00.50727.1 for x86 
Copyright (C) Microsoft Corporation. All rights reserved. 

dll.c // or dll.cpp 
Microsoft (R) Incremental Linker Version 11.00.50727.1 
Copyright (C) Microsoft Corporation. All rights reserved. 

/out:dll.dll 
/dll 
/implib:dll.lib 
/out:dll.dll 
dll.obj 
c:\Python33\libs\python33.lib 
    Creating library dll.lib and object dll.exp 

Su módulo de Python:

import ctypes 
dll = ctypes.CDLL('your_dll_path') 
dll._sin.argtypes = [ctypes.c_double] 
dll._sin.restype = ctypes.c_double 
print(dll._sin(34)) 


# return 0.5290826861200238 
+2

He tenido problemas para conseguir comunicaciones básicas entre C++ y python durante un total combinado de 12 horas aproximadamente en un lapso de 2 días. GRACIAS por esta respuesta, ya que finalmente era lo que estaba buscando. –

+1

@ ZoranPavlovic, espero que hayas aprendido a no vincular python33.lib cuando tu código no lo use en absoluto. Que su código eventualmente se cargue en un proceso de Python no requiere ningún enlace en tiempo de compilación a la DLL de Python. Eso sería extraño y casi completamente derrota el punto de crear una DLL. – eryksun

+0

Intentó y no funciona. Creé el dll de 2 formas usando dev-C++ y Visual Studio con el código exacto que me proporcionó y no funciona. Me sale un error: no se pudo encontrar el módulo especificado. El error ocurrió en la línea de carga en Python. – Brana

5

que acaba de tener un problema similar con ctypes.CDLL, y yo tengo que trabajar cambiar el directorio actual al directorio de la biblioteca y cargando la biblioteca solo por nombre (supongo que poner el directorio en la ruta del sistema también funcionaría) Por lo tanto, en lugar de

CDLL('C:/library/path/library.dll') 

lo hice

os.chdir('C:/library/path') 
CDLL('library') 
+2

Cambiar el directorio actual es correcto si está en un proceso de subproceso único o si solo está cargando las DLL al inicio del programa. En general, cambiar el directorio de trabajo no es seguro para subprocesos. Para un Windows Vista + actualizado (es decir, KB2533623 instalado), puede llamar a ['AddDllDirectory'] (https://msdn.microsoft.com/en-us/library/hh310513) y [' SetDefaultDllDirectories'] (https: // msdn.microsoft.com/en-us/library/hh310515). Si 'kernel32.AddDllDirectory' no existe, vuelva a extender' 'PATH''. – eryksun

Cuestiones relacionadas