Jump to content

[Solved] Call C Library from AutoIt?


Recommended Posts

Hi!

I am just getting started with C and C++. I have created a pretty simple C code which is calling a dll function.

Spoiler

#include <iostream>
#include <sapnwrfc.h>

int mainU(int argc, SAP_UC** argv) {
	RFC_RC rc = RFC_OK;
	RFC_CONNECTION_PARAMETER loginParams[6];
	RFC_ERROR_INFO errorInfo;
	RFC_CONNECTION_HANDLE connection;
	unsigned parameterCount = 0, fieldCount = 0;
	
	loginParams[0].name = L"ashost";	loginParams[0].value = L"xxxxx";
	loginParams[1].name = L"sysnr";		loginParams[1].value = L"30";
	loginParams[2].name = L"client";	loginParams[2].value = L"150";
	loginParams[3].name = L"user";		loginParams[3].value = L"xxxx";
	loginParams[4].name = L"lang";		loginParams[4].value = L"EN";
	loginParams[5].name = L"passwd";	loginParams[5].value = L"xxxxx";
	
	connection = RfcOpenConnection(loginParams, 6, &errorInfo);
	if (connection == NULL) {
		printfU(L"Error during logon: %s\n%s: %s\n", RfcGetRcAsString(errorInfo.code),
			errorInfo.key, errorInfo.message);
		return 1;
	}

	return 0;
}

 

When I compile and run, I get the appropriate Output. So it works fine.

image.png.5af42934c8032d929fe4a46acc5871bc.png

 

Now I would want to transform that to AutoIt. -> I would like to call the "RfcOpenConnection" function from AutoIt - but whatever I try with DLLCall, I can not get it to work. 

Can someone point me in the right direction? DLL, C Sourcecode and compiled exe are attached too large to be attached, so they're uploaded here: 

https://drive.google.com/file/d/12CUSsISl0mojiMCNxKjps1Sdoox3JlCX/view?usp=sharing

 

Thanks a bunch!

Edited by SEuBo
Link to post
Share on other sites

This will be a lot of work for translating this functions, structures, types and enums to AutoIt. Will not be to difficult, but consumes more time than I have.

Hope someone has already done this for you, but I don't think so.

Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs, and the Universe
trying to produce bigger and better idiots.
So far, the Universe is winning.

Link to post
Share on other sites

Yes, I also dont think someone already has done such a thing. 
It would be a highly useful thing in context of SAP & AutoIt Connectivity though.

Would you have some guidance on how I would be translating those functions? I probably won't utilize the whole functionality and I assume a lot of the stuff is for backwards-compability. I'd be willing to work into that topic, but I am a bit lost with all the information available online. I am a complete C beginner so it's hard to follow some technical discussions; I barely managed to compile this simple C code and spent 3 days until I've added the right compiler and linker directives.


..This is when you appreciate the easiness of AutoIt.

Link to post
Share on other sites

Thanks @RTFC

Just in to make sure I understand the approach right:

  • I would write a C++ Dll to "wrap" the C functions that are provided in sapnwrfc.h (and thus in the sapnwrfc.dll?) ? 
  • I would define those functions as "extern c"
  • I would compile it to DLL (e.g. "AutoItSapnwrfc"), would include the AutoItSapnwrfc.dll and sapnwrfc.dll + (probably the others?) into my autoit script folder and do the DLLCall on my AutoItSapnwrfc.dll?

Is there no way to "directly" call the sapnwrfc.dll-functions? Isn't it already a dll?

Sorry for the stupid questions. 

Thanks,

Edited by SEuBo
Link to post
Share on other sites
23 hours ago, SEuBo said:

Sorry for the stupid questions. 

Never apologise for asking questions (and they're not stupid at all).;) The answer is "yes" to all three questions; the dll itself can live anywhere, as long as you supply the correct path when calling DllOpen (ensure using cdecl calling convention here, and don't forget to close the dll again after you're done calling it; note that some variable types have a different name in AutoIt).

23 hours ago, SEuBo said:

Is there no way to "directly" call the sapnwrfc.dll-functions? Isn't it already a dll?

The C code you posted (which produces an executable) is including the functions directly, not as an external dll, which instead would provide a lookup table for other code (e.g., in other languages) to find a specific function by name (hence the "extern" declaration in the C code, so the function names don't get decorated to make them internally unique).

Side note: It is possible to access an external dll from C as well, but you'd have to provide a typedef for a function-specific method for each function you wish to call (with the correct parameters, in the right order!). Then you'd call that method instead of the dll function.

Edited by RTFC
Link to post
Share on other sites
  • 2 weeks later...

Hey there,

Thanks again for the response - and apologies that my answer is a bit late.

On 2/12/2021 at 11:32 AM, RTFC said:

The C code you posted (which produces an executable) is including the functions directly, not as an external dll, which instead would provide a lookup table for other code (e.g., in other languages) to find a specific function by name (hence the "extern" declaration in the C code, so the function names don't get decorated to make them internally unique).

My understanding is now the following, please correct me: 

In my C++ code, I am currently not actually "using" the dll file, since I am including the sapnwrfc.h (which is the function definition) and the sapnwrfc.lib (which is the actual function "coding"?).  -- Or is the C++ code still "including" the functions but the logic resides in sapnwrfc.dll and sapnwrfc.lib is just some kind of placeholder?

 

On 2/12/2021 at 11:32 AM, RTFC said:

Never apologise for asking questions (and they're not stupid at all).;) The answer is "yes" to all three questions; the dll itself can live anywhere, as long as you supply the correct path when calling DllOpen (ensure using cdecl calling convention here, and don't forget to close the dll again after you're done calling it; note that some variable types have a different name in AutoIt).

I think I've got that. The problem is, that even a simple DLLOpen(...) with the sapnwrfc.dll from autoit doesn't work. I didn't even get to try to call a function from it.

What is the reason for this? Is there some kind of difference between C or C++ DLLs? or are some DLL's just for internal use?

 

On 2/12/2021 at 11:32 AM, RTFC said:

Side note: It is possible to access an external dll from C as well, but you'd have to provide a typedef for a function-specific method for each function you wish to call (with the correct parameters, in the right order!). Then you'd call that method instead of the dll function.

so then I would "re-route" from my C method call directly into the DLL, so to say?

 

Again - a lot of noobish questions.

Cheers,

Link to post
Share on other sites

Here's an easy explainer of the difference between static (.lib) and dynamic (.dll) libraries.

Basically, a static lib is permanently incorporated into the executable, and cannot be changed independently (except when recompiling the library itself, and then recompiling the exe with the new lib), whereas a dll is stand-alone and can be accessed (NB with the correct calling convention, which determines stack-order of the function arguments parsed) by any programme that opens the dll and calls its externally-available functions with appropriate parameters. An exe with static lib does not need/use the dll. The advantage of dlls is portable, independently-upgradeable code accessible from multiple languages.

In order to get your dll working you need to know for each function used which parameters to parse, of which type, and in what order. You also need to know the return type of any result parsed back to the calling program (to interpret it correctly). some functions return a result directly, but others expect a memory address of a buffer where the function is to write its output. Key is to obtain the dll documentation on this. Of course, if you have the library's source code, you can just look this up directly.

Re. your last question: correct.

If you can't even open your dll for access then something else is wrong (@error code??), maybe you're mixing 32/64-bit? if the dll is x64, ensure the AutoIt interpreter is running as an x64 process too (default is 32-bit).

Edited by RTFC
Link to post
Share on other sites
3 hours ago, RTFC said:

maybe you're mixing 32/64-bit? if the dll is x64, ensure the AutoIt interpreter is running as an x64 process too (default is 32-bit).

🤩 Oh my god. I can't believe it was such a stupid error.
/Edit: And also thank you for the other explanation.

Edited by SEuBo
Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By bogQ
      Simple script latest autoit version.
      #include <GUIConstantsEx.au3> #include <FontConstants.au3> Example() Func Example() GUICreate("test", 800, 540) GUISetFont(12, $FW_NORMAL, $GUI_FONTNORMAL) GUICtrlCreateLabel("testing",680,310) GUISetState(@SW_SHOW) Do Until GUIGetMsg() = $GUI_EVENT_CLOSE GUIDelete() EndFunc ;==>Example After using GUISetFont(12) or GUISetFont(12, $FW_NORMAL, $GUI_FONTNORMAL) every GUI control is changed to italic.
       
      Am i doing something wrong?

    • By woffi
      Hi,
      I'm afraid I'm just stupid or blind or both: how can I read user input from an AutoIt console program? Just a simple String input, terminated with pressing "Return"? 

      This can't be difficult, but I can't find a solution.
    • By WhaleJesus
      #include <FileConstants.au3> #include <MsgBoxConstants.au3> #include <file.au3> ; Create Data Folder if it doesn't exist yet If FileExists(@ScriptDir & "\Data") Then Else ShellExecute(@ScriptDir) DirCreate(@ScriptDir & "\Data") EndIf ; Playlist Name & location input Global $playlistnameinput = InputBox("Playlist", "Enter The playlist name", _ "Name") Global $playlistlocationinput = InputBox("Location", "Specify where you would like the playlist folder to be stored", @ScriptDir & "\Playlists\" & $playlistnameinput) ; Create file in Data folder and other vars Global $sDataFile = @ScriptDir & "\Data\Data.txt" Global $DataHandle = FileOpen($sDataFile, 1) Global $DataFileLine = FileReadLine($sDataFile, 1) FileClose($DataFileLine) MsgBox(0, "", $DataFileLine, 10) ; Prove it exists If FileExists($sDataFile) Then _FileWriteToLine($DataHandle, $DataFileLine, $playlistnameinput, True, True) $DataFileLine += 1 _FileWriteToLine($DataHandle, 1, $DataFileLine, True) Else MsgBox($MB_SYSTEMMODAL, "Error", "File " & $sDataFile & "Does not exist") EndIf Global $sPDataFile = @ScriptDir & "\Data\" & $playlistnameinput & "_Data.txt" Global $PDataHandle = FileOpen($sPDataFile, 1) If FileExists($sPDataFile) Then _FileWriteToLine($PDataHandle, 1, $playlistnameinput, True, True) _FileWriteToLine($PDataHandle, 2, $playlistlocationinput, True, True) Else MsgBox($MB_SYSTEMMODAL, "Error", "File " & $sPDataFile & "Does not exist") EndIf _FileWriteToLine stopped working and i don't know what it is in my code that's causing this, please help
    • By DannyJ
      $sCommands1 = 'powershell.exe Get-ChildItem' $iPid = run($sCommands1   , @WorkingDir , @SW_SHOW , 0x2) $sOutput = ""  While 1     $sOutput &= StdoutRead($iPID)         If @error Then             ExitLoop         EndIf  WEnd ;~ msgbox(0, '' , $sOutput) ConsoleWrite("$sOutput") ConsoleWrite($sOutput) ConsoleWrite(@CRLF) $aOutput = stringsplit($sOutput ,@LF , 2) For $i=0 To  UBound($aOutput) - 1 Step 1     ConsoleWrite($aOutput[$i]) Next The script above reads the whole directory into a one dimensional array, but I need to work with the array, so I need to split the array into multiple dimensions.
      I have already read some forum answers here, and I have already tried these commands:
       
      Are there any way to use the $aOutput variable like in PowerShell:
      PowerShell:
      $a = Get-ChildItem $a.Mode I imagine this in AutoIt  $aOutput
      ConsoleWrite($aOutput[i].Mode) Or if I split this command into 2 dimension like:
      For $i To UBound($aOutput)-1 Step 1 ConsoleWrite($aOutput[$i][1]) ConsoleWrite($aOutput[$i][2]) Next  
    • By DannyJ
      If I run this code, it works perfectly
      $CmdPid = Run("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noexit " & 'Get-ChildItem',@DesktopDir, @SW_SHOW) But this code
      $CmdPid = Run("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noexit " & 'Get-RDUserSession',@DesktopDir, @SW_SHOW) I get this error:
      Get-RDUserSession : The term 'Get-RDUserSession' is not recognized as the name of a cmdlet, function, script file, or o perable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try aga in. If I try run the command Get-RDUserSession  in normal PowerShell (started from windows start menu) the command works perfectly.
      But If I run with AutoIt I get the above mentioned error .
      Any ideas?
×
×
  • Create New...