Recent

Author Topic: [SOLVED] Declaration of variable through function (DLLGetVersion entry)  (Read 7021 times)

tudi_x

  • Hero Member
  • *****
  • Posts: 532
hi All,
in the attached unit there are variables that are defined through functions but they do not have the external attribute as  I saw is needed when using functions in a C compiled file.
i do not have knowledge of this kind of declaration - could you please point me in the right direction?
i do not even know how to describe this situation in order to seek information in the internet.

example:

Code: Pascal  [Select][+][-]
  1. ************** Device enumeration and capabilities ******************
  2.     var Pa_GetDeviceCount: function:PaDeviceIndex ; cdecl;
  3.     var Pa_GetDefaultInputDevice: function:PaDeviceIndex ; cdecl;
  4.     var Pa_GetDefaultOutputDevice: function:PaDeviceIndex ; cdecl;
  5.     var Pa_GetDeviceInfo: function(device : PaDeviceIndex):PPaDeviceInfo ; cdecl;

it is really strange as the code below does not work with 'Error: Type identifier expected' when trying to define Pa_GetDeviceCount.

Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2. {$PACKRECORDS C}
  3.  
  4. uses
  5.   Classes,
  6.   SysUtils,
  7.   CustApp, ctypes,
  8.   dynlibs;
  9.  
  10.   procedure TWorkDLL.DoRun;
  11.   var
  12.     DLLInstance: THandle;
  13.     c: TDevCount;
  14.     PaDeviceIndex : CInt32;
  15.     Pa_GetDeviceCount: function:PaDeviceIndex ; cdecl;

thank you
« Last Edit: August 11, 2017, 02:18:56 pm by tudi_x »
Lazarus 2.0.2 64b on Debian LXDE 10

Blestan

  • Sr. Member
  • ****
  • Posts: 461
Re: Declaration of variable through function
« Reply #1 on: August 10, 2017, 06:16:20 pm »
1/ define all  functions as types
2/ if functions do not have result ( void ) declare them as procedures
3/ when assinging use @ operator
Speak postscript or die!
Translate to pdf and live!

tudi_x

  • Hero Member
  • *****
  • Posts: 532
Re: Declaration of variable through function
« Reply #2 on: August 11, 2017, 10:51:04 am »
thank you Blestan
i did not succeed in the 3rd issue.

to make the dll generic i picked the shell32.dll from Windows in order to interrogate for the DLL version information as per: https://msdn.microsoft.com/en-us/library/windows/desktop/bb776404(v=vs.85).aspx and https://msdn.microsoft.com/en-us/library/windows/desktop/bb773263(v=vs.85).aspx . althouth there is version 2 of structure i understood the DllGetVersion is backward compatible and would return a structure like the below:

typedef struct _DllVersionInfo {
  DWORD cbSize;
  DWORD dwMajorVersion;
  DWORD dwMinorVersion;
  DWORD dwBuildNumber;
  DWORD dwPlatformID;
} DLLVERSIONINFO;

so what i did i mapped the returning structure to a record type - TVersionInfoRecord .
i defined the entry point in the DLL but the question that arises is how do i get access to the record member values?

something like v.cbSize would return an error with 'Error: illegal qualifier . found'. so in this case how do i create a record from the function call result?

thank you!

Code: Pascal  [Select][+][-]
  1. program learn_dlls;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   Classes,
  7.   SysUtils,
  8.   CustApp,
  9.   CTypes,
  10.   dynlibs;
  11.  
  12. type
  13.   TWorkDLL = class(TCustomApplication)
  14.   protected
  15.     procedure DoRun; override;
  16.   public
  17.     constructor Create(TheOwner: TComponent); override;
  18.     destructor Destroy; override;
  19.   end;
  20.  
  21. type
  22.   TVersionInfoRecord = record
  23.     cbSize: word;
  24.     dwMajorVersion: word;
  25.     dwMinorVersion: word;
  26.     DdwBuildNumber: word;
  27.     dwPlatformID: word;
  28.   end;
  29.  
  30. type
  31.   TLibVersion = function: TVersionInfoRecord; stdcall;
  32.  
  33.   procedure TWorkDLL.DoRun;
  34.   var
  35.     DLLInstance: THandle;
  36.     v: TLibVersion;
  37.     i: TVersionInfoRecord;
  38.  
  39.   begin
  40.     try
  41.       try
  42.         DLLInstance := SafeLoadLibrary('C:\Windows\System32\shell32.dll');
  43.  
  44.         if DLLInstance <> NilHandle then
  45.         begin
  46.           v := TLibVersion(GetProcedureAddress(DLLInstance, 'DllGetVersion'));
  47.           if @v <> nil then
  48.           begin
  49.             i:= @v;   //how do i create a record from the function call result?
  50.           end;
  51.         end
  52.         else
  53.           writeln('there was an error');
  54.  
  55.       finally
  56.         FreeLibrary(DLLInstance);
  57.       end;
  58.  
  59.     except
  60.       on E: Exception do
  61.       begin
  62.         writeln('error: ' + E.Message);
  63.       end;
  64.     end;
  65.  
  66.     Terminate;
  67.   end;
  68.  
  69.   constructor TWorkDLL.Create(TheOwner: TComponent);
  70.   begin
  71.     inherited Create(TheOwner);
  72.     StopOnException := True;
  73.   end;
  74.  
  75.   destructor TWorkDLL.Destroy;
  76.   begin
  77.     inherited Destroy;
  78.   end;
  79.  
  80. var
  81.   Application: TWorkDLL;
  82. begin
  83.   Application := TWorkDLL.Create(nil);
  84.   Application.Title := 'Test_DLL';
  85.   Application.Run;
  86.   Application.Free;
  87. end.
  88.  



Lazarus 2.0.2 64b on Debian LXDE 10

balazsszekely

  • Guest
Re: Declaration of variable through function
« Reply #3 on: August 11, 2017, 11:12:22 am »
@tudi_x

1. Type of cbSize, dwMajorVersion, etc is DWord not Word.
2. Declaration of TLibVersion is wrong
Try something like this:
Code: Pascal  [Select][+][-]
  1. uses windows, dynlibs;
  2.  
  3. type
  4.   PVersionInfoRecord = ^TVersionInfoRecord;
  5.   TVersionInfoRecord = record
  6.     cbSize: DWord;
  7.     dwMajorVersion: DWord;
  8.     dwMinorVersion: DWord;
  9.     DdwBuildNumber: DWord;
  10.     dwPlatformID: DWord;
  11.   end;
  12.  
  13. type
  14.   TLibVersion = function(PVIR: PVersionInfoRecord): PVersionInfoRecord; stdcall;
  15.  
  16.  
  17. procedure TForm1.Button1Click(Sender: TObject);
  18. var
  19.   DLLInstance: THandle;
  20.   v: TLibVersion;
  21.   PVIR: PVersionInfoRecord;
  22. begin
  23.   try
  24.     try
  25.       DLLInstance := SafeLoadLibrary('C:\Windows\system32\shell32.dll');
  26.       if DLLInstance <> NilHandle then
  27.       begin
  28.         v := TLibVersion(GetProcedureAddress(DLLInstance, 'DllGetVersion'));
  29.         if @v <> nil then
  30.         begin
  31.           New(PVIR);
  32.           try
  33.             ZeroMemory(PVIR, SizeOf(PVIR^));
  34.             PVIR^.cbSize := SizeOf(PVIR^);
  35.             v(PVIR);
  36.             //dummy line, please remove it in your thread.
  37.             ShowMessage('MajorVersion: ' + IntToSTr(PVIR^.dwMajorVersion) + sLineBreak +
  38.                         'MinorVersion: ' + IntToSTr(PVIR^.dwMajorVersion));
  39.           finally
  40.             Dispose(PVIR);
  41.           end;
  42.         end;
  43.       end
  44.       else
  45.         writeln('there was an error');
  46.     finally
  47.       FreeLibrary(DLLInstance);
  48.     end;
  49.   except
  50.     on E: Exception do
  51.     begin
  52.       writeln('error: ' + E.Message);
  53.     end;
  54.   end;
  55. end;

It returns 10/10 in win10 for shell32.dll.

tudi_x

  • Hero Member
  • *****
  • Posts: 532
Re: Declaration of variable through function
« Reply #4 on: August 11, 2017, 11:59:39 am »
thank you so much.

please advise on:
 a. ZeroMemory(PVIR, SizeOf(PVIR^)); - i see ZeroMemory is in windows unit. what would be a cross platform approach?
 b. PVIR^.cbSize := SizeOf(PVIR^);   - why is this line necessary? we are calling afterwards v(PVIR) so at this stage wasn't New(PVIR) enough for the pointer initialization?

thank you
Lazarus 2.0.2 64b on Debian LXDE 10

balazsszekely

  • Guest
Re: Declaration of variable through function (DLL entries)
« Reply #5 on: August 11, 2017, 12:13:26 pm »
@tudi_x
Quote
a. ZeroMemory(PVIR, SizeOf(PVIR^)); - i see ZeroMemory is in windows unit. what would be a cross platform approach?
FillByte perhaps? But why do you want a cross platform solution. There are no dlls outside windows(so-linux, dynlib-osx). Also no DllGetVersion.

Quote
b. PVIR^.cbSize := SizeOf(PVIR^);   - why is this line necessary? we are calling afterwards v(PVIR) so at this stage wasn't New(PVIR) enough for the pointer initialization?
You have to pass the size of the structure. If you remove that line the function will fail.

PS: According to this page: https://msdn.microsoft.com/en-us/library/windows/desktop/bb776404(v=vs.85).aspx the function result is HResult not PVersionInfoRecord so you should change that. Checking the return value of the API is also important. Please modify the code accordingly.
« Last Edit: August 11, 2017, 12:15:43 pm by GetMem »

tudi_x

  • Hero Member
  • *****
  • Posts: 532
Re: Declaration of variable through function (DLL entries)
« Reply #6 on: August 11, 2017, 12:45:32 pm »
@GetMem
a. i would need to integrate with the PortAudio library for enumerating the audio devices (http://portaudio.com/docs/v19-doxydocs/querying_devices.html) on windows, linux and osx.

b. now i think i saw why. as per the windows documentation: 'The cbSize member must be filled in before you call this function.'

i am lost about HResult. firstly if I change the type to
Code: Pascal  [Select][+][-]
  1. type
  2.   TLibVersion = function(APointer: PVersionInfoRecord): string; stdcall;        

i mean - this is a function returning a HRESULT of S_OK - but in where it would return the record structure? the record structure is actually something like the
Code: Pascal  [Select][+][-]
  1. type
  2.   TLibVersion = function([b]var [/b]APointer: PVersionInfoRecord): [i]string[/i]; stdcall;        

how to modify to check the function is actually returning the S_OK? please advise

Lazarus 2.0.2 64b on Debian LXDE 10

balazsszekely

  • Guest
Re: Declaration of variable through function (DLL entries)
« Reply #7 on: August 11, 2017, 01:27:40 pm »
Quote
i would need to integrate with the PortAudio library for enumerating the audio devices
Ok, I see. Then you need something like this:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. uses
  4.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  5.   cthreads,
  6.   {$ENDIF}{$ENDIF}
  7.   classes,
  8.   SysUtils,
  9.   dynlibs
  10.   {$IFDEF MSWINDOWS}, windows{$ENDIF};
  11.  
  12.  
  13. {$IFDEF MSWINDOWS}
  14. function GetDLLVersion(const AFileName: String; out AResult: String): Boolean;
  15. type
  16.   PVersionInfoRecord = ^TVersionInfoRecord;
  17.   TVersionInfoRecord = record
  18.     cbSize: DWord;
  19.     dwMajorVersion: DWord;
  20.     dwMinorVersion: DWord;
  21.     DdwBuildNumber: DWord;
  22.     dwPlatformID: DWord;
  23.   end;
  24.  
  25. type
  26.   TLibVersion = function(PVIR: PVersionInfoRecord): HRESULT; stdcall;
  27.  
  28. var
  29.   DLLInstance: THandle;
  30.   v: TLibVersion;
  31.   PVIR: PVersionInfoRecord;
  32. begin
  33.   Result := False;
  34.   try
  35.     try
  36.       DLLInstance := SafeLoadLibrary(AFileName);
  37.       if DLLInstance <> NilHandle then
  38.       begin
  39.         v := TLibVersion(GetProcedureAddress(DLLInstance, 'DllGetVersion'));
  40.         if @v <> nil then
  41.         begin
  42.           New(PVIR);
  43.           try
  44.             ZeroMemory(PVIR, SizeOf(PVIR^));
  45.             PVIR^.cbSize := SizeOf(PVIR^);
  46.             if v(PVIR) = S_OK  then
  47.             begin
  48.                AResult := 'MajorVersion: ' + IntToSTr(PVIR^.dwMajorVersion) + sLineBreak +
  49.                           'MinorVersion: ' + IntToSTr(PVIR^.dwMajorVersion);
  50.                Result := True;
  51.             end
  52.             else
  53.               AResult := SysErrorMessage(GetLastOSError);
  54.           finally
  55.             Dispose(PVIR);
  56.           end;
  57.         end;
  58.       end
  59.       else
  60.         AResult := SysErrorMessage(GetLastOSError);
  61.     finally
  62.       FreeLibrary(DLLInstance);
  63.     end;
  64.   except
  65.     on E: Exception do
  66.       AResult := SysErrorMessage(GetLastOSError);
  67.   end;
  68. end;
  69. {$ENDIF}
  70.  
  71. var
  72.   Res: String;
  73. begin
  74.   {$IFDEF MSWINDOWS}
  75.    if GetDLLVersion('C:\Windows\system32\shell32.dll', Res) then
  76.      Writeln(Res)
  77.    else
  78.      Writeln('Error: ' + Res);
  79.   {$ENDIF}
  80.   //other os here
  81.   Readln;
  82. end.

tudi_x

  • Hero Member
  • *****
  • Posts: 532
Re: Declaration of variable through function (DLL entries)
« Reply #8 on: August 11, 2017, 02:18:11 pm »
thank you!
Lazarus 2.0.2 64b on Debian LXDE 10

ASerge

  • Hero Member
  • *****
  • Posts: 2250
Re: Declaration of variable through function (DLL entries)
« Reply #9 on: August 11, 2017, 06:19:27 pm »
Ok, I see. Then you need something like this:
Code: Pascal  [Select][+][-]
  1. ..
  2.     try
  3.       DLLInstance := SafeLoadLibrary(AFileName);
  4.       if DLLInstance <> NilHandle then
  5. ..
  6.     finally
  7.       FreeLibrary(DLLInstance);
  8.  
I think it's a little better to write this:
Code: Pascal  [Select][+][-]
  1. ..
  2.     DLLInstance := SafeLoadLibrary(AFileName);
  3.     if DLLInstance <> NilHandle then
  4.     try
  5. ..
  6.     finally
  7.       FreeLibrary(DLLInstance);
  8.  

 

TinyPortal © 2005-2018