Skip to content

Commit 03e6b5c

Browse files
author
David Miguel Susano Pinto
committed
Use a standard search path for DLL loading in windows (#235)
Since Python 3.8, ctypes uses an altered search path when loading DLLs in Windows which only includes the application directory, the system32 directory, and directories manually specified with add_dll_directory. None of those suit us. The standard search path can be specified with `winmode=0`. This commit adds a new function to loads libraries with `winmode=0` and makes all modules that load shared libraries use it.
1 parent 6a4ee44 commit 03e6b5c

File tree

10 files changed

+78
-18
lines changed

10 files changed

+78
-18
lines changed

NEWS.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ Version 0.7.0 (upcoming)
6767

6868
* Microscope is now dependent on Python 3.7 or later.
6969

70+
* Python 3.8 changed the default DLL search path in Windows which
71+
caused all cameras, deformable mirrors, and Linkam stage to fail to
72+
load. This version restores the use of Windows standard search
73+
path.
74+
7075

7176
Version 0.6.0 (2021/01/14)
7277
--------------------------

microscope/_utils.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
## You should have received a copy of the GNU General Public License
1818
## along with Microscope. If not, see <http://www.gnu.org/licenses/>.
1919

20+
import ctypes
21+
import os
22+
import sys
2023
import threading
2124
import typing
2225

@@ -43,6 +46,36 @@
4346
)
4447

4548

