Call SHGetImageList in Go - windows

I'm trying to call SHGetImageList in Go in order to extract icons from .EXE files. The problem is that I don't know how to create/pass an "IImageList2 interface" in Go, which is something SHGetImageList requires.
I've tried miscellaneous things for a few hours, but it all results in the same E_NOINTERFACE error. Basically they're all "shots in the dark" ([]byte array to see if I could receive any "data" at all, an actual interface{} in Go containing the same functions as the IImagelist2 interface defined by MSDN, etc). If it is of any relevance, I do have a working version of this in C# using something along the lines of http://www.pinvoke.net/default.aspx/shell32.shgetimagelist, but I simply have no real clue on how to "translate" that to Go. Any help would be much appreciated.
Example Go code below, with some info and links to MSDN in the comments.
package main
import (
"fmt"
"syscall"
"unsafe"
)
var (
shell32 = syscall.MustLoadDLL("shell32.dll")
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb762179(v=vs.85).aspx
procSHGetFileInfo = shell32.MustFindProc("SHGetFileInfoW")
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx
procSHGetImageList = shell32.MustFindProc("SHGetImageList")
)
func main() {
someExeFile := `c:\windows\explorer.exe`
iconIndex := GetIconIndex(someExeFile)
// The problem:
HRESULT, _, _ := procSHGetImageList.Call(
uintptr(SHIL_JUMBO),
uintptr(unsafe.Pointer(&IID_IImageList2)),
// I don't know how pass/create an "IImageList interface" in Go,
// or if it's even possible without relying on CGO.
// IImageList interface:
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb761419(v=vs.85).aspx
// Currently there's just a pointer to an empty []byte so that the code will compile.
// HRESULT naturally contains the error code E_NOINTERFACE (2147500034),
// which makes sense seeing as I'm not passing a valid interface.
uintptr(unsafe.Pointer(&[]byte{})),
)
fmt.Println(iconIndex, HRESULT)
}
const SHIL_JUMBO = 0x4
const shGetFileInfoLen = 3
const shGetFileInfoFlags = 16400 //(SysIconIndex|LargeIcon|UseFileAttributes)
// use SHGetFileInfo to get the icon index (only value we care about)
func GetIconIndex(fileName string) int {
buf := make([]uint16, shGetFileInfoLen)
ret, _, _ := procSHGetFileInfo.Call(
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(fileName))),
0,
uintptr(unsafe.Pointer(&buf[0])),
shGetFileInfoLen,
shGetFileInfoFlags,
)
if ret != 0 && buf[2] > 0 {
return int(buf[2])
}
return 0
}
// From: "192B9D83-50FC-457B-90A0-2B82A8B5DAE1"
var IID_IImageList2 = &GUID{0x192b9d83, 0x50fc, 0x457b, [8]byte{0x90, 0xa0, 0x2b, 0x82, 0xa8, 0xb5, 0xda, 0xe1}}
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373931.aspx
type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}
UPDATE
The problem now is that it's exiting with an error(0xC0000005) when calling the Syscall to get the actual icon pointer.
package main
import (
"fmt"
"syscall"
"unsafe"
)
var (
shell32 = syscall.MustLoadDLL("shell32.dll")
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb762179(v=vs.85).aspx
procSHGetFileInfo = shell32.MustFindProc("SHGetFileInfoW")
//https://msdn.microsoft.com/en-us/library/windows/desktop/bb762185(v=vs.85).aspx
procSHGetImageList = shell32.MustFindProc("SHGetImageList")
ole32 = syscall.MustLoadDLL("ole32.dll")
procCoInitialize = ole32.MustFindProc("CoInitialize")
)
func main() {
someExeFile := `c:\windows\explorer.exe`
procCoInitialize.Call()
iconIndex := GetIconIndex(someExeFile)
var imglist *IImageList
hr, _, _ := procSHGetImageList.Call(
uintptr(SHIL_JUMBO),
uintptr(unsafe.Pointer(&IID_IImageList)),
uintptr(unsafe.Pointer(&imglist)),
)
// These look OK
fmt.Println(iconIndex, hr, imglist.Vtbl.GetIcon)
var hIcon uintptr
// GetIcon: https://msdn.microsoft.com/en-us/library/windows/desktop/bb761463(v=vs.85).aspx
hr, _, _ = syscall.Syscall(imglist.Vtbl.GetIcon,
uintptr(unsafe.Pointer(imglist)),
uintptr(iconIndex),
getIconFlags,
uintptr(unsafe.Pointer(&hIcon)),
)
// Errors: "Process finished with exit code -1073741819 (0xC0000005)"
fmt.Println("hIcon:", hIcon) // Never reaches this
}
// ILD_TRANSPARENT | ILD_IMAGE
const getIconFlags = 0x00000001 | 0x00000020
const SHIL_JUMBO = 0x4
const shGetFileInfoLen = 3
const shGetFileInfoFlags = 16400 //(SysIconIndex|LargeIcon|UseFileAttributes)
// use SHGetFileInfo to get the icon index (only value we care about)
func GetIconIndex(fileName string) int {
buf := make([]uint16, shGetFileInfoLen)
ret, _, _ := procSHGetFileInfo.Call(
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(fileName))),
0,
uintptr(unsafe.Pointer(&buf[0])),
shGetFileInfoLen,
shGetFileInfoFlags,
)
if ret != 0 && buf[2] > 0 {
return int(buf[2])
}
return 0
}
// From: "46EB5926-582E-4017-9FDF-E8998DAA0950"
var IID_IImageList = GUID{0x46eb5926, 0x582e, 0x4017, [8]byte{0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x09, 0x50}}
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373931.aspx
type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}
type IImageList struct {
Vtbl *IImageListVtbl
}
type IImageListVtbl struct {
Add uintptr
ReplaceIcon uintptr
SetOverlayImage uintptr
Replace uintptr
AddMasked uintptr
Draw uintptr
Remove uintptr
GetIcon uintptr
GetImageInfo uintptr
Copy uintptr
Merge uintptr
Clone uintptr
GetImageRect uintptr
GetIconSize uintptr
SetIconSize uintptr
GetImageCount uintptr
SetImageCount uintptr
SetBkColor uintptr
GetBkColor uintptr
BeginDrag uintptr
EndDrag uintptr
DragEnter uintptr
DragLeave uintptr
DragMove uintptr
SetDragCursorImage uintptr
DragShowNolock uintptr
GetDragImage uintptr
GetItemFlags uintptr
GetOverlayImage uintptr
}

