@@ -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,126 @@ 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
+ } else {
114
+ return & universalNameRetrievalError {errorType : errorExtendedError ,
115
+ message : `a network-specific error occurred; Network provider "` + providerName + `" reports: ` + errorMessage }
116
+ }
117
+ }
118
+ if returnCode == C .ERROR_MORE_DATA {
119
+ return & universalNameRetrievalError {errorType : errorMoreData ,
120
+ message : `despite trying to query with the requested buffer size, the buffer pointed to by the lpBuffer parameter was too small` }
121
+ }
122
+ if returnCode == C .ERROR_NOT_SUPPORTED {
123
+ return & universalNameRetrievalError {errorType : errorNotSupported ,
124
+ 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)` }
125
+ }
126
+ if returnCode == C .ERROR_NO_NET_OR_BAD_PATH {
127
+ return & universalNameRetrievalError {errorType : errorNoNetOrBadPath ,
128
+ 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` }
129
+ }
130
+ if returnCode == C .ERROR_NO_NETWORK {
131
+ return & universalNameRetrievalError {errorType : errorNoNetwork ,
132
+ message : `the network is unavailable` }
133
+ }
134
+ if returnCode == C .ERROR_NOT_CONNECTED {
135
+ return & universalNameRetrievalError {errorType : errorNotConnected ,
136
+ message : `the device specified by the path is not redirected` }
137
+ }
138
+ return & universalNameRetrievalError {errorType : errorUndocumented ,
139
+ message : fmt .Sprintf (`undocumented error code %d` , returnCode )}
140
+ }
141
+
142
+ func getLastWNetError () (errorMessage , providerName string , err error ) {
143
+ var lpError C.DWORD
144
+
145
+ const errorBufferSize = 5000
146
+ const nErrorBufSize C.DWORD = errorBufferSize
147
+ lpErrorBuf := (C .LPWSTR )(C .calloc (C .size_t (errorBufferSize + 1 ), C .size_t (unsafe .Sizeof (uint16 (0 )))))
148
+ defer C .free (unsafe .Pointer (lpErrorBuf ))
149
+
150
+ const nameBufferSize = 1000
151
+ const nNameBufSize C.DWORD = nameBufferSize
152
+ lpNameBuf := (C .LPWSTR )(C .calloc (C .size_t (nameBufferSize + 1 ), C .size_t (unsafe .Sizeof (uint16 (0 )))))
153
+ defer C .free (unsafe .Pointer (lpNameBuf ))
154
+
155
+ returnCode := C .WNetGetLastErrorW (& lpError , lpErrorBuf , nErrorBufSize , lpNameBuf , nNameBufSize )
156
+ if returnCode == C .NO_ERROR {
157
+ return UTF16WinApiStringToGoString (unsafe .Pointer (lpErrorBuf )), UTF16WinApiStringToGoString (unsafe .Pointer (lpNameBuf )), nil
158
+ }
159
+ if returnCode == C .ERROR_INVALID_ADDRESS {
160
+ return "" , "" , fmt .Errorf ("could not get last WNet error: ERROR_INVALID_ADDRESS" )
161
+ }
162
+ return "" , "" , fmt .Errorf ("could not get last WNet error: undocumented extended error code %d" , returnCode )
163
+ }
164
+
165
+ func GoStringToConstantUTF16WinApiString (s string ) unsafe.Pointer {
166
+ utf16String := utf16 .Encode ([]rune (s ))
167
+ utf16StringPointer := (* uint16 )(C .calloc (C .size_t (len (utf16String )+ 1 ), C .size_t (unsafe .Sizeof (uint16 (0 )))))
168
+ currentCharPointer := utf16StringPointer
169
+ for _ , c := range utf16String {
170
+ * currentCharPointer = c
171
+ currentCharPointer = (* uint16 )(unsafe .Pointer (uintptr (unsafe .Pointer (currentCharPointer )) + unsafe .Sizeof (uint16 (0 ))))
172
+ }
173
+ return unsafe .Pointer (utf16StringPointer )
174
+ }
175
+
176
+ func UTF16WinApiStringToGoString (lpwString unsafe.Pointer ) string {
177
+ ptr := (* uint16 )(lpwString )
178
+ data := make ([]uint16 , 0 , 0 )
179
+ for {
180
+ if * ptr == 0 {
181
+ break
182
+ }
183
+ data = append (data , * ptr )
184
+ ptr = (* uint16 )(unsafe .Pointer (((uintptr )(unsafe .Pointer (ptr ))) + unsafe .Sizeof (uint16 (0 ))))
185
+ }
186
+ s := utf16 .Decode (data )
187
+ return string (s )
188
+ }
0 commit comments