Info: After writing this whole text, I came across some other SO post, tried it one more time and it just worked. However, as the solution (or rather the problem) to me was not obvious at all (and writing this question was a lot of work), I decided to still post this text along with the solution. Hopefully, this might help some developer coming across this post in the future.
As I'm still rather new to SO, I don't really know if this will be appreciated so feel free to let me know to remove it.
TLDR:
I was trying to call a DLL which required a string argument and returned a string. I tried several solutions and approaches that didn't help. In the end - by chance - I found out the problem was the way I passed the string.
My solution is the following:
imagePath := "*30 C:\\absolute\\path\\to\\test.png"
bytePath := []byte(imagePath + "\x00") // Make string null-terminated (required by C)
dll := syscall.NewLazyDLL("ImageSearchDLL.dll")
imageSearch := dll.NewProc("ImageSearch")
r1, _, _ := imageSearch.Call(
0,
0,
1920,
1080,
uintptr(unsafe.Pointer(&bytePath[0]))) // Make sure to pass index 0
// DLL returned char*, parse that to a GoString
var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
hdr.Data = uintptr(unsafe.Pointer(r1))
hdr.Len = 32 // Adjust length as needed!
fmt.Println(s)
The parsing of the char*
to a string and reverse could perhaps somehow be done with C.CString
and C.GoString
, I could not get that working in a short enough time so I decided to keep it this way.
What should also be done is checking the length of the string (is "terminated" by \x00).
I'm quite new to go but want to use it in my next desktop automation project. To do this, I'm trying to find an image present in the display. As I could not find any go library capable of doing this, I decided to try to use a DLL I know from my Autoit-experience years ago: DLL Download Source Code
However, after several hours of trying to find a solution (including the wiki), I could not get it working.
The library/function itself itself has this signature: char* WINAPI ImageSearch(int aLeft, int aTop, int aRight, int aBottom, char *aImageFile)
As I'm not familiar with C at all, I tried both other solutions presented:
path := "*30 C:\\absolute\\path\\to\\test.png"
dll := syscall.NewLazyDLL("ImageSearchDLL.dll")
imageSearch := dll.NewProc("ImageSearch")
r1, r2, r3 := imageSearch.Call(0, 0, 1920, 1080, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(imagePath))))
fmt.Println(r1, r2, r3)
and:
path := "*30 C:\\absolute\\path\\to\\test.png"
dll2, _ := syscall.LoadLibrary("ImageSearchDLL.dll")
imageSearch2, _ := syscall.GetProcAddress(dll2, "ImageSearch")
r1, r2, r3 = syscall.Syscall6(imageSearch2,
5,
uintptr(0),
uintptr(0),
uintptr(1920),
uintptr(1920),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(imagePath))),
0)
fmt.Println(r1, r2, r3)
Note that the path variable is set according to this solution using C#.
The output of both is as followed:
6442522512 0 Der Vorgang wurde erfolgreich beendet.
Meaning: "The process has been completed successfully".
As the DLL is supposed to return a char*
, I opened up the documentation of unsafe and found Pointer > (6). Using a guessed length for the string, I came up with this attempt:
imagePath := "*30 C:\\absolute\\path\\to\\test.png"
dll := syscall.NewLazyDLL("ImageSearchDLL.dll")
imageSearch := dll.NewProc("ImageSearch")
r1, r2, r3 := imageSearch.Call(0, 0, 1920, 1080, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(imagePath))))
fmt.Println(r1, r2, r3)
var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
hdr.Data = uintptr(unsafe.Pointer(r1))
hdr.Len = 64
fmt.Println(s)
This did at least print some kind of string:
6442522512 0 Der Vorgang wurde erfolgreich beendet.
0 0 0 0 0 1|%d|%d|%d|%d 0 c:\pic.bmp
From my understanding, the problem might arise from 3 possible problems:
char*
is not workingAs I have to clue on how to approach 2 & 3, I at least wanted to make sure 1 is not the case. Therefore, I did a quick test in Autoit returning the expected result:
#include "array.au3"
$res = DllCall("ImageSearchDLL.dll", "str", "ImageSearch", "int", 0, "int", 0, "int", 1920, "int", 1080, "str", "*30 C:\\absolute\\path\\to\\test.png")
_ArrayDisplay($res)
Output:
Row|Col 0
[0]|1|505|1051|21|20
[1]|0
[2]|0
[3]|1920
[4]|1080
[5]|*30 C:\\absolute\\path\\to\\test.png
This gave me the confidence, that I'm at least using the dll correctly. However, this also led to the point where I'm really out of clues on what else to try. Having a look on cgo, I did not really get how I would be doing what I want to achieve, especially without much knowledge of C.
Do you have any suggestion on my further approach and what else to try?