Oh, I see the actual problem now.
uintptr(unsafe.Pointer(&IID_IImageList2)),
...
var IID_IImageList2 = &GUID{0x192b9d83, 0x50fc, 0x457b, [8]byte{0x90, 0xa0, 0x2b, 0x82, 0xa8, 0xb5, 0xda, 0xe1}}
Your IID_IImageList2 is already a pointer. In your call, you're taking a pointer to that pointer, which means the address is used as the GUID. You should either do
uintptr(unsafe.Pointer(&IID_IImageList2)),
...
var IID_IImageList2 = GUID{0x192b9d83, 0x50fc, 0x457b, [8]byte{0x90, 0xa0, 0x2b, 0x82, 0xa8, 0xb5, 0xda, 0xe1}}
or
uintptr(unsafe.Pointer(IID_IImageList2)),
...
var IID_IImageList2 = &GUID{0x192b9d83, 0x50fc, 0x457b, [8]byte{0x90, 0xa0, 0x2b, 0x82, 0xa8, 0xb5, 0xda, 0xe1}}
That way, the GUID itself is used as the GUID, not its location in memory.

Interfacing COM with Go is going to be painful.
A COM interface like IImageList2 is a list of function pointers, and you can't use those C function pointers from Go directly; you have to use syscall.Syscall() and its siblings (depending on the number of arguments the individual function takes) to call them.
At its core, a COM interface instance is a structure whose first and only field is a pointer to this list of methods. So it'd be something along the lines of
type IImageList2Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
|
type IImageList2 struct {
Vtbl *IImageList2Vtbl
}
When you pass a pointer to a variable of type IImageList2 to the Windows API function you call to create that object, be it SHGetImageList(), CoCreateInstance(), D3D11CreateDeviceAndSwapChain(), or what have you, the system will fill in the Vtbl entry with a pointer in read-only system memory that contains the list of functions.
So the first thing you have to do is make sure the methods are in the right order, so the Go structure IImageList2Vtbl and the list that Windows will give you match. Unfortunately, MSDN isn't good at this; you'll have to spelunk through the header files. Here's what I get:
type IImageList2Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
Add uintptr
ReplaceIcon uintptr
SetOverlayImage uintptr
Replace uintptr
AddMasked uintptr
Draw uintptr
Remove uintptr
GetIcon uintptr
GetImageInfo uintptr
Copy uintptr
Merge uintptr
Clone uintptr
GetImageRect uintptr
GetIconSize uintptr
SetIconSize uintptr
GetImageCount uintptr
SetImageCount uintptr
SetBkColor uintptr
GetBkColor uintptr
BeginDrag uintptr
EndDrag uintptr
DragEnter uintptr
DragLeave uintptr
DragMove uintptr
SetDragCursorImage uintptr
DragShowNolock uintptr
GetDragImage uintptr
GetItemFlags uintptr
GetOverlayImage uintptr
Resize uintptr
GetOriginalSize uintptr
SetOriginalSize uintptr
SetCallback uintptr
GetCallback uintptr
ForceImagePresent uintptr
DiscardImages uintptr
PreloadImages uintptr
GetStatistics uintptr
Initialize uintptr
Replace2 uintptr
ReplaceFromImageList uintptr
}
I'm sure I got this wrong; please report any errors. :)
This comes from commoncontrols.h. Go with the C-style, not C++-style, interface definitions (if there are more than one), as that will have all the methods, including those of interfaces that IImageList2 derives from (IUnknown, which all interfaces derive from, and IImageList).
Er wait, I lied: Windows doesn't expect you to give it the memory for the IImageList2. That's because COM interfaces are just like Go interfaces: they're a set of methods that any implementation can implement. So in reality you have to let Windows give you a pointer to an IImageList2, not an IImageList2Vtbl.
So what do we do? We store each instance as a pointer to the interface, and then pass a pointer to that to the creation functions.
So from that, we have
var imglist *IImageList2
hr, _, _ := procSHGetImageList.Call(
uintptr(SHIL_JUMBO),
uintptr(unsafe.Pointer(&IID_IImageList2)),
uintptr(unsafe.Pointer(&imglist)),
)
Notice that we pass a pointer to the IImageList2. I'll call this the instance, and the Vtbl member the vtable.
Now when you want to call a method, you have to add an extra first parameter: the instance itself. You can look on MSDN for the return types and other parameters:
// HRESULT EndDrag(void);
hr, _, _ = syscall.Syscall(instance.Vtbl.EndDrag,
uintptr(unsafe.Pointer(imglist)))
// HRESULT SetOverlayImage(int, int);
hr, _, _ = syscall.Syscall(instance.Vtbl.SetOverlayImage,
uintptr(unsafe.Pointer(imglist)),
4,
2)
Notice that we pass imglist this time, not &imglist.
Now because C and C++ don't support multiple returns like Go does, functions typically return a HRESULT (the COM equivalent of Go's error) and have you pass in pointers to the rest of the return values at the end of the argument list. For other COM interfaces, we follow the rules above. For other type, we refer to the Windows Data Types page to see what each named type represents, and remember that C int is always Go int32 on Windows, even on 64-bit systems.
// HRESULT AddMasked(HBITMAP, COLORREF, (OUT) int *);
// we'll use uintptr for HBITMAP and int32 for COLORREF
var index int32
hr, _, _ = syscall.Syscall(instance.Vtbl.AddMasked,
uintptr(unsafe.Pointer(instance)),
hbitmap,
mask,
uintptr(unsafe.Pointer(&index)))
Finally, COM requires us to call the Release() method when we're done with an object. This takes no extra parameters and its return value is irrelevant. You can stuff this in a defer if you want.
syscall.Syscall(instance.Vtbl.Release,
uintptr(unsafe.Pointer(instance)))
Note: I don't know if SHGetImageList() requires COM to be initialized. If it doesn't, ignore this part until you need some other COM interface.
Oh but that's not enough, because you also have to initialize COM. And COM can operate in a number of threading models. Only two are important: single-threaded apartment, which is used for anything related to GUI, and multithreaded apartment, which is used for special purposes.
The CoInitialize(), CoInitializeEx(), and CoUninitialize() functions all handle COM initialization and uninitialization. You already know how to call them; they're just normal DLL functions.
But beware! If you need a single-threaded apartment, you have to use runtime.LockOSThread() to ensure Go doesn't move the current goroutine to another OS thread from under you. If you don't do this, things will break in bizarre ways.
This is all a load of work, and you have to do it for every interface you're going to use. You might as well use a package that someone already made that does the heavy lifting for you. There are several packages, such as go-ole, that do the basic stuff. I don't see one that provides IImageList2, but you might be able to piggyback off an existing one. There seems to be only one reference for IImageList.
Good luck!

