|
| 1 | +import ctypes |
1 | 2 | import os |
2 | 3 | import platform |
3 | | -import ctypes |
4 | | -from typing import Callable, Optional, List |
| 4 | +from typing import Callable, Optional |
5 | 5 |
|
6 | 6 |
|
7 | 7 | def load_dll( |
8 | 8 | dll_prefix_name: str, |
9 | 9 | dll_load_func: Callable = ctypes.WinDLL, |
10 | | - possible_bin_paths: Optional[List[str]] = None, |
11 | 10 | hardcoded_dll_location: Optional[str] = None |
12 | 11 | ): |
13 | 12 | """ |
14 | | - Load a DLL file based on architecture and search paths, returning the loaded DLL handle. |
15 | | -
|
16 | | - This function attempts to locate and load a DLL file matching the current system's |
17 | | - architecture (x86 or x64). The DLL filename is constructed by appending the architecture |
18 | | - suffix to a given prefix name (e.g., "processInspect_x64.dll"). |
19 | | -
|
20 | | - You can specify a hardcoded absolute DLL path or provide a list of possible relative |
21 | | - distribution paths to search through. If both are provided, the function raises a ValueError. |
22 | | -
|
23 | | - Args: |
24 | | - dll_prefix_name (str): |
25 | | - The base name of the DLL without architecture or extension. |
26 | | - For example, for "processInspect_x64.dll", the prefix is "processInspect". |
27 | | -
|
28 | | - dll_load_func (Callable, optional): |
29 | | - The function used to load the DLL, typically `ctypes.WinDLL` or `ctypes.CDLL`. |
30 | | - Defaults to `ctypes.WinDLL`. |
31 | | -
|
32 | | - possible_bin_paths (Optional[List[str]], optional): |
33 | | - A list of possible relative paths where the DLL might reside. These are |
34 | | - joined with the DLL filename and searched in order. |
35 | | - Defaults to None, which triggers searching in default distribution directories. |
36 | | -
|
37 | | - hardcoded_dll_location (Optional[str], optional): |
38 | | - An absolute path to the DLL. If provided, this path is used exclusively, |
39 | | - bypassing the search logic. |
40 | | -
|
41 | | - Raises: |
42 | | - ValueError: |
43 | | - If both `hardcoded_dll_location` and `possible_dist_paths` are provided |
44 | | - simultaneously. |
45 | | -
|
46 | | - FileNotFoundError: |
47 | | - If the DLL cannot be found at the specified or searched locations. |
48 | | -
|
49 | | - Returns: |
50 | | - ctypes.WinDLL or ctypes.CDLL: |
51 | | - The loaded DLL object returned by the provided `dll_load_func`. |
52 | | -
|
53 | | - Example: |
54 | | - dll = load_dll("processInspect") |
| 13 | + Loads a Windows DLL with architecture awareness and optional custom path. |
| 14 | +
|
| 15 | + This function attempts to load a dynamic-link library (DLL) based on the provided prefix name, |
| 16 | + automatically selecting the correct architecture (x64 or x86) according to the current Python |
| 17 | + interpreter. The DLL is expected to be located in a subdirectory structure of the form: |
| 18 | + ./bin/{arch}/{dll_prefix_name}_{arch}.dll, where {arch} is either 'x64' or 'x86'. |
| 19 | +
|
| 20 | + Alternatively, a hardcoded DLL path can be provided, in which case the function will verify |
| 21 | + the existence of the file at that location before attempting to load it. |
| 22 | +
|
| 23 | + Parameters |
| 24 | + ---------- |
| 25 | + dll_prefix_name : str |
| 26 | + The prefix of the DLL filename (e.g., 'myLibrary' for 'myLibrary_x64.dll'). |
| 27 | + dll_load_func : Callable, optional |
| 28 | + The function used to load the DLL. Defaults to ctypes.WinDLL, but can be replaced with |
| 29 | + ctypes.CDLL or any compatible loader for testing or non-standard DLLs. |
| 30 | + hardcoded_dll_location : Optional[str], optional |
| 31 | + An explicit path to the DLL file. If provided and valid, this path is used instead of |
| 32 | + constructing the path from the prefix and architecture. |
| 33 | +
|
| 34 | + Returns |
| 35 | + ------- |
| 36 | + ctypes.CDLL or ctypes.WinDLL |
| 37 | + The loaded DLL object, as returned by the specified loader function. |
| 38 | +
|
| 39 | + Raises |
| 40 | + ------ |
| 41 | + FileNotFoundError |
| 42 | + If the DLL cannot be found at the constructed or provided path. |
| 43 | + OSError |
| 44 | + If the DLL fails to load due to an invalid format or missing dependencies. |
| 45 | +
|
| 46 | + Notes |
| 47 | + ----- |
| 48 | + - This function is intended for use on Windows platforms. |
| 49 | + - The architecture is determined by the running Python interpreter, not the OS alone. |
| 50 | + - The default search path assumes a project structure with DLLs in 'bin/x64' or 'bin/x86'. |
55 | 51 | """ |
56 | 52 |
|
57 | | - # Validate mutually exclusive parameters |
58 | | - if hardcoded_dll_location and possible_bin_paths: |
59 | | - raise ValueError("Cannot provide both hardcoded_dll_location and possible_dist_paths.") |
60 | | - |
61 | 53 | # Determine system architecture to pick the correct DLL version |
62 | 54 | arch = 'x64' if platform.architecture()[0] == '64bit' else 'x86' |
63 | 55 |
|
64 | 56 | # Construct DLL filename based on prefix and architecture suffix |
65 | 57 | dll_name = f'{dll_prefix_name}_{arch}.dll' |
66 | 58 |
|
67 | | - # Base directory is the directory where this script resides |
68 | | - base_dir = os.path.abspath(os.path.dirname(__file__)) |
69 | | - |
70 | | - if hardcoded_dll_location: |
71 | | - # If hardcoded path is provided, use it after verifying it exists |
| 59 | + # If hardcoded path is provided, use it after verifying it exists |
| 60 | + if hardcoded_dll_location is None: |
| 61 | + dll_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'bin', arch, dll_name)) |
| 62 | + elif os.path.isfile(hardcoded_dll_location): |
72 | 63 | dll_path = os.path.abspath(hardcoded_dll_location) |
73 | | - if not os.path.isfile(dll_path): |
74 | | - raise FileNotFoundError(f"Hardcoded DLL location does not exist: {dll_path}") |
75 | 64 | else: |
76 | | - # If no hardcoded path, define default search paths if not provided |
77 | | - if possible_bin_paths is None: |
78 | | - # Common fallback directories where DLL might be located relative to this file |
79 | | - possible_bin_paths = [ |
80 | | - os.path.join(base_dir, 'bin', arch, dll_name), |
81 | | - os.path.join(base_dir, '..', 'bin', arch, dll_name), |
82 | | - os.path.join(base_dir, '..', '..', 'bin', arch, dll_name), |
83 | | - ] |
84 | | - |
85 | | - # Convert all candidate paths to absolute paths |
86 | | - abs_paths = [os.path.abspath(p) for p in possible_bin_paths] |
87 | | - |
88 | | - # Find the first path where the DLL file actually exists |
89 | | - dll_path = next((p for p in abs_paths if os.path.isfile(p)), None) |
90 | | - |
91 | | - # If no valid DLL file was found, raise error with detailed info |
92 | | - if dll_path is None: |
93 | | - raise FileNotFoundError( |
94 | | - f"Could not find {dll_name} DLL in any of the expected locations:\n" + |
95 | | - "\n".join(abs_paths) |
96 | | - ) |
97 | | - |
98 | | - # Inform user about which DLL path is being loaded |
99 | | - print(f"Loading {dll_path} DLL...") |
| 65 | + raise FileNotFoundError(f"Hardcoded DLL location does not exist: {hardcoded_dll_location}") |
100 | 66 |
|
101 | 67 | # Load and return the DLL using the specified loading function |
102 | 68 | return dll_load_func(dll_path) |
0 commit comments