@@ -6,11 +6,15 @@ import (
6
6
"os/exec"
7
7
"runtime"
8
8
"strings"
9
+ "unicode/utf16"
10
+ "unsafe"
9
11
10
12
"golang.org/x/sys/windows"
11
13
)
12
14
15
+ // #cgo LDFLAGS: -lMpr
13
16
//#include <windows.h>
17
+ //#include <winnetwk.h>
14
18
import "C"
15
19
16
20
func mustDetectArchitecture () {
@@ -59,3 +63,129 @@ func isProcessRunning(p *os.Process) bool {
59
63
result := C .GetExitCodeProcess (handle , & lpExitCode )
60
64
return (result != 0 ) && (lpExitCode == C .STILL_ACTIVE )
61
65
}
66
+
67
+ func universalPathName (p string ) (string , error ) {
68
+ s , lpBufferSize , err := universalPathNameWithBufferSize (p , 1000 )
69
+ if err != nil && err .(* universalNameRetrievalError ).ErrorType () == errorMoreData {
70
+ s , _ , err = universalPathNameWithBufferSize (s , lpBufferSize )
71
+ }
72
+ if err != nil {
73
+ return p , err
74
+ }
75
+ return s , err
76
+ }
77
+
78
+ func universalPathNameWithBufferSize (p string , lpBufferSizeUse C.DWORD ) (universalPath string , lpBufferSize C.DWORD , err error ) {
79
+ cp := C .LPCWSTR (GoStringToConstantUTF16WinApiString (p ))
80
+ defer C .free (unsafe .Pointer (cp ))
81
+
82
+ // The possible data written to infoStruct (we request a UNIVERSAL_NAME_INFO below) not only consists of the struct, but also of the data (strings)
83
+ // pointed to by pointer-members within the struct. That's why this allocation needs to be much larger than just large enough to hold the struct itself.
84
+ infoStruct := C .calloc (C .size_t (lpBufferSizeUse ), 1 )
85
+ defer C .free (unsafe .Pointer (infoStruct ))
86
+
87
+ lpBufferSize = lpBufferSizeUse
88
+ errorCode := C .WNetGetUniversalNameW (cp , C .UNIVERSAL_NAME_INFO_LEVEL , C .LPVOID (infoStruct ), C .LPDWORD (unsafe .Pointer (& lpBufferSize )))
89
+ err = getErrorOfWNetGetUniversalNameW (errorCode )
90
+ if err == nil {
91
+ universalPath = UTF16WinApiStringToGoString (infoStruct )
92
+ }
93
+ return universalPath , lpBufferSize , err
94
+ }
95
+
96
+ func getErrorOfWNetGetUniversalNameW (returnCode C.DWORD ) error {
97
+ if returnCode == C .NO_ERROR {
98
+ return nil
99
+ }
100
+ if returnCode == C .ERROR_BAD_DEVICE {
101
+ return & universalNameRetrievalError {errorType : errorBadDevice ,
102
+ message : `the string pointed to by the lpLocalPath parameter is invalid` }
103
+ }
104
+ if returnCode == C .ERROR_CONNECTION_UNAVAIL {
105
+ return & universalNameRetrievalError {errorType : errorConnectionUnavailable ,
106
+ message : `there is no current connection to the remote device, but there is a remembered (persistent) connection to it` }
107
+ }
108
+ if returnCode == C .ERROR_EXTENDED_ERROR {
109
+ errorMessage , providerName , err := getLastWNetError ()
110
+ if err != nil {
111
+ return & universalNameRetrievalError {errorType : errorExtendedError ,
112
+ message : `a network-specific error occurred; getting extended error information failed: ` + err .Error ()}
113
+ }
114
+ return & universalNameRetrievalError {errorType : errorExtendedError ,
115
+ message : `a network-specific error occurred; Network provider "` + providerName + `" reports: ` + errorMessage }
116
+ }
117
+ if returnCode == C .ERROR_MORE_DATA {
118
+ return & universalNameRetrievalError {errorType : errorMoreData ,
119
+ message : `despite trying to query with the requested buffer size, the buffer pointed to by the lpBuffer parameter was too small` }
120
+ }
121
+ if returnCode == C .ERROR_NOT_SUPPORTED {
122
+ return & universalNameRetrievalError {errorType : errorNotSupported ,
123
+ message : `the dwInfoLevel parameter is set to UNIVERSAL_NAME_INFO_LEVEL, but the network provider does not support UNC names. (None of the network providers support this function)` }
124
+ }
125
+ if returnCode == C .ERROR_NO_NET_OR_BAD_PATH {
126
+ return & universalNameRetrievalError {errorType : errorNoNetOrBadPath ,
127
+ message : `none of the network providers recognize the local name as having a connection. However, the network is not available for at least one provider to whom the connection may belong` }
128
+ }
129
+ if returnCode == C .ERROR_NO_NETWORK {
130
+ return & universalNameRetrievalError {errorType : errorNoNetwork ,
131
+ message : `the network is unavailable` }
132
+ }
133
+ if returnCode == C .ERROR_NOT_CONNECTED {
134
+ return & universalNameRetrievalError {errorType : errorNotConnected ,
135
+ message : `the device specified by the path is not redirected` }
136
+ }
137
+ return & universalNameRetrievalError {errorType : errorUndocumented ,
138
+ message : fmt .Sprintf (`undocumented error code %d` , returnCode )}
139
+ }
140
+
141
+ func getLastWNetError () (errorMessage , providerName string , err error ) {
142
+ var lpError C.DWORD
143
+
144
+ const errorBufferSize = 5000
145
+ const nErrorBufSize C.DWORD = errorBufferSize
146
+ lpErrorBuf := (C .LPWSTR )(C .calloc (C .size_t (errorBufferSize + 1 ), C .size_t (unsafe .Sizeof (uint16 (0 )))))
147
+ defer C .free (unsafe .Pointer (lpErrorBuf ))
148
+
149
+ const nameBufferSize = 1000
150
+ const nNameBufSize C.DWORD = nameBufferSize
151
+ lpNameBuf := (C .LPWSTR )(C .calloc (C .size_t (nameBufferSize + 1 ), C .size_t (unsafe .Sizeof (uint16 (0 )))))
152
+ defer C .free (unsafe .Pointer (lpNameBuf ))
153
+
154
+ returnCode := C .WNetGetLastErrorW (& lpError , lpErrorBuf , nErrorBufSize , lpNameBuf , nNameBufSize )
155
+ if returnCode == C .NO_ERROR {
156
+ return UTF16WinApiStringToGoString (unsafe .Pointer (lpErrorBuf )), UTF16WinApiStringToGoString (unsafe .Pointer (lpNameBuf )), nil
157
+ }
158
+ if returnCode == C .ERROR_INVALID_ADDRESS {
159
+ return "" , "" , fmt .Errorf ("could not get last WNet error: ERROR_INVALID_ADDRESS" )
160
+ }
161
+ return "" , "" , fmt .Errorf ("could not get last WNet error: undocumented extended error code %d" , returnCode )
162
+ }
163
+
164
+ // GoStringToConstantUTF16WinApiString returns a WINAPI LPWSTR for a given string.
165
+ // The caller is responsible for freeing the returned pointer.
166
+ func GoStringToConstantUTF16WinApiString (s string ) unsafe.Pointer {
167
+ utf16String := utf16 .Encode ([]rune (s ))
168
+ utf16StringPointer := (* uint16 )(C .calloc (C .size_t (len (utf16String )+ 1 ), C .size_t (unsafe .Sizeof (uint16 (0 )))))
169
+ currentCharPointer := utf16StringPointer
170
+ for _ , c := range utf16String {
171
+ * currentCharPointer = c
172
+ currentCharPointer = (* uint16 )(unsafe .Pointer (uintptr (unsafe .Pointer (currentCharPointer )) + unsafe .Sizeof (uint16 (0 ))))
173
+ }
174
+ return unsafe .Pointer (utf16StringPointer )
175
+ }
176
+
177
+ // UTF16WinApiStringToGoString returns a string for a given WINAPI LPWSTR or LPCWSTR.
178
+ // This function does not free lpwString.
179
+ func UTF16WinApiStringToGoString (lpwString unsafe.Pointer ) string {
180
+ ptr := (* uint16 )(lpwString )
181
+ data := make ([]uint16 , 0 , 0 )
182
+ for {
183
+ if * ptr == 0 {
184
+ break
185
+ }
186
+ data = append (data , * ptr )
187
+ ptr = (* uint16 )(unsafe .Pointer (((uintptr )(unsafe .Pointer (ptr ))) + unsafe .Sizeof (uint16 (0 ))))
188
+ }
189
+ s := utf16 .Decode (data )
190
+ return string (s )
191
+ }
0 commit comments