I've been able to directly call the NetDfsGetInfo
function using Python's "ctypes" module.
Some stumbling points I had was understanding the C++/Python interface and variable marshalling - that's what the dfs.argtypes
helps with.
The C++ calls return their structures by placing pointers into a buffer you supply to the call. Using byref
you are matching the function prototype LPBYTE *Buffer
Processing the output requires defining a "Structure" that matches the function return, in this case DFS_INFO_3
. The python "buffer" variable is cast as a pointer to DFS_INFO_3
and ctypes.Structure
defines the field names and the types the struct is build from. Then you can access them via attribute name, eg, dfs_info.EntryPath
There was a pointer to a variable-length array (DFS_STORAGE_INFO
) returned too, which is able to be accessed via normal Python storage[i]
syntax.
import ctypes as ct
from ctypes import wintypes as win
dfs = ct.windll.netapi32.NetDfsGetInfo
dfs.argtypes = [
win.LPWSTR,
win.LPWSTR,
win.LPWSTR,
win.DWORD,
ct.POINTER(win.LPBYTE),
]
class DFS_STORAGE_INFO(ct.Structure):
"""Contains information about a DFS root or link target in a DFS namespace."""
_fields_ = [ # noqa: WPS120
("State", win.ULONG),
("ServerName", win.LPWSTR),
("ShareName", win.LPWSTR),
]
class DFS_INFO_3(ct.Structure): # noqa: WPS114
"""Contains information about a Distributed File System (DFS) root or link."""
_fields_ = [ # noqa: WPS120
("EntryPath", win.LPWSTR),
("Comment", win.LPWSTR),
("State", win.DWORD),
("NumberOfStorages", win.DWORD),
("Storage", ct.POINTER(DFS_STORAGE_INFO)),
]
# ----- Function call -----
buffer = win.LPBYTE() # allocate a LPBYTE type buffer to be used for return pointer
dret = dfs(r"\\something.else\here", None, None, 3, ct.byref(buffer))
# specify that buffer now points to a DFS_INFO_3 struct
dfs_info = ct.cast(buffer, ct.POINTER(DFS_INFO_3)).contents
print(dfs_info.EntryPath)
for i in range(dfs_info.NumberOfStorages):
storage = dfs_info.Storage[i]
print(
f"{storage.ServerName=}",
f"{storage.ShareName=}",
)