Recent

Author Topic: Importing external function via pointer  (Read 2756 times)

Pixy

  • New Member
  • *
  • Posts: 49
Importing external function via pointer
« on: November 09, 2019, 03:31:22 am »
I have a dll and a gui which are separate projects/processes. How can I call a function located inside the dll from gui? I have tried storing the address of the function into a pointer and then sending it to the gui, but when I call it in the gui the function does not return any value.

Code: Pascal  [Select][+][-]
  1. ~dll side~
  2. function Simple: String; stdcall;
  3. begin
  4.     Result:='Simple';
  5. end;
  6. ...
  7. print(IntToStr(Cardinal(@Simple))); // print pointer address
  8. secndtogui(receive, IntToStr(Cardinal(@Simple));
  9.  
  10. ~gui side~
  11. procedure receive(Adr: String);
  12. var
  13.     P: Pointer;
  14.     F: function: String;
  15. begin
  16.     P := Pointer(Cardinal(StrToInt(Adr)));
  17.     print(IntToStr(Cardinal(P))); // prints the same pointer address
  18.     F:=P; // or F:=P^;
  19.     print(F); // prints/returns nothing
  20. end;
  21.  

I can pass pointers to values such as strings, integers and print them, but I have not managed to do that with procedures/functions. Any ideas?
« Last Edit: November 09, 2019, 03:55:00 am by Pixy »

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Importing external function via pointer
« Reply #1 on: November 09, 2019, 09:15:43 am »
I would not use strings in a dll, but pchar, array of char or shortstring. other strings are managed types.
If you insist on using strings you also need sharemem.
The problem can be solved by declaring a procedural type
Code: Pascal  [Select][+][-]
  1. type
  2.   TSimple = function:string;stdcall;
  3.  
  4. var
  5.   Simple:TSimple;external 'yourdll' name 'simple';
https://freepascal.org/docs-html/ref/refse17.html
« Last Edit: November 09, 2019, 09:23:32 am by Thaddy »
Specialize a type, not a var.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Importing external function via pointer
« Reply #2 on: November 09, 2019, 10:43:03 am »
I have a dll and a gui which are separate projects/processes. How can I call a function located inside the dll from gui? I have tried storing the address of the function into a pointer and then sending it to the gui, but when I call it in the gui the function does not return any value.

Code: Pascal  [Select][+][-]
  1. ~dll side~
  2. function Simple: String; stdcall;
  3. begin
  4.     Result:='Simple';
  5. end;
  6. ...
  7. print(IntToStr(Cardinal(@Simple))); // print pointer address
  8. secndtogui(receive, IntToStr(Cardinal(@Simple));
  9.  
  10. ~gui side~
  11. procedure receive(Adr: String);
  12. var
  13.     P: Pointer;
  14.     F: function: String;
  15. begin
  16.     P := Pointer(Cardinal(StrToInt(Adr)));
  17.     print(IntToStr(Cardinal(P))); // prints the same pointer address
  18.     F:=P; // or F:=P^;
  19.     print(F); // prints/returns nothing
  20. end;
  21.  

I can pass pointers to values such as strings, integers and print them, but I have not managed to do that with procedures/functions. Any ideas?
First of you should not pass strings or other managed types between the application and the DLL unless you're also using a shared memory manager. Use PChars otherwise (which of course means manual memory management).

What I don't get is how your two parts are interacting. What is your SendToGUI doing? How is your Receive called?

Pixy

  • New Member
  • *
  • Posts: 49
Re: Importing external function via pointer
« Reply #3 on: November 10, 2019, 06:02:54 am »
I would not use strings in a dll, but pchar, array of char or shortstring. other strings are managed types.
If you insist on using strings you also need sharemem.
The problem can be solved by declaring a procedural type
Code: Pascal  [Select][+][-]
  1. type
  2.   TSimple = function:string;stdcall;
  3.  
  4. var
  5.   Simple:TSimple;external 'yourdll' name 'simple';
https://freepascal.org/docs-html/ref/refse17.html


I will try it right away, thanks! About strings, what are the reasons to avoid them in this situation?

Edit: Got some errors  %) see attachment

What I don't get is how your two parts are interacting. What is your SendToGUI doing? How is your Receive called?

It is a function that was included in the example code. If you are interested here is the exact code: https://www.sendspace.com/file/zlooui (Delphi 2010 RAD Studio). I did alter it for ease. The ~gui side~ actually refers to the script (TestPlugin.txt) and the "sendtogui" function is this:
Code: Pascal  [Select][+][-]
  1. function PluginProc(Code: Cardinal; p1, p2, p3: WideString): WideString; stdcall;
  2.  
If you take a look at the project you will understand.

Basically, this is a plugin that is called by a script (TestPlugin.txt) which is running on a script engine. Here is the program for which I write this plugin: https://adrenalinebot.com/en/ However, in order to have access to the script engine you must have access to the main program which requires a paid key around $10/month. Example: https://youtu.be/UmlmvKuh4ZI?t=58

If you recall, I had this interop question, because I wanted the plugin to call my GUI written in VB net. Thanks to your advice, I have managed to transfer data through GUI (VB net dll with form) -> Plugin (delphi dll) -> Script (.pas or .txt), but I have no means to transfer data the opposite way; from the script all the way to the GUI.
« Last Edit: November 10, 2019, 07:46:40 am by Pixy »

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Importing external function via pointer
« Reply #4 on: November 10, 2019, 07:48:45 am »
The reason to avoid pascal strings is already explained by me and PascalDragon: other compilers do not understand Pascal strings, so you should use any of the types that can be used for interoperatibility, like PChar, PWideChar, Array of char.
Pascal strings can only be used between pascal programs and pascal modules and require a shared memory manager like \rtl\win\sharemem.pp.
But this will not help in your case. You Need PChar etc. because you are interfacing with code written in VB/C/C++
« Last Edit: November 10, 2019, 07:53:54 am by Thaddy »
Specialize a type, not a var.

Pixy

  • New Member
  • *
  • Posts: 49
Re: Importing external function via pointer
« Reply #5 on: November 10, 2019, 08:16:06 am »
I did not notice any problem so far. For example, to send a string from the script to delphi dll to vb net dll I use this
Code: Pascal  [Select][+][-]
  1. ~delphi dll~ (Plugin.dll)
  2. procedure ShowUN(out UN: WideString); stdcall;
  3. var
  4.   GetNid : WideString;
  5.  
  6. begin
  7.   Com2 := CreateOleObject('com.GUI');
  8.   GetNid := Com2.ShowNid; // retrieves from vb dll the index of the bot whose username we want
  9.   UN := PluginProc(1, GetNid, '', ''); // gets username from the script
  10.   if Length(UN) = 0 then
  11.   begin
  12.     UN := 'Error';
  13.   end;
  14. end;
  15.  
  16. exports
  17.   ShowUN;
  18. end.
  19.  
  20. ~vb dll~
  21. <DllImport("Plugin.dll", CallingConvention:=CallingConvention.StdCall)>
  22.     Public Shared Sub ShowUN(<MarshalAs(UnmanagedType.BStr)> ByRef UN As String)
  23.  
  24.     End Sub
  25.  
  26. Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
  27.         Dim Uname As String = Nothing : ShowUN(Uname)
  28.  
  29.         If CheckBox1.Checked = True Then
  30.             Try
  31.                 TabPage3.Text = Uname
  32.             Catch ex As Exception
  33.                 MessageBox.Show(String.Format("Error: {0}", ex.Message))
  34.             End Try
  35.         End If
  36.     End Sub
  37. //...
  38. //'com class
  39. Public Function ShowNid() Implements IGUI.ShowNid
  40.         Return Class2.Nid //'bot index as string "0"
  41.     End Function
  42.  
  43. ~script~
  44.  
  45. uses
  46.         SysUtils, Classes;
  47.  
  48. function PluginProc(Code: Cardinal; p1, p2, p3: WideString): WideString; stdcall;
  49. begin
  50.   case Code of
  51.         1: Result := GetControlByIndex(StrToInt(p1)).GetUser.Name;
  52.         // ...
  53.   end;  
  54. end;
  55.  
  56. procedure StartPlugins;
  57. begin
  58.   Script.StartPlugin(Script.Path + '\Plugin.dll', @PluginProc, True);
  59. end;
  60.  
  61. begin
  62.   Script.MainProc(@StartPlugins);
  63.   Delay(-1);
  64. end.
  65.  

This will successfully get the username across. This is done by checking a checkbox in vb dll which triggers the procedure ShowUN in delphi dll which triggers the  PluginProc function of the script that retrieves the username.

The thing is that I cannot do this from the script. For example, let's say that the character dies in game. At that point, I want to show the string "Dead" in the GUI (vb dll). So I want the script to initiate this process by calling a procedure similar to ShowUN (without PluginProc) which will in turn send the string to the vb dll. Function PluginProc allows the delphi dll to interact with the script, but not the other way around.

In short, the problem is not related to interoperability with VB/C/C++, but with delphi dll and delphi script. Both are using the same compiler I assume or some kind of sharemem. It is possible that the main program which is closed source exposes the script and the dll in a sharemem space, but I have no knowledge of that. The developer told me to pass the address of a pointer of a procedure/function of the delphi dll from that dll to the script (using PluginProc) and then when needed call the procedure/function from the script using that pointer. But.. I don't know how to make it work.
« Last Edit: November 10, 2019, 09:25:35 am by Pixy »

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Importing external function via pointer
« Reply #6 on: November 10, 2019, 10:04:04 am »
Note widestring is NOT a reference counted Pascal string type, so is safe to use indeed. It is bwstr com compatible type. Still be careful with memory leaks.

Also note to not confuse widestring with UnicodeString, which is a referenc ecounted pascal string type and can not be used without casting.
I would recommend to take some time and read the documentation for the different string types.
https://www.freepascal.org/docs-html-3.0.0/ref/refse13.html#refsu7.html
« Last Edit: November 10, 2019, 10:38:23 am by Thaddy »
Specialize a type, not a var.

Pixy

  • New Member
  • *
  • Posts: 49
Re: Importing external function via pointer
« Reply #7 on: November 10, 2019, 01:57:02 pm »
Thanks. From what I read it has to do with memory allocation https://stackoverflow.com/questions/8414972/delphis-sharemem-when-it-is-not-needed

I will make it like this then:
Code: Pascal  [Select][+][-]
  1. Type
  2.     TSimple = function: WideString; stdcall;
  3.  
  4. var
  5.     Simple: TSimple; external 'Plugin.dll' name 'simple';
  6.  
  7. implementation
  8.  
  9. function TSimple.Simple: WideString; stdcall;
  10. begin
  11.     Result:='Simple';
  12. end;
  13. //...
  14.  

There are just two things. Firstly, that part "'Plugin.dll' name 'simple'" reads "Expected : but received a string literal "Plugin. dll" at line 50 (50:29) Expected ; but received a string literal "simple" at line 50 (50:47)". Currently looking at this https://www.freepascal.org/docs-html/current/prog/progsu148.html . Secondly, I assume that I have to get the pointer of the variable "Simple", how do I go about it and how do I use it?

Thanks.
« Last Edit: November 10, 2019, 02:02:26 pm by Pixy »

Leledumbo

  • Hero Member
  • *****
  • Posts: 8746
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Importing external function via pointer
« Reply #8 on: November 10, 2019, 05:07:48 pm »
Thanks. From what I read it has to do with memory allocation https://stackoverflow.com/questions/8414972/delphis-sharemem-when-it-is-not-needed

I will make it like this then:
Code: Pascal  [Select][+][-]
  1. Type
  2.     TSimple = function: WideString; stdcall;
  3.  
  4. var
  5.     Simple: TSimple; external 'Plugin.dll' name 'simple';
  6.  
  7. implementation
  8.  
  9. function TSimple.Simple: WideString; stdcall;
  10. begin
  11.     Result:='Simple';
  12. end;
  13. //...
  14.  

There are just two things. Firstly, that part "'Plugin.dll' name 'simple'" reads "Expected : but received a string literal "Plugin. dll" at line 50 (50:29) Expected ; but received a string literal "simple" at line 50 (50:47)". Currently looking at this https://www.freepascal.org/docs-html/current/prog/progsu148.html . Secondly, I assume that I have to get the pointer of the variable "Simple", how do I go about it and how do I use it?

Thanks.
That's syntactially wrong. I think you just need a basic dll interfacing with FPC, so you'll find the attached archive does exactly that, using both static and dynamic loading of the library and function.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Importing external function via pointer
« Reply #9 on: November 10, 2019, 05:22:29 pm »
That's syntactially wrong.
Yes, he did not follow my example to the letter. Which was syntactically correct...(at least in mode delphi)
And yes, he needs just a basic example on how to write a dll correctly, including widestring  handling, but there already so many examples on this forum that I refuse to do that. See my signature  :P .... and the manuals....
« Last Edit: November 10, 2019, 05:26:04 pm by Thaddy »
Specialize a type, not a var.

Pixy

  • New Member
  • *
  • Posts: 49
Re: Importing external function via pointer
« Reply #10 on: November 10, 2019, 06:27:34 pm »
That's syntactially wrong. I think you just need a basic dll interfacing with FPC, so you'll find the attached archive does exactly that, using both static and dynamic loading of the library and function.

About dynamic link, TLibHandle doesn't seem to be supported by the script engine. Regarding static link, I attach a photo showing the violation error that I got. The only way that I successfully retrieve strings and integers from "thelib" is by dereferencing their pointers (except PluginProc method). That's why I was hoping to find a way to do the same for procedures and functions.
« Last Edit: November 10, 2019, 06:43:10 pm by Pixy »

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Importing external function via pointer
« Reply #11 on: November 10, 2019, 06:56:53 pm »
You are still wrong in so many ways that I suggest to wait until I post an example for which I do not have the time right now.
But I have tomorrow.
Specialize a type, not a var.

Pixy

  • New Member
  • *
  • Posts: 49
Re: Importing external function via pointer
« Reply #12 on: November 10, 2019, 09:21:37 pm »
Now I understand your persistence on PChar. It is basically a pointer with no strings attached. It works better than WideStrings, confirmed. Check your inbox.

 

TinyPortal © 2005-2018