I can't pass a delphi string as a parameter to a cgo dll - go

I have a DLL compiled in Go as a c-shared lib that takes in a string as a parameter. It works fine in other languages, like Java, and Python using ctypes to simulate a c-string.
But, when trying to use this DLL in Delphi, I can't pass in a string as a parameter.
This is the function declaration in Go:
//export RequestCertificate
func RequestCertificate(jsonString string) *C.char{
log.Println("String: ", jsonString)`
In Python, I declared a structure that simulates a c-string like this:
class GoString(Structure):
_fields_ = [("chars", c_char_p), ("length", c_longlong)]
And then passed this structure as a parameter:
jsonStr = json.dumps(payload)
jsonB64 = base64.b64encode(bytes(jsonStr, "ansi"))
goJson = GoString(jsonB64, len(jsonB64))
response = [GoString]
response = lib.RequestCertificate(goJson)
In the terminal, the DLL shows exactly what I expect.
String: eyJ1cmwiOiAiaHR0cHM6Ly9scC5ub...
But, I can't replicate this in Delphi. This is how I declared the structure that simulates a c-string:
type
TGoString = record
chars: PAnsiChar;
length: LongInt;
jsonStr := payload.ToString;
jsonBytes := TEncoding.ANSI.GetBytes(jsonStr);
jsonB64 := TNetEncoding.Base64.Encode(jsonBytes);
goJson.chars := PAnsiChar(jsonB64);
goJson.length := Length(jsonB64);
response := RequestCertificate(goJson);
But all I get is an empty reply:
String:
I already changed the declaration of my Go function to receive a c-string instead of a Go string this way:
//export RequestCertificate
func RequestCertificate(CString *C.char) *C.char{
log.Println("C String: ", CString)
goString := C.GoString(CString)
log.Println("GoString: ", goString)
The result in Python is this:
C String: 0xcbe9bef070
GoString: eyJ1cmwiOiAiaHR0cHM6Ly9scC5ub...
But in Delphi, the result is this:
C String: 0x14feb0
GoString :
I also tried to encode my Delphi string in other formats instead of ANSI, but it didn't work.
What could the reason be for this behavior? Incompatibility between Delphi and Go encoding?
I am running this in a Windows virtual machine, which runs on an Ubuntu system.

Related

Convert func to string literal in golang

I have some func saved in a variable and I need to convert it into a string literal:
testFunc := func(orgId int64) bool {
return false
}
The expected result string literal is like this:
resultStr := `func(orgId int64) bool {
return false
}`
Here is what I have tried:
testFuncStr := reflect.ValueOf(testFunc).String()
However this only get me the following literal:
"<func(int64) bool Value>",
As #BurakSerdar said - Go is not a language which is interpreted, but compiled, so there's rather limited amount of stuff you can do. reflect is a powerful package - you can get function names, number of function arguments, and even call stack (which is handy for logging), but you will never be able to obtain a source code of a function from within a build binary.

Issue with sys package related to I/O operation

As I want to access some lower-level API to do I/O operation using CreateFile function
syscall.CreateFile( name *uint16…)
While doing so I face a problem that the name parameter is of *uint16 but it should be an array ([]uint16) so that it can handle the string in the UTF-16 format. As we can see in the example provided by Microsoft -> link where TEXT macro convert the string into wchar_t array or we can say []uint16.
Thanks in advance and sorry if I said anything wrong as I’m just a toddler in this field.
(Solution 1)
func UTF16PtrFromString(s string) (*uint16, error)
Built-in encoder which returns a pointer to the UTF-16 encoding
(Solution 2)
As previously I was unaware of Solution 1 so I wrote my own function which does the exact work so you can ignore this solution
For passing the file name (string) to the sys package we have to first convert the string to an array of UTF-16 and pass the pointer of the first element
var srcUTf16 [ ]uint16 = utf16.Encode([ ]rune(src+ "\x00"))
syscall.CreateFile(&srcUTf16[0],..... )
Edit:-Adding solution
Edit:- Adding correct solution and adding Terminating NUL in solution 2.
I don't really care for Windows API function signatures Go has made, and I have written about this. So if you want, you can write your own. Make a file like this:
//go:generate mkwinsyscall -output zfile.go file.go
//sys createFile(name string, access int, mode int, sec *windows.SecurityAttributes, disp int, flag int, template int) (hand int, err error) = kernel32.CreateFileW
package main
import "golang.org/x/sys/windows"
func main() {
n, e := createFile(
"file.txt",
windows.GENERIC_READ,
0,
nil,
windows.CREATE_NEW,
windows.FILE_ATTRIBUTE_NORMAL,
0,
)
if e != nil {
panic(e)
}
println(n)
}
Then build:
go mod init file
go generate
go mod tidy
go build
I know the result works, because it returns a valid handle the first time, and invalid handle the second time (also because a file is created of course):
PS C:\> .\file.exe
336
PS C:\> .\file.exe
-1
If you want, you can edit the signature line I put above, to suit your needs.

unicode being output literally instead of as unicode

I am creating an IRC bot using Go as a first project to get to grips with the language. One of the bot functions is to grab data from the TVmaze API and display in the channel.
I have imported an env package which allows the bot admin to define how the output is displayed.
For example SHOWSTRING="#showname# - #status# – #network.name#"
I am trying to add functionality to it so that the admin can use IRC formatting functionality which is accessed with \u0002 this is bold \u0002 for example.
I have a function which generates the string that is being returned and displayed in the channel.
func generateString(show Show) string {
str := os.Getenv("SHOWSTRING")
r := strings.NewReplacer(
"#ID#", string(show.ID),
"#showname#", show.Name,
"#status#", show.Status,
"#network.name#", show.Network.Name,
)
result := r.Replace(str)
return result
}
From what i have read i think that i need to use the rune datatype instead of string and then converting the runes into a string before being output.
I am using the https://github.com/thoj/go-irceven package for interacting with IRC.
Although i think that using rune is the correct way to go, i have tried a few things that have confused me.
If i add \u0002 to the SHOWSTRING from the env, it returns \u0002House\u0002 - Ended - Fox. I am doing this by con.Privmsg(roomName, tvmaze.ShowLookup('house'))
However if i try con.Privmsg(roomName, "\u0002This should be bold\u0002") it outputs bold text.
What is the best option here? If it is converting the string into runes and then back to a string, how do i go about doing that?
I needed to use strconv.Unquote() on my return in the function.
The new generateString function now outputs the correct string and looks like this
func generateString(show Show) string {
str := os.Getenv("SHOWSTRING")
r := strings.NewReplacer(
"#ID#", string(show.ID),
"#showname#", show.Name,
"#status#", show.Status,
"#network.name#", show.Network.Name,
)
result := r.Replace(str)
ret, err := strconv.Unquote(`"` + result + `"`)
if err != nil {
fmt.Println("Error unquoting the string")
}
return ret
}

How to convert interface{} to string?

I'm using docopt to parse command-line arguments. This works, and it results in a map, such as
map[<host>:www.google.de <port>:80 --help:false --version:false]
Now I would like to concatenate the host and the port value to a string with a colon in-between the two values. Basically, something such as:
host := arguments["<host>"] + ":" + arguments["<port>"]
Unfortunately, this doesn't work, as I get the error message:
invalid operation: arguments[""] + ":" (mismatched types interface {} and string)
So obviously I need to convert the value that I get from the map (which is just interface{}, so it can be anything) to a string. Now my question is, how do I do that?
You need to add type assertion .(string). It is necessary because the map is of type map[string]interface{}:
host := arguments["<host>"].(string) + ":" + arguments["<port>"].(string)
Latest version of Docopt returns Opts object that has methods for conversion:
host, err := arguments.String("<host>")
port, err := arguments.String("<port>")
host_port := host + ":" + port
You don't need to use a type assertion, instead just use the %v format specifier with Sprintf:
hostAndPort := fmt.Sprintf("%v:%v", arguments["<host>"], arguments["<port>"])
To expand on what Peter said:
Since you are looking to go from interface{} to string, type assertion will lead to headaches since you need to account for multiple incoming types. You'll have to assert each type possible and verify it is that type before using it.
Using fmt.Sprintf (https://golang.org/pkg/fmt/#Sprintf) automatically handles the interface conversion. Since you know your desired output type is always a string, Sprintf will handle whatever type is behind the interface without a bunch of extra code on your behalf.
You could also use text/template:
package main
import (
"text/template"
"strings"
)
func format(s string, v interface{}) string {
t, b := new(template.Template), new(strings.Builder)
template.Must(t.Parse(s)).Execute(b, v)
return b.String()
}
func main() {
m := map[string]interface{}{"<host>": "www.google.de", "<port>": "80"}
s := format(`{{index . "<host>"}}:{{index . "<port>"}}`, m)
println(s == "www.google.de:80")
}
https://pkg.go.dev/text/template

Using Golang to get Windows idle time (GetLastInputInfo or similar)

Is there an example or method of getting a Windows system's idle time using Go?
I've been looking at the documentation at the Golang site but I think I'm missing how to access (and use) the API to get system information including the idle time.
Go's website is hardcoded to show the documentation for the standard library packages on Linux. You will need to get godoc and run it yourself:
go get golang.org/x/tools/cmd/godoc
godoc --http=:6060
then open http://127.0.0.1:6060/ in your web browser.
Of note is package syscall, which provides facilities for accessing functions in DLLs, including UTF-16 helpers and callback generation functions.
Doing a quick recursive search of the Go tree says it doesn't have an API for GetLastInputInfo() in particular, so unless I'm missing something, you should be able to call that function from the DLL directly:
user32 := syscall.MustLoadDLL("user32.dll") // or NewLazyDLL() to defer loading
getLastInputInfo := user32.MustFindProc("GetLastInputInfo") // or NewProc() if you used NewLazyDLL()
// or you can handle the errors in the above if you want to provide some alternative
r1, _, err := getLastInputInfo.Call(uintptr(arg))
// err will always be non-nil; you need to check r1 (the return value)
if r1 == 0 { // in this case
panic("error getting last input info: " + err.Error())
}
Your case involves a structure. As far as I know, you can just recreate the structure flat (keeping fields in the same order), but you must convert any int fields in the original to int32, otherwise things will break on 64-bit Windows. Consult the Windows Data Types page on MSDN for the appropriate type equivalents. In your case, this would be
var lastInputInfo struct {
cbSize uint32
dwTime uint32
}
Because this (like so many structs in the Windows API) has a cbSize field that requires you to initialize it with the size of the struct, we must do so too:
lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))
Now we just need to pass a pointer to that lastInputInfo variable to the function:
r1, _, err := getLastInputInfo.Call(
uintptr(unsafe.Pointer(&lastInputInfo)))
and just remember to import syscall and unsafe.
All args to DLL/LazyDLL.Call() are uintptr, as is the r1 return. The _ return is never used on Windows (it has to do with the ABI used).
Since I went over most of what you need to know to use the Windows API in Go that you can't gather from reading the syscall docs, I will also say (and this is irrelevant to the above question) that if a function has both ANSI and Unicode versions, you should use the Unicode versions (W suffix) and the UTF-16 conversion functions in package syscall for best results.
I think that's all the info you (or anyone, for that matter) will need to use the Windows API in Go programs.
Regarding for answer from andlabs. This is ready for use example:
import (
"time"
"unsafe"
"syscall"
"fmt"
)
var (
user32 = syscall.MustLoadDLL("user32.dll")
kernel32 = syscall.MustLoadDLL("kernel32.dll")
getLastInputInfo = user32.MustFindProc("GetLastInputInfo")
getTickCount = kernel32.MustFindProc("GetTickCount")
lastInputInfo struct {
cbSize uint32
dwTime uint32
}
)
func IdleTime() time.Duration {
lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))
currentTickCount, _, _ := getTickCount.Call()
r1, _, err := getLastInputInfo.Call(uintptr(unsafe.Pointer(&lastInputInfo)))
if r1 == 0 {
panic("error getting last input info: " + err.Error())
}
return time.Duration((uint32(currentTickCount) - lastInputInfo.dwTime)) * time.Millisecond
}
func main() {
t := time.NewTicker(1 * time.Second)
for range t.C {
fmt.Println(IdleTime())
}
}
This is code print idle time every second. Try run and don't touch mouse/keyboard

Resources