Related

How can I convert type uintptr to *string?

I'm trying to make a conversion from uintptr to *string that doesn't seem to be working so far. I get the value as uintptr and need to pass it on as a *string.
The value I'm working represents an address in the file directory
here's what I've tried so far:
if x is uintptr
and y is *string
y = string(x)
cannot use string(x) (value of type string) as type *string in assignment
y = *string(x)
cannot indirect string(x) (value of type string)
*y = string(x)
conversion from uintptr to string yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?)
You must use unsafe.Pointer to turn a uintptr into a Go pointer value.
Example:
import (
"fmt"
"unsafe"
)
func main() {
var s *string
var p uintptr = 1
s = (*string)(unsafe.Pointer(p))
fmt.Println(s)
}
Output:
0x1
Read the documentation of unsafe and particularly unsafe.Pointer to ensure correct usage.
Pointer represents a pointer to an arbitrary type. There are four special operations available for type Pointer that are not available for other types:
A pointer value of any type can be converted to a Pointer.
A Pointer can be converted to a pointer value of any type.
A uintptr can be converted to a Pointer.
A Pointer can be converted to a uintptr.
Pointer therefore allows a program to defeat the type system and read and write arbitrary memory. It should be used with extreme care.
The following patterns involving Pointer are valid. Code not using these patterns is likely to be invalid today or to become invalid in the future. Even the valid patterns below come with important caveats.
[ ... ]

Passing a struct containing *byte to Syscall and read its content after execution

I'm using syscall.Syscall(...) to call a C method in a dll.
This is the C method signature:
SENSEI_API HSENSEI SENSEI_open(const char* sensigrafo, const char* options, SENSEI_ERR* se);
This is the SENSEI_ERR struct:
typedef struct
{
int code;
char* error_string;
} SENSEI_ERR;
In my GO program I declared a struct:
type senseiErr struct {
code int
error_string *byte
}
And tried to call the method:
var nargs uintptr = 3
var err senseiErr
ret, _, callErr := syscall.Syscall(uintptr(senseiOpen),
nargs,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("en"))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(""))),
uintptr(unsafe.Pointer(&err)),
)
As you may have guessed, the SENSEI_open method fill the SENSEI_ERR argument with the code and the text of the error.
Now I need to read the content of that error.
err.code actually has the correct value.
About err.error_string I don't know. I'm new to GO and i have some questions:
Since the C struct has the field char* error_string, is error_string *byte in my GO struct correct?
Should I use []byte or something else?
How do I read the content of the error_string field?
fmt.Println(err.error_string) prints the memory address
fmt.Println(*err.error_string) prints always "101"
1) I doubt that cost char* meant to be UTF16 encoded. So all what you need is just getting raw data:
sensigrafo := "en\000" // \000 = 0 = null termination, \0 does not valid
options := "\000"
...
uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&sensigrafo))
uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&options))
// *(*unsafe.Pointer) are accessing the first field of string header:
type string struct {
data *byte
len int
}
// same with slices
// but for them there's less ugly way:
sensigrafo := []byte("en\000")
options := []byte("\000")
uintptr(unsafe.Pointer(&sensigrafo[0]))
uintptr(unsafe.Pointer(&options[0]))
2) C's int and Golang's int might have different sizeof, so this requires cgo declaration (C.int) or manual matching with random selection (try also int32, int64 if you don't want to use cgo)
type senseiErr struct {
code C.int /* Golang's int32/int64 */
error_string *byte // pointer types are same as C's void* or Golang's unsafe.Pointer
}
Wrong offset might cause error_string be empty or point to random addr.
3) To read content you have to use same methods as C does (read data until null terminated byte, considering that *byte points to first element of string), but I propose to use already implemented runtime functions:
//go:linkname gostringn runtime.gostringn
func gostringn(p *byte, l int) string
//go:linkname findnull runtime.findnull
//go:nosplit
func findnull(s *byte) int
...
error_string := gostringn(err.error_string, findnull(err.error_string))
// or cgo one:
type senseiErr struct {
code C.int
error_string *C.char
}
...
error_string := C.GoString(err.error_string)

Windows callback function in Golang

I wanna make push subscription to Windows Event Log in Golang
How exactly should I pass a callback function?
EVT_SUBSCRIBE_CALLBACK is the pointer of function, like
typedef DWORD ( WINAPI *EVT_SUBSCRIBE_CALLBACK)(
EVT_SUBSCRIBE_NOTIFY_ACTION Action,
PVOID UserContext,
EVT_HANDLE Event
);
So, my variant looks like this:
func logCallback() syscall.Handle {
cb := func(_ uintptr, _ uintptr, _ uintptr) uint64 {
fmt.Printf("callback called %v", data)
return 0
}
ptr := syscall.NewCallback(cb)
return syscall.Handle(ptr) // type syscall.Handle uintptr
}
I get successfully subscribed handler with no errors, but it still doesn't work.
Any ideas why? Where should I look first?
When using syscall make sure the to include import "C" at the top of your file. Glad it helped you.

Call COM object method from Go without CGo

I have created a Direct3D9 wrapper in Go which uses CGo to interface with the COM objects in C.
I would like to get rid of the dependency on a C-compiler under Windows so the user would not have to install MinGW or Cygwin to use DirectX from Go.
The problem is that d3d9.dll does not expose C-functions but uses COM. The only function that can be called directly after loading the DLL (with syscall.LoadLibrary("d3d9.dll")) is Direct3DCreate9. This returns a COM object which exposes all functionality as methods.
How can I call COM object methods in a DLL from pure Go without CGo?
I know of the Go-OLE library which states it calls COM interfaces without CGo but I cannot, from the sources, see how I would go about doing the same thing for Direct3D9. A simple example with only the relevant parts would be of great help.
I asked the guys from go-ole, like #kostix suggested.
Here is the solution:
d3d9 doesn't have generally COM vtbl. for example, it doesn't have
IDispatch interface. So you can't use go-ole for d3d9. But you can do
it with writing all interface in go.
package main
import (
"fmt"
"log"
"syscall"
"unsafe"
)
const (
D3D9_SDK_VERSION = 32
)
var (
libd3d9 = syscall.NewLazyDLL("d3d9.dll")
procDirect3DCreate9 = libd3d9.NewProc("Direct3DCreate9")
)
type IDirect3D struct {
lpVtbl *IDirect3DVtbl
}
type IDirect3DVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
RegisterSoftwareDevice uintptr
GetAdapterCount uintptr
GetAdapterIdentifier uintptr
GetAdapterModeCount uintptr
EnumAdapterModes uintptr
GetAdapterDisplayMode uintptr
CheckDeviceType uintptr
CheckDeviceFormat uintptr
CheckDeviceMultiSampleType uintptr
CheckDepthStencilMatch uintptr
CheckDeviceFormatConversion uintptr
GetDeviceCaps uintptr
GetAdapterMonitor uintptr
CreateDevice uintptr
}
func (v *IDirect3D) AddRef() int32 {
ret, _, _ := syscall.Syscall(
v.lpVtbl.AddRef,
1,
uintptr(unsafe.Pointer(v)),
0,
0)
return int32(ret)
}
func (v *IDirect3D) Release() int32 {
ret, _, _ := syscall.Syscall(
v.lpVtbl.Release,
1,
uintptr(unsafe.Pointer(v)),
0,
0)
return int32(ret)
}
func (v *IDirect3D) GetAdapterCount() uint32 {
ret, _, _ := syscall.Syscall(
v.lpVtbl.GetAdapterCount,
1,
uintptr(unsafe.Pointer(v)),
0,
0)
return uint32(ret)
}
func main() {
v, r, err := procDirect3DCreate9.Call(uintptr(D3D9_SDK_VERSION))
if r != 0 && err != nil {
log.Fatal(err)
}
d3d := *((**IDirect3D)(unsafe.Pointer(&v)))
d3d.AddRef()
defer d3d.Release()
fmt.Println(d3d.GetAdapterCount())
}
(c) mattn
(Not a comprehensive answer as I have no time to actually test this, but still…)
While MSDN most of the time assumes you work with COM objects using some platform which has built-in "glue" for them (such as Visual C++™ product or something like this), in fact it is possible to work with COM objects using plain C—look here and here for starters.
Studying those resources you can learn that calling methods on a "COM interface" amounts to properly working with its "VTBL" (Virtual function TaBLe) block which is always located in a well-known place relative to the pointer disguised by that "interface" "thing" returned by functions instantiating COM objects.
The go-ole package implements in plain Go what you'd otherwise do in plain C, so armed with the knowledge of "to call methods on a COM objects we need to operate on its VTBL" we can find the implementation of IDispatch support in that package. So I'd start there.
I'd also go right into the go-ole issue tracker asking for implementing a piece of example code which would show how to call methods on a COM object's handler acquired by means other than calling functions from go-ole package.

golang, call GetVolumeInformation winapi function

Tries to call GetVolumeInformation function from golang. Want to get volume name.
Use spec's of api:
BOOL WINAPI GetVolumeInformation(
_In_opt_ LPCTSTR lpRootPathName,
_Out_opt_ LPTSTR lpVolumeNameBuffer,
_In_ DWORD nVolumeNameSize,
_Out_opt_ LPDWORD lpVolumeSerialNumber,
_Out_opt_ LPDWORD lpMaximumComponentLength,
_Out_opt_ LPDWORD lpFileSystemFlags,
_Out_opt_ LPTSTR lpFileSystemNameBuffer,
_In_ DWORD nFileSystemNameSize
);
Use code:
// test
package main
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
var lpRootPathName = "C:\\"
var lpVolumeNameBuffer string
var nVolumeNameSize uint64
var lpVolumeSerialNumber uint64
var lpMaximumComponentLength uint64
var lpFileSystemFlags uint64
var lpFileSystemNameBuffer string
var nFileSystemNameSize uint32
kernel32, _ := syscall.LoadLibrary("kernel32.dll")
getVolume, _ := syscall.GetProcAddress(kernel32, "GetVolumeInformationW")
var nargs uintptr = 8
ret, _, callErr := syscall.Syscall9(uintptr(getVolume),
nargs,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpRootPathName))),
uintptr(unsafe.Pointer(&lpVolumeNameBuffer)),
uintptr(unsafe.Pointer(&nVolumeNameSize)),
uintptr(unsafe.Pointer(&lpVolumeSerialNumber)),
uintptr(unsafe.Pointer(&lpMaximumComponentLength)),
uintptr(unsafe.Pointer(&lpFileSystemFlags)),
uintptr(unsafe.Pointer(&lpFileSystemNameBuffer)),
uintptr(unsafe.Pointer(&nFileSystemNameSize)),
0)
fmt.Println(ret, callErr, lpVolumeNameBuffer)
}
... and finally have error :(
unexpected fault address 0xffffffffffffffff
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x456b11]
Don't understand and google cant'd help with calling winapi functions and returng string as result.
Thank's.
Package unsafe
Package unsafe contains operations that step around the type safety of
Go programs.
type Pointer
type Pointer *ArbitraryType
Pointer represents a pointer to an arbitrary type. There are four
special operations available for type Pointer that are not available
for other types.
1) A pointer value of any type can be converted to a Pointer.
2) A Pointer can be converted to a pointer value of any type.
3) A uintptr can be converted to a Pointer.
4) A Pointer can be converted to a uintptr.
Pointer therefore allows a program to defeat the type system and read
and write arbitrary memory. It should be used with extreme care.
You failed to heed the warning that unsafe.Pointer "should be used with extreme care."
Try this:
package main
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
var RootPathName = `C:\`
var VolumeNameBuffer = make([]uint16, syscall.MAX_PATH+1)
var nVolumeNameSize = uint32(len(VolumeNameBuffer))
var VolumeSerialNumber uint32
var MaximumComponentLength uint32
var FileSystemFlags uint32
var FileSystemNameBuffer = make([]uint16, 255)
var nFileSystemNameSize uint32 = syscall.MAX_PATH + 1
kernel32, _ := syscall.LoadLibrary("kernel32.dll")
getVolume, _ := syscall.GetProcAddress(kernel32, "GetVolumeInformationW")
var nargs uintptr = 8
ret, _, callErr := syscall.Syscall9(uintptr(getVolume),
nargs,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(RootPathName))),
uintptr(unsafe.Pointer(&VolumeNameBuffer[0])),
uintptr(nVolumeNameSize),
uintptr(unsafe.Pointer(&VolumeSerialNumber)),
uintptr(unsafe.Pointer(&MaximumComponentLength)),
uintptr(unsafe.Pointer(&FileSystemFlags)),
uintptr(unsafe.Pointer(&FileSystemNameBuffer[0])),
uintptr(nFileSystemNameSize),
0)
fmt.Println(ret, callErr, syscall.UTF16ToString(VolumeNameBuffer))
}
I don't know the exact problem you are having but I think it is likely because you are not using the functions in https://github.com/golang/go/blob/master/src/syscall/syscall_windows.go related to converting from the format that comes out of the kernel to what Go needs. Look at other callers to UTF16ToString, like in env_windows.go, to see how they are used.

Resources