How do I pass Varargs for object[] args but i worry about Linux and macOS because __arglist doesn't work everyone. #107007
-
Hello I am looking for function: nint CreateVarArgs(object[] args) for [DllImport("msvcrt", EntryPoint = "print")]
private static extern void pinvoke_print(string fmt, nint args);
...
private static nint CreateVarArgs(object[] args)
{
// for argument of implementation like pass to string, integer or pointer
}
public static void print(string fmt, params object[] args)
{
pinvoke_print(fmt, CreateVarArgs(args));
} Ps ChatGPT went wrong because it tells more arguments. But I don't want to waste my time. Can I get simple create variable arguments? I have tried to resolve. PS I use with |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 8 replies
-
You can't interop with va_args on other platforms. They are only supported on Windows x86/x64.
va_args is not passed by one pointer. Such function will never work. You can simulate va_args with separated parameters for simple cases, but it can't handle cases like floating point parameters need to be passed in both integer and FP register. I can't find the previous discussion about this. If you still do want to do this, the only way is to see what assembly is generated by C code, and try to simulate it with cdecl based on knowledge of calling convention. |
Beta Was this translation helpful? Give feedback.
-
I found my old example with pinvokes: Now I have replaced modern version of loading shared library or passing static library like ( -lc or -lSDL2 etc ) // Define function pointer first for GetProcAddress-like function
private unsafe delegate*<sbyte*, void*> s_getProcAddress;
// Create first struct for current OS example: Windows as WinCRT ( Windows's C-Runtime -> msvcrt(.dll) )
private struct WinVCRT
{
private const string kernel32 = "kernel32";
[DllImport(kernel32)]
private unsafe static extern void* LoadLibraryA(sbyte* libPath);
[DllImport(kernel32)]
private unsafe static extern void* GetProcAddress(void* libHandle, sbyte* procName);
[DllImport(kernel32)]
public unsafe static extern int FreeLibrary(void* libHandle);
public unsafe static void* StdString_GetProcAddress(sbyte* procName)
{
void* handle = GetProcAddress(LoadLibraryA("msvcrt".ToUTF8SignedBytePointer()), procName);
if (handle == null)
{
Console.WriteLine("Error: Not found msvcrt.dll");
FreeLibrary(handle);
return null;
}
return handle;
}
}
// other os you should put but you should same GetProcAddress-like function because it equals delegate*<sbyte*, void*> s_getProcAddress;
...
// Create GetAPI()
public unsafe static StdString GetAPI()
{
StdString stri = new();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
stri.s_getProcAddress = &WinVCRT.StdString_GetProcAddress;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
stri.s_getProcAddress = &LinuxC.StdString_GetProcAddress;
}
// If you would like to add complete variables like in C/C++
// But sometimes need to access variables...
return stri;
}
// If you don't use create custom GetProcAddress-like function - but you need to OS-switcher RuntimeInformation.IsOSPlatform(OSPlatform)
public static GLInvoker GetApi(delegate*<sbyte*, void*> func)
{
StdString stri = new();
stri.s_getProcAddress = func;
if (func == null)
{
Console.WriteLine(@"Error: Wrong or incorrect GetProcAddress other built-in advanced get proc address! Thanks!");
}
return stri;
}
// Example memcpy()
/*
* void *memcpy(void *dest_str, const void * src_str, size_t n)
*/
public unsafe delegate*<void*, void*, nuint, void*> memcpy
{
get
{
return (delegate*<void*, void*, nuint, void*>)s_getProcAddress("memcpy".ToUTF8SignedBytePointer());
}
}
// any public methods if you add here. And also printf from stdio.h I already create simple if you love to get implements with printf under C# /*
* int printf(const char *fmt, ...)
*/
private delegate*<sbyte*, void*, int> internal_printf_param1
{
get
{
return (delegate*<sbyte*, void*, int>)s_getProcAddress("printf".ToUTF8SignedBytePointer());
}
}
public unsafe int printf(sbyte* fmt, params void*[] args)
{
if (fmt == null)
{
return 0;
}
int result = 0;
int argIndex = 0;
while (*fmt != 0)
{
if (*fmt == 0x25) // '%'
{
fmt++;
if (*fmt == 0x73) // 's'
{
// Handle string
sbyte* str = (sbyte*)args[argIndex];
result += internal_printf_param1("%s".ToUTF8SignedBytePointer(), str);
argIndex++;
}
else if (*fmt == 0x64 || *fmt == 0x69 || *fmt == 0x66) // 'd' 'i' 'f'
{
int number = *((int*)args[argIndex]);
result += internal_printf_param1("%d".ToUTF8SignedBytePointer(), (void*)number);
argIndex++;
}
else if (*fmt == 0x70) // 'p'
{
void* pointer = args[argIndex];
result += internal_printf_param1("%p".ToUTF8SignedBytePointer(), pointer);
argIndex++;
}
else
{
// Unsupported format specifier, print the literal '%'
result += internal_printf_param1("%c".ToUTF8SignedBytePointer(), (void*)(int)'%'); // Print '%'
}
}
else
{
// Print the current character as it is
result += internal_printf_param1("%c".ToUTF8SignedBytePointer(), (void*)(int)*fmt);
}
fmt++;
}
return result;
} If you want to I hope you get successful with C#. |
Beta Was this translation helpful? Give feedback.
If the native code is under your control, the only way is defining it like this:
And define your own rule to interpret
ARG_SLOT
with different types.If it's not under your control like
printf
, then you have literally no way to use it correctly.