49+
def library_loader(
50+
libname: str, dlltype: typing.Type[ctypes.CDLL] = ctypes.CDLL, **kwargs
51+
) -> ctypes.CDLL:
52+
"""Load shared library.
53+
54+
This exists mainly to search for DLL in Windows using a standard
55+
search path, i.e, search for dlls in ``PATH``.
56+
57+
Args:
58+
libname: file name or path of the library to be loaded as
59+
required by `dlltype`
60+
dlltype: the class of shared library to load. Typically,
61+
``CDLL`` but sometimes ``WinDLL`` in windows.
62+
kwargs: other arguments passed on to ``dlltype``.
63+
"""
64+
# Python 3.8 in Windows uses an altered search path. Their
65+
# reasons is security and I guess it would make sense if we
66+
# installed the DLLs we need ourselves but we don't. `winmode=0`
67+
# restores the use of the standard search path. See issue #235.
68+
if (
69+
os.name == "nt"
70+
and sys.version_info >= (3.8)
71+
and "winmode" not in kwargs
72+
):
73+
winmode_kwargs = {"winmode": 0}
74+
else:
75+
winmode_kwargs = {}
76+
return dlltype(libname, **winmode_kwargs, **kwargs)
77+
78+
4679
class OnlyTriggersOnceOnSoftwareMixin(microscope.abc.TriggerTargetMixin):
4780
"""Utility mixin for devices that only trigger "once" with software.
4881

microscope/_wrappers/BMC.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@
2424
import os
2525
from ctypes import c_char, c_char_p, c_double, c_int, c_uint, c_uint32
2626

27+
import microscope._utils
28+
2729

2830
if os.name == "nt": # is windows
2931
# Not actually tested yet
30-
SDK = ctypes.WinDLL("BMC2")
32+
SDK = microscope._utils.library_loader("BMC2", ctypes.WinDLL)
3133
else:
32-
SDK = ctypes.CDLL("libBMC.so.3")
34+
SDK = microscope._utils.library_loader("libBMC.so.3")
3335

3436

3537
# Definitions from BMCDefs.h

microscope/_wrappers/asdk.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@
2424
import os
2525
from ctypes import c_char_p, c_double, c_int, c_size_t, c_uint32
2626

27+
import microscope._utils
28+
2729

2830
if os.name == "nt": # is windows
2931
_libname = "ASDK"
3032
else:
3133
_libname = "libasdk.so" # Not actually tested yet
32-
33-
SDK = ctypes.CDLL(_libname)
34+
SDK = microscope._utils.library_loader(_libname)
3435

3536

3637
class DM(ctypes.Structure):

microscope/_wrappers/dcamapi4.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@
3434
import enum
3535
import os
3636

37+
import microscope._utils
38+
3739

3840
if os.name == "nt":
39-
_LIB = ctypes.WinDLL("dcamapi")
41+
_LIB = microscope._utils.library_loader("dcamapi", ctypes.WinDLL)
4042
else:
41-
_LIB = ctypes.CDLL("libdcamapi.so")
43+
_LIB = microscope._utils.library_loader("libdcamapi.so", ctypes.CDLL)
4244

4345

4446
if os.name == "nt":

microscope/_wrappers/mirao52e.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222

2323
import ctypes
2424

25+
import microscope._utils
26+
2527

2628
# Vendor only supports Windows
27-
SDK = ctypes.WinDLL("mirao52e")
29+
SDK = microscope._utils.library_loader("mirao52e", ctypes.WinDLL)
2830

2931

3032
TRUE = 1 # TRUE MroBoolean value

microscope/cameras/_SDK3.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import os
2424
from ctypes import POINTER, c_double, c_int, c_uint, c_void_p
2525

26+
import microscope._utils
27+
2628

2729
#### typedefs
2830
AT_H = ctypes.c_int
@@ -34,12 +36,20 @@
3436
_stdcall_libraries = {}
3537

3638
if os.name == "nt": # is windows
37-
_stdcall_libraries["ATCORE"] = ctypes.WinDLL("atcore")
38-
_stdcall_libraries["ATUTIL"] = ctypes.WinDLL("atutility")
39+
_stdcall_libraries["ATCORE"] = microscope._utils.library_loader(
40+
"atcore", ctypes.WinDLL
41+
)
42+
_stdcall_libraries["ATUTIL"] = microscope._utils.library_loader(
43+
"atutility", ctypes.WinDLL
44+
)
3945
CALLBACKTYPE = ctypes.WINFUNCTYPE(c_int, AT_H, POINTER(AT_WC), c_void_p)
4046
else:
41-
_stdcall_libraries["ATCORE"] = ctypes.CDLL("atcore.so")
42-
_stdcall_libraries["ATUTIL"] = ctypes.CDLL("atutility.so")
47+
_stdcall_libraries["ATCORE"] = microscope._utils.library_loader(
48+
"atcore.so"
49+
)
50+
_stdcall_libraries["ATUTIL"] = microscope._utils.library_loader(
51+
"atutility.so"
52+
)
4353
CALLBACKTYPE = ctypes.CFUNCTYPE(c_int, AT_H, POINTER(AT_WC), c_void_p)
4454

4555
#### Defines

microscope/cameras/atmcd.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
from numpy.ctypeslib import ndpointer
6565

6666
import microscope
67+
import microscope._utils
6768
import microscope.abc
6869

6970

@@ -88,9 +89,9 @@
8889
else:
8990
_dllName = "atmcd64d"
9091
if os.name == "nt": # is windows
91-
_dll = ctypes.WinDLL(_dllName)
92+
_dll = microscope._utils.library_loader(_dllName, ctypes.WinDLL)
9293
else:
93-
_dll = ctypes.CDLL(_dllName + ".so")
94+
_dll = microscope._utils.library_loader(_dllName + ".so")
9495

9596
# Andor's types
9697
at_32 = c_long

microscope/cameras/pvcam.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@
144144
import Pyro4
145145

146146
import microscope
147+
import microscope._utils
147148
import microscope.abc
148149

149150

@@ -676,11 +677,12 @@ class md_frame(ctypes.Structure):
676677

677678
if os.name == "nt": # is windows
678679
if platform.architecture()[0] == "32bit":
679-
_lib = ctypes.WinDLL("pvcam32")
680+
_lib = microscope._utils.library_loader("pvcam32", ctypes.WinDLL)
680681
else:
681-
_lib = ctypes.WinDLL("pvcam64")
682+
_lib = microscope._utils.library_loader("pvcam64", ctypes.WinDLL)
682683
else:
683-
_lib = ctypes.CDLL("pvcam.so")
684+
_lib = microscope._utils.library_loader("pvcam.so")
685+
684686

685687
### Functions ###
686688
STRING = ctypes.c_char_p

microscope/stages/linkam.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
from enum import Enum, IntEnum
4949

5050
import microscope
51+
import microscope._utils
5152
import microscope.abc
5253

5354

@@ -975,9 +976,10 @@ def get_sdk_version():
975976
def init_sdk():
976977
"""Initialise the SDK and set up event callbacks"""
977978
if os.name == "nt": # is windows
978-
__class__._lib = ctypes.CDLL("LinkamSDK.dll")
979+
_libname = "LinkamSDK.dll"
979980
else: # assuming Linux. Not tested.
980-
__class__._lib = ctypes.CDLL("libLinkamSDK.so")
981+
_libname = "libLinkamSDK.so"
982+
__class__._lib = microscope._utils.library_loader(_libname)
981983
_lib = __class__._lib
982984
"""Initialise the SDK, and create and set the callbacks."""
983985
# Omit conditional pending a fix for ctypes issues when optimisations in use.

0 commit comments

Comments
 (0)