Converting C dll header to delphi: char* - c

I'm using an NIDAQmx DLL in a Delphi XE4 app. The DLL only has an ANSI C header file.
I'm trying to convert this function:
int32 __CFUNC DAQmxGetPhysicalChanName(TaskHandle taskHandle,
const char channel[], char *data, uInt32 bufferSize);
This is my translation:
function DAQmxGetPhysicalChanName(taskHandle: TTaskHandle;
chanName: PAnsiChar; chanPhysName: PAnsiChar;
bufferSize: DWORD): Integer; stdcall; external NI_DLL_NAME delayed;
When I call it like this:
var
s1,s2: String[200];
sp: PAnsiChar;
begin
// sp:=#s2[1]; When I uncomment this, fucntion works as
// expected, otherwice return string s2 is empty!
res:=DAQmxGetPhysicalChanName(taskHandle,#s1[1],#s2[1],200);
The function works only when I insert sp:=#s2[1] before the call. I never actually use the sp pointer, but just the fact that it get assigned helps. Without that, the s2 string is empty. I cant understand why. What am I doing wrong?

Related

Calling a DLL function with an allocated character buffer that the function fills in Inno Setup

I am using (Unicode) Inno Setup 6.0.5 on Windows 10 64-bit.
The exported symbol, I want to use has the signature:
typedef int(__stdcall *GetDirVST2x86) (LPWSTR lpString1);
The Inno Setup [Code] section has its declaration as:
function GetDirVST2x86(var lpString1: String): Integer;
external 'GetDirVST2x86#files:R2RINNO.DLL stdcall setuponly';
where, lpString1 will contain a pointer to the wide-string after the function returns and R2RINNO.DLL is a 32-bit DLL.
Now my problem is, if I compile and run this setup, a read access violation occurs right when I try to retrieve the value. I get the correct result when I execute this same function from a C program. Removing the var from the prototype declaration in Inno script fetches an empty (or possibly) empty or blank string, so that doesn't help either.
I don't have the source for the DLL I wish to use, and I figured out the signature from IDA. The scripting engine Inno Setup seems hopelessly inadequate as it doesn't support pointers at all.
One interesting thing I observed was if I changed the type of lpString1 to Cardinal or Integer and used IntToStr to fetch the string I got the value of the directory in which the setup was getting created.
Here's a working C code:
#include <windows.h>
#include <stdio.h>
#define _UNICODE
#define UNICODE
typedef int(WINAPI *GetDirVST2x86) (LPWSTR );
int main() {
HMODULE hModule = LoadLibrary("R2RINNO.DLL");
if (NULL != hModule) {
GetDirVST2x86 pGetDirVST2x86 = (GetDirVST2x86) GetProcAddress (hModule, "GetDirVST2x86");
if (NULL != pGetDirVST2x86) {
LPWSTR lpszVST2x86;
pGetDirVST2x86(lpszVST2x86);
wprintf(lpszVST2x86);
}
FreeLibrary(hModule);
}
}
Here's the output:
C:\Program Files (x86)\Steinberg\VstPlugins
Here's the IDA screenshot of the function I want to use:
Pascal Script equivalent of the C declaration should be:
function GetDirVST2x86(lpString1: string): Integer;
external 'GetDirVST2x86#files:R2RINNO.DLL stdcall setuponly';
(i.e. no var, as it is an input character pointer argument).
Assuming the function contract is that you (as a caller) allocate a buffer and provide it to the function to be filled in, you should call the function like this:
var
Buf: string;
begin
{ Allocate buffer for the result large enough according to the API specification }
SetLength(Buf, 1000);
GetDirVST2x86(Buf);
SetLength(Result, Pos(#0, Result) - 1);
end;
See also How to return a string from a DLL to Inno Setup?

Calling a C function with a double pointer output parameter using CGo

I am trying to figure out th right way to call this function:
size_t
fz_buffer_storage(fz_context *ctx, fz_buffer *buf, unsigned char **datap)
{
if (datap)
*datap = (buf ? buf->data : NULL);
return (buf ? buf->len : 0);
}
using CGo to get the underlying string and its length as a byte array in Go.
Is this the right way to do it?
var bufferContents *C.uchar
length := C.fz_buffer_storage(ctx, buf, &bufferContents)
bytes := C.GoBytes(unsafe.Pointer(bufferContents), C.int(length))
Since the C code overwrites *datap, I am not sure if the garbage collector will still do the right thing.
I saw an answer here suggesting something along the lines of
var tempUcharPtr *C.uchar
bufferContents := C.malloc(C.size_t(unsafe.Sizeof(tempUcharPtr)))
defer C.free(bufferContents)
length := C.fz_buffer_storage(ctx, buf, (**C.uchar)(bufferContents))
bytes := C.GoBytes(unsafe.Pointer(*(**C.uchar)(bufferContents)), C.int(length))
which also seems to work, but it's much more convoluted and I'm wondering if it's better / safer than the previous version.
Apparently, the first version is fine. Quoting the docs:
Go code may pass a Go pointer to C provided the Go memory to which it points does not contain any Go pointers.
From what I understand, since var bufferContents *C.uchar will be initialised to nil, it does not count as a "Go pointer" for the above rule. The following simplified code examples confirm this:
package main
// void F(char **p) {}
import "C"
func main() {
var p *C.char = new(C.char)
C.F(&p)
}
will trigger "panic: runtime error: cgo argument has Go pointer to Go pointer"
package main
// void F(char **p) {}
import "C"
func main() {
var p *C.char
C.F(&p)
}
works just fine, even when setting GODEBUG=cgocheck=2.
Thanks to the folks on the #cgo channel on the Gophers Slack community for helping me understand this!

Converting C functions in Delphi 10 Seattle

I'm trying to convert API SendAnywhere header file from C to Delphi 10 code for Windows.
Using presented table of Embarcadero I successfully convert some of the functions:
typedef void* PaprikaTask;
typedef void* PaprikaAuthToken;
__declspec(dllexport) void paprika_set_apikey(const char* key);
__declspec(dllexport) PaprikaAuthToken paprika_auth_create();
__declspec(dllexport) PaprikaAuthToken paprika_auth_create_with_deviceid(const char* id, const char* password);
__declspec(dllexport) void paprika_auth_close(PaprikaAuthToken auth);
__declspec(dllexport) bool paprika_is_running(PaprikaTask task);
__declspec(dllexport) PaprikaTask paprika_create_download(const wchar_t* key, const wchar_t* destDirPath);
__declspec(dllexport) void paprika_start(PaprikaTask task);
This is my conversion:
PaprikaAuthToken = Pointer;
PaprikaTask = Pointer;
procedure paprika_set_apikey(const key: PChar); cdecl; external 'sendanywhere.dll';
function paprika_auth_create: PaprikaAuthToken; cdecl; external 'sendanywhere.dll';
function paprika_auth_create_with_deviceid(const id: PAnsiChar; const password: PAnsiChar): PaprikaAuthToken; cdecl; external 'sendanywhere.dll';
procedure paprika_auth_close(auth: PaprikaAuthToken); cdecl; external 'sendanywhere.dll';
function paprika_is_running(task: PaprikaTask): Boolean; cdecl; external 'sendanywhere.dll';
function paprika_create_download(const key: PWideChar; const destDirPath: PWideChar): PaprikaTask; cdecl; external 'sendanywhere.dll';
procedure paprika_start(task: PaprikaTask); cdecl; external 'sendanywhere.dll';
Example from SendAnywhere uses these functions as follows:
PaprikaTask pTask;
PaprikaAuthToken gToken;
paprika_set_apikey("YOUR_API_KEY");
gToken = paprika_auth_create();
pTask = paprika_create_download(L"KEY", L"/tmp");
paprika_set_auth(pTask, gToken);
paprika_start(pTask);
This is how I interpreted the proper code:
var
FAuthToken: PaprikaAuthToken;
FOption: PaprikaOption;
paprika_set_apikey(PChar('my_correct_api_key'));
FAuthToken = paprika_auth_create();
// backslash for windows
FTask := paprika_create_download(PWideChar('correct_key'), PWideChar('\tmp')); //directory exists
paprika_set_auth(FTask, FAuthToken);
paprika_start(FTask);
I checked the work of the task by function paprika_is_running. It returns FALSE before calling paprika_start_function. It begins to return TRUE after calling paprika_start_function but after 1-2 seconds (regardless of the size of the transferring file) it returns FALSE.
This means that the function is working, but it seems that the problem is in the arguments.
Please tell me where I am going wrong?
Maybe there is an incorrect conversion from string to TWideChar.
Or the path of the directory is passed incorrectly, for example and used an incorrect slash. I've tried different paths:
('C:/', 'C:\', 'C:', 'file://C:', 'file://C:/', '/', '\', '')
What is incorrect? Help please. I need your assistance.
procedure paprika_set_apikey(const key: PChar); cdecl; external 'sendanywhere.dll';
This is wrong. PChar is an alias to PWideChar. The type should be PAnsiChar.
As an aside you don't need the PAnsiChar or PWideChar casts when passing literals. Remove them to simplify the code.
Further, your use of const in parameters has a different meaning from that in the C++ code, and is in any case meaningless in an external declaration. I personally would remove those modifiers and pass the pointers as plain value params.

Read a C-type string from Go

I am trying to write a simple OpenGL application in Go and would like to read the OpenGL Version from the driver. I am using this function:
http://godoc.org/github.com/chsc/gogl/gl21#GetString
which is a wrapper function for
const GLubyte* glGetString( GLenum name);
This code:
fmt.Println(gl.GetString(gl.RENDERER))
fmt.Println(*gl.GetString(gl.VERSION))
outputs
0x4708ae0
50
The output is probably a C-type string, pointer to the first byte of the string. How can I convert the output from the GetString function into a normal go string?
Solution:
The package provided the right converter function, it is just not really obvious:
fmt.Println( gl.GoStringUb( gl.GetString( gl.RENDERER )))
General approach: (if the package wouldn't provide a *Ubyte to string conversion function)
pointer := unsafe.Pointer(gl.GetString(gl.RENDERER))
str := C.GoString( (*C.char)(pointer) )
fmt.Println(str)
The package you linked provides a function GoStringUb that does the trick:
render := gl.GoStringUb(gl.GetString(gl.RENDERER))
version := gl.GoStringUb(gl.GetString(gl.VERSION))

using a delphi callback function in a C dll

I'm using a C dll in a Delphi XE2 program without problem. One of the DLL function takes a function as argument.
Here is the prototype of the function:
var
LMX_MySetOption: function(LmxHandle: LMX_HANDLE;
eOption: _LMX_SETTINGS;
callback: TCallBackProcedure): LMX_STATUS cdecl
{$IFDEF WIN32} stdcall {$ENDIF};
The original prototype in C of the function was:
LMX_STATUS LMX_SetOption(LMX_HANDLE LmxHandle, LMX_SETTINGS eOption,
const void *pSetting);
TCallBackProcedure is defined as follow:
type
TCallBackProcedure = procedure(bla : Pointer) stdcall;
I'm calling the function this way:
LMX_MySetOption(LmxHandle, LMX_OPT_HEARTBEAT_EXIT_FUNCTION, UserExitRoutine);
The UserExitRoutine is definede as follow:
procedure UserExitRoutine(bla : Pointer) stdcall;
begin
...
end;
It's not working (access violation)
I can't modify the C dll.
Many thanks for any idea!
If is a C procedure don't use stdcall use cdecl .
And you can simply declare this
function LMX_MySetOption(LmxHandle: LMX_HANDLE;
eOption: _LMX_SETTINGS;
callback: Pointer): LMX_STATUS;cdecl;external 'yourmodule.dll';
procedure callback(bla:Pointer);cdecl;
begin
//Some code
end;
LMX_MySetOption(LmxHandle, LMX_OPT_HEARTBEAT_EXIT_FUNCTION, #callback);
It should work...if it doesn't maybe you don't know the exact nr of parameters that the function has...

Resources