Unmanaged DLL

How to translate const char**?

int myFunc1(const char** filename_list, int file_count);

How to translate double*?

double myFunc2(double *x, double *y);

There are many different options:

void f(int * x)

Can be translated as any of:

procedure f(x: ^integer); // raw pointer
procedure f(var x: integer); // reference (managed pointer), can point to an array item (including not the [0] one)
procedure f(x: array of integer); // an array

It all depends on how you want to use the subprogram from managed code…


Meanwhile char in unmanaged code is ANSI (1 byte) character, but in .Net it is UTF16 (2 bytes) characters. The default marshaler would only handle conversion if you use the string type. So filename_list would probably be array of string.

Also, it might help the default marshaler to add [In] and/or [Out] (from System.Runtime.InteropServices namespace) before parameters.

In the case of filename_list, const char** means that the library promises not to change the contents of the converted strings, but since it’s not const char* const* - it can substitute a new string. In other words, in the unmanaged equivalent:

var filename_list: array of string;
...
filename_list[str_ind][char_ind] := ...; // myFunc1 promises not to do this
filename_list[str_ind] := ...; // myFunc1 can do this, completely replacing one of strings
filename_list := ...; // myFunc1 can do this, but then you'll not see the change outside of the function

To handle this properly - you will need to marshal the strings manually, using Marshal.StringToHGlobalAnsi:

function myFunc1_unmanaged([&In] filename_list: array of IntPtr; [&In] file_count: integer): integer;
  external 'some.dll' name 'myFunc1';
function myFunc1(filename_list: array of string): integer;
begin
  var marshaled := filename_list.ConvertAll(
    Marshal.StringToHGlobalAnsi
  );
  try
    myFunc1_unmanaged(marshaled, filename_list.Length);
    // Marshal back all the strings, in case some of them were replaced
    // Can maybe make a copy of "marshaled" and only convert ptr's that were replaced
    // It may or may not be an optimization...
    for var i := 0 to marshaled.Length-1 do
      filename_list[i] := Marshal.PtrToStringAnsi(marshaled[i]);
  finally
    // Marshal.StringToHGlobalAnsi allocates unmanaged (by CLR, and so GC too) memory
    // So you need to free it manually and do it in "finally" in case "myFunc1_unmanaged" fails
    marshaled.ForEach(Marshal.FreeHGlobal);
  end;
end;

But I also suspect that missing const in filename_list is an oversight and the function isn’t actually going to substitute any string… In that case, you can just have the external version of the function take [&In] filename_list: array of string and let the default marshaler do a simple one-way conversion.

1 лайк

I gave you the basic blocks. I’m not planning to write all the code for you.

Provide proper context, your own attempts, and problems you don’t fully understand, then there will be something for me to explain and suggest…

1 лайк

Wow, what a bargain :slight_smile:

Maybe try something else?

Not, but seriously, put more effort in…

What a waste of time talking with arrogant men! Man, I’m not your student and we are not in your class. Not because this is a forum of a school that everyone joined it is your student. Keep that in mind. I have no need to persuade you or to bargain with you. I don’t have to please anyone here. It’s you said that you only give me basic blocks. That’s OK. But your so called basic blocks is very incomplete to be useful in practical. This is why I said I will give more questions to you so you could complete your tutorial. So far, you are missing opaque struct, pointer to opaque struct, void pointer, pointer to void pointer, fixed size array, fixed size char array (fixed size C string) and so on. People can easily find the solutions for these on Google. I work with P/Invoke in C#. PascalABC.NET has it own syntax for Unmanaged DLL. I found this is interesting and want to know more. But the help file is very useless because the section for Unmanaged DLL is too bare metal so I go here. I don’t need you to lecture me how to use P/Invoke.

Manually writing P/Invoke is already obsolete in the .NET world. Plenty of tools available to help, e.g: ClangSharp, CppSharp, C2CS, SharpGenTools,… There are tools to help you with Reverse P/Invoke, too. What I see is a weird syntax and I want to know more. Do you think I need your language? No. Even C# 6.0 is more powerful.

“Arrogant” huh… Did I misinterpret your previous attitude of “I ask questions and you just have to give me answers and nothing else”?

I also don’t owe anything to you either, I’m helping people for my own enjoyment. And your form of asking for help was not enjoyable. Unpleasant.

Most importantly, your way of asking seems mostly efficient for Google and CharGPT. You ask general questions, expect general answers, and plan to take away only a small portion of the info. That’s only okay when on the other side is the machine that doesn’t care about the effort it puts in.


In more concrete terms:

For instance, you could’ve started by saying you have experience in C# P/Invoke and mainly just don’t understand the syntax. Then, I’d spend much less effort on wringing such that even a P/Invoke noob would understand, and get to the point quicker.

The reason I wanted you to give me more context and show your thought process - is not to be like a teacher, but to establish communication.


As for char*** - it is, of course, a jagged array (with items being strings). Jagged arrays are not supported by the default marshaler - and in C# this is no less true.

Yes, marshaling by hand is error-prone and should be replaced by codegen in any big project. Tho, all the general-purpose marshaling code generators were not very good for any specific case I needed them for. So for my own big projects, I found it’s better to write project-specific code-generators.

Meanwhile, in the case of one-time marshaling, doing it manually may still be justified. Especially if you want to first understand the principal/syntax/etc.

That being said, I am still oblivious to what exactly of your C# P/Invoke experience you don’t know how to translate to Pascal when it comes to marshaling jagged arrays. I mean, array of string is already like a specific case of a jagged array of characters…

This is the only thing of all you mention that can be a problem. There is no Pascal syntax for declaring stack-allocated fixed-size arrays.

What I’ve mostly done so far was to codegen a record (aka C# struct) with attributes specifying this record’s size and field offsets. Well, this really is very ugly to write if you are marshaling manually… At the very least you need to declare a new record for every string size.

But you can also use [MarshalAs] on fields and parameters, to have a non-fixed-size managed array or string be automatically marshaled (and padded/trimmed) to a fixed-size C array. It’s not nearly as efficient (it will always involve a copy), but that’s just another push to switch to codegen, once you know what you need for your specific case.