Plugin For Take Command Console

Just starting out? Need help? Post your questions and find answers here.
JoeC4281
User
User
Posts: 29
Joined: Fri Aug 06, 2021 4:47 pm

Plugin For Take Command Console

Post by JoeC4281 »

This is a continuation of the thread viewtopic.php?f=7&t=77724

Objective: Create a template, using PureBasic, for a Take Command Console plugin.

As I am not interested in utilizing Take Command internals via a plugin at this time, I do not need;

#include "TakeCmd.h"

from PlugIn.cpp

The PlugIn.cpp code can be viewed at https://github.com/JoeC4281/PureBasicTC ... plugin.cpp
Note that this is the code that I am using as a basis for creating the PureBasic code.

Here's the PureBasic code (plugin.pb) I have developed, which is not doing what it is supposed to;

Code: Select all

; These 4 procedures are Windows specific
;

; This procedure is called once, when the program loads the library
; for the first time. All init stuffs can be done here (but not DirectX init)
;
ProcedureDLL AttachProcess(Instance)
EndProcedure
  
  
; Called when the program release (free) the DLL
;
ProcedureDLL DetachProcess(Instance)
EndProcedure
  
  
; Both are called when a thread in a program call or release (free) the DLL
;
ProcedureDLL AttachThread(Instance)
EndProcedure
  
ProcedureDLL DetachThread(Instance)
EndProcedure

Structure PLUGININFO_STRUCT
  pszDll.l
	pszAuthor.l
	pszEmail.l
	pszWWW.l
	pszDescription.l
	pszFunctions.l
	nMajor.l
	nMinor.l
	nBuild.l
EndStructure

ProcedureDLL.l InitializePlugin()
  OutputDebugString_("InitializePlugin")
  ProcedureReturn #Null  
EndProcedure

ProcedureDLL.l ShutdownPlugin(bEndProcess.b)
  OutputDebugString_("ShutdownPlugin")
  ProcedureReturn #Null  
EndProcedure

ProcedureDLL.l GetPluginInfo()
  OutputDebugString_("GetPluginInfo")
  Static pi.PLUGININFO_STRUCT
  Define DLLname.s
  Define DLLauth.s
  Define DLLmail.s
  Define DLLwww.s
  Define DLLdesc.s
  Define DLLfuns.s
  
  DLLname = "Plugin Dummy"
  DLLauth = "Joe Caverly"
  DLLmail = "jlcaverlyca@yahoo.ca"
  DLLwww  = "https://www.twitter.com/JoeC4281"
  DLLdesc = "TCC Plugin Template written using Purebasic"
  DLLfuns = "@Reverse"
  
  pi\pszDll         = @DLLname
  pi\pszAuthor      = @DLLauth
  pi\pszEmail       = @DLLmail
  pi\pszWWW         = @DLLwww
  pi\pszDescription = @DLLdesc
  pi\pszFunctions   = @DLLfuns
  
  pi\nMajor = 2021
  pi\nMinor = 8
  pi\nBuild = 7
  
  ; How do I return the PluginInfo to TCC?
  ProcedureReturn @pi
EndProcedure

ProcedureDLL.l f_Reverse(*pstrArgs)
  
  ; How do I get the text from *pstrArgs?
  ; ReverseString("Stuff") 
  ; How do I return the reversed string back via *pstrArgs?

  ProcedureReturn #Null
EndProcedure
Here is how I generate the .dll from the .pb source code;
"C:\Program Files\PureBasic\Compilers\pbcompiler.exe" /dll plugin.pb /exe "plugin.dll"

Code: Select all

******************************************
PureBasic 5.73 LTS (Windows - x64)
******************************************

Compiling plugin.pb
Loading external libraries...
Starting compilation...
82 lines processed.
Creating DLL "plugin.dll"

- Feel the ..PuRe.. Power -
Verify that a 64-bit DLL was generated;

Code: Select all

file plugin.dll
plugin.dll: PE32+ executable (DLL) (console) x86-64, for MS Windows
Verify that the DLL exports the functions;
dumpbin /exports plugin.dll

Code: Select all

Microsoft (R) COFF/PE Dumper Version 14.29.30133.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file plugin.dll

File Type: DLL

  Section contains the following exports for plugin.dll

    00000000 characteristics
    61282C10 time date stamp Thu Aug 26 20:04:32 2021
        0.00 version
           1 ordinal base
           4 number of functions
           4 number of names

    ordinal hint RVA      name

          1    0 00001169 GetPluginInfo
          2    1 000010E5 InitializePlugin
          3    2 00001107 ShutdownPlugin
          4    3 00001150 f_Reverse

  Summary

        1000 .code
        1000 .data
        1000 .pdata
        1000 .rdata
        1000 .reloc
        1000 .text
Load the plugin from the TCC Command Line;
plugin /l plugin.dll
DbgView.exe shows the functions that were accessed;

Code: Select all

[21976] InitializePlugin
[21976] GetPluginInfo
However, typing
plugin plugin
from the TCC Command Line shows that TCC does not see the plugin.

Note also that
plugin /u plugin
command does not unload the DLL.

Thus, even though GetPluginInfo was called, the results were not returned to TCC.

Links to the TCC Plugin SDK, and other TCC info at viewtopic.php?f=7&t=77724

Constructive suggestions would be appreciated on how to return the PluginInfo to TCC, return the results of the reverse string function, and get this working.

Thanks from Joe

Code: Select all

PureBasic 5.73 LTS (Windows - x64) - (c) 2020 Fantaisie Software

TCC  28.00.12 x64   Windows 10 [Version 10.0.19043.1165]

Processor	Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz   2.60 GHz  (2 processors)
Installed RAM	128 GB
Device ID	7334AD11-227D-4EA2-A424-EE5C735B7AF1
Product ID	00330-80000-00000-AA032
System type	64-bit operating system, x64-based processor
Pen and touch	No pen or touch input is available for this display

Edition	Windows 10 Pro
Version	21H1
Installed on	‎2021-‎01-‎16
OS build	19043.1165
Experience	Windows Feature Experience Pack 120.2212.3530.0
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: Plugin For Take Command Console

Post by Mijikai »

Have you ever tested my example beacuse the PLUGININFO_STRUCT structure i came up with looked way different?
Also what kind of strings is TCC expecting?
JoeC4281
User
User
Posts: 29
Joined: Fri Aug 06, 2021 4:47 pm

Re: Plugin For Take Command Console

Post by JoeC4281 »

Mijikai wrote: Fri Aug 27, 2021 11:41 am
Yes, thankyou, I tried your code, but it also did not work.

TCC expects a pointer to a Unicode String.

Here is an example from plugin.cpp (https://github.com/JoeC4281/PureBasicTC ... plugin.cpp);

Code: Select all

// this is a variable function called from TCC; it reverses the string argument
DLLExports INT WINAPI f_reverse( LPTSTR lpszString )
{
	if ( lpszString == NULL )
		return 1;

	_wcsrev( lpszString );
	return 0;
}
Unlike a regular DLL that would return a string, TCC requires the plugin DLL to return a pointer to a Unicode String.

Back in 2011, I was fortunate to have a fellow TCC user assist me in getting a 32-bit Plugin developed, using PowerBASIC.
https://jpsoft.com/forums/threads/writi ... post-13268

Unfortunately, there does not seem to be any other TCC users who use PureBasic for 64-bit TCC Plugin development.

Joe
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: Plugin For Take Command Console

Post by Mijikai »

Maybe this works:

Code: Select all

EnableExplicit

Import ""
  wprintf.i(format.s)
EndImport

Structure PLUGININFO_STRUCT
    *pszDll
	*pszAuthor
	*pszEmail
	*pszWWW
	*pszDescription
	*pszFunctions	
	nMajor.l
	nMinor.l
	nBuild.l
	hModule.i
	*pszModule
EndStructure

ProcedureDLL.i InitializePlugin()
  ProcedureReturn #Null  
EndProcedure

ProcedureDLL.i ShutdownPlugin(bEndProcess.b)
  ProcedureReturn #Null  
EndProcedure

ProcedureDLL.i GetPluginInfo()
  Static pi.PLUGININFO_STRUCT
  Static str_dll.s
  Static str_function.s
  str_dll = "Plugin Dummy"
  str_function = "Dummy"
  pi\pszDll = @str_dll
  pi\pszFunctions =  @str_function
  ProcedureReturn @pi
EndProcedure

ProcedureDLL.i Dummy()
  wprintf("Hello World!\r\n")
  ProcedureReturn #Null
EndProcedure
Optional try this with the code above:

Code: Select all

Structure PLUGININFO_STRUCT
  *pszDll
	*pszAuthor
	*pszEmail
	*pszWWW
	*pszDescription
	*pszFunctions	
	nMajor.i
	nMinor.i
	nBuild.i
	hModule.i
	*pszModule
EndStructure
JoeC4281
User
User
Posts: 29
Joined: Fri Aug 06, 2021 4:47 pm

Re: Plugin For Take Command Console

Post by JoeC4281 »

Mijikai wrote: Fri Aug 27, 2021 2:17 pm Maybe this works:
Many Thanks!

Your changes have helped me to learn more about PureBasic.

I am now able to load the plugin, display information about the plugin, and unload the plugin.

Code: Select all

******************************************
PureBasic 5.73 LTS (Windows - x64)
******************************************

Compiling plugin.pb
Loading external libraries...
Starting compilation...
106 lines processed.
Creating DLL "plugin.dll"

- Feel the ..PuRe.. Power -
Microsoft (R) COFF/PE Dumper Version 14.29.30133.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file plugin.dll

File Type: DLL

  Section contains the following exports for plugin.dll

    00000000 characteristics
    6128EE35 time date stamp Fri Aug 27 09:52:53 2021
        0.00 version
           1 ordinal base
           7 number of functions
           7 number of names

    ordinal hint RVA      name

          1    0 00001093 Dummy
          2    1 00001120 GetPluginInfo
          3    2 000010B7 InitializePlugin
          4    3 000010C8 ShutdownPlugin
          5    4 0000110A _hello
          6    5 000010DE f_PlusOne
          7    6 000010F4 f_Reverse

  Summary

        1000 .code
        1000 .data
        1000 .pdata
        1000 .rdata
        1000 .reloc
        1000 .text
Module:      E:\Documents\PureBasic\tcc\2021-08-27\plugin.dll
Name:        PBPlugin
Author:      Joe Caverly
Email:       jlcaverlyca@hayoo.ac
Web:         https://www.twitter.com/JoeC4281
Description: TCC Plugin Template written using Purebasic
Implements:  Dummy,@Reverse,@PlusOne,_hello
Version:     2021.8  Build 27
Here's my update to the code you provided;

Code: Select all

EnableExplicit

Import ""
  wprintf.i(format.s)
EndImport

Structure PLUGININFO_STRUCT
    *pszDll
	*pszAuthor
	*pszEmail
	*pszWWW
	*pszDescription
	*pszFunctions	
	nMajor.l
	nMinor.l
	nBuild.l
	hModule.i
	*pszModule
EndStructure

ProcedureDLL.i InitializePlugin()
  ProcedureReturn #Null  
EndProcedure

ProcedureDLL.i ShutdownPlugin(bEndProcess.b)
  ProcedureReturn #Null  
EndProcedure

ProcedureDLL.i GetPluginInfo()
  Static pi.PLUGININFO_STRUCT
  Static DLLname.s
  Static DLLauth.s
  Static DLLmail.s
  Static DLLwww.s
  Static DLLdesc.s
  Static DLLfuns.s
  
  DLLname = "PBPlugin"
  DLLauth = "Joe Caverly"
  DLLmail = "jlcaverlyca@yahoo.ca"
  DLLwww  = "https://www.twitter.com/JoeC4281"
  DLLdesc = "TCC Plugin Template written using Purebasic"
  DLLfuns = "Dummy,@Reverse,@PlusOne,_hello"
  
  pi\pszDll         = @DLLname
  pi\pszAuthor      = @DLLauth
  pi\pszEmail       = @DLLmail
  pi\pszWWW         = @DLLwww
  pi\pszDescription = @DLLdesc
  pi\pszFunctions	= @DLLfuns
  pi\nMajor.l       = 2021
  pi\nMinor.l       = 08
  pi\nBuild.l       = 27

  ProcedureReturn @pi
EndProcedure

; Command
ProcedureDLL.i Dummy(*lpszString)
  wprintf("Hello World!\r\n")
  ProcedureReturn #Null
EndProcedure

;// this is an internal command called from TCC
;DLLExports INT WINAPI remark( LPTSTR lpszString )
;{
;	wprintf( L"What a trivial sample PlugIn!\r\n" );
;	return 0;
;}

; Function
ProcedureDLL.i f_Reverse(*lpszString)
  ProcedureReturn #Null
EndProcedure

;// this is a variable function called from TCC; it reverses the string argument
;DLLExports INT WINAPI f_reverse( LPTSTR lpszString )
;{
;	if ( lpszString == NULL )
;		return 1;
;
;	_wcsrev( lpszString );
;	return 0;
;}


; Function
ProcedureDLL.i f_PlusOne(*lpszString)
  ProcedureReturn #Null
EndProcedure

; Internal Variable
ProcedureDLL.i _hello(*lpszString)
  ProcedureReturn #Null
EndProcedure

;// this is an internal variable called from TCC
;DLLExports INT WINAPI _hello( LPTSTR lpszString )
;{
;	if ( lpszString == NULL )
;		return 1;
;
;	lstrcpy( lpszString, L"Hello, PlugIn World!" );
;	return 0;
;}
The Dummy command outputs to the screen;

Code: Select all

e:\documents\purebasic\tcc\2021-08-27>dummy
Hello World!\r\n
Now, how would I go about accepting an argument to a Command/Function/Internal Variable?

The cpp code shows that a pointer is passed as an argument to the function, manipulated, and then returned as a pointer to the argument.
I have added the cpp code as comments in the pb code.

In PowerBasic, the pointer to the argument would be converted to an string, maniplulated, converted back to a unicode string, which was then copied back to the argument pointer.

Here is an example of how this was accomplished with PowerBasic;

Code: Select all

FUNCTION f_times2 ALIAS "f_TIMES2" (BYVAL pstrArgs AS WORD PTR) EXPORT AS DWORD
  DIM StrX AS STRING
  DIM SUReturn AS STRING
  DIM ValX AS EXT

  StrX = ACodeZ(pstrArgs)
  'STDOUT "Passed in: "  + StrX
  StrX = REMOVE$(StrX, ",")
  StrX = LTRIM$(StrX, ANY "$+ ")
  IF IsNumeric(StrX) THEN
    ValX = VAL(StrX)
    ValX = ValX * 2
    StrX = LTRIM$(STR$(ValX))
  ELSE
    ' Return 0 or whatever you prefer for incorrect input
    'StrX = "0"
    StrX = "*ERR_INVALID_INPUT*"
  END IF
  SUReturn = UCODE$(StrX) + CHR$(0,0)
  CALL MoveMemory(BYVAL pstrArgs, BYVAL STRPTR(SUReturn), LEN(SUReturn))
  FUNCTION = 0
END FUNCTION
I do not know how this would be accomplished in PureBasic, so constructive suggestions would be appreciated.

Joe
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: Plugin For Take Command Console

Post by Mijikai »

JoeC4281 wrote: Fri Aug 27, 2021 3:32 pm In PowerBasic, the pointer to the argument would be converted to an string, maniplulated, converted back to a unicode string, which was then copied back to the argument pointer.
That doesnt make much sense... how does one know how large the buffer inside TCC is ?

- Read the string with PeekS()
- Write it with anything that copies memory (PokeS(), lstrcpy(), CopyMemory(), CopyMemoryString(), ...)

Code: Select all

Procedure.i Dummy(*Parameter)
  PokeS(*Parameter,PeekS(*Parameter) + " <- ALRIGHT!")
  ProcedureReturn #Null
EndProcedure
JoeC4281
User
User
Posts: 29
Joined: Fri Aug 06, 2021 4:47 pm

Re: Plugin For Take Command Console

Post by JoeC4281 »

Mijikai wrote: Fri Aug 27, 2021 4:05 pm
JoeC4281 wrote: Fri Aug 27, 2021 3:32 pm
Thankyou again!

Here's the working code for a TCC Plugin;

Code: Select all

EnableExplicit

Import ""
  wprintf.i(format.s)
EndImport

Structure PLUGININFO_STRUCT
    *pszDll
	*pszAuthor
	*pszEmail
	*pszWWW
	*pszDescription
	*pszFunctions	
	nMajor.l
	nMinor.l
	nBuild.l
	hModule.i
	*pszModule
EndStructure

ProcedureDLL.i InitializePlugin()
  ProcedureReturn #Null  
EndProcedure

ProcedureDLL.i ShutdownPlugin(bEndProcess.b)
  ProcedureReturn #Null  
EndProcedure

ProcedureDLL.i GetPluginInfo()
  Static pi.PLUGININFO_STRUCT
  Static DLLname.s
  Static DLLauth.s
  Static DLLmail.s
  Static DLLwww.s
  Static DLLdesc.s
  Static DLLfuns.s
  
  DLLname = "PBPlugin"
  DLLauth = "Joe Caverly"
  DLLmail = "jlcaverlyca@yahoo.ca"
  DLLwww  = "https://www.twitter.com/JoeC4281"
  DLLdesc = "TCC Plugin Template written using Purebasic"
  DLLfuns = "Dummy,@Reverse,@PlusOne,_hello"
  
  pi\pszDll         = @DLLname
  pi\pszAuthor      = @DLLauth
  pi\pszEmail       = @DLLmail
  pi\pszWWW         = @DLLwww
  pi\pszDescription = @DLLdesc
  pi\pszFunctions	= @DLLfuns
  pi\nMajor.l       = 2021
  pi\nMinor.l       = 08
  pi\nBuild.l       = 27

  ProcedureReturn @pi
EndProcedure

; Command
ProcedureDLL.i Dummy(*lpszString)
  Static theString.s
  Static theValue.i
  
  theString = PeekS(*lpszString)
  
  wprintf(theString)
  
  ProcedureReturn #Null
EndProcedure

; Function
ProcedureDLL.i f_Reverse(*lpszString)
  Static theString.s
  Static theValue.i
  
  theString = PeekS(*lpszString)
  theString = ReverseString(theString)
  
  PokeS(*lpszString,theString)
  
  ProcedureReturn #Null
EndProcedure

; Function
ProcedureDLL.i f_PlusOne(*lpszString)
  Static theString.s
  Static theValue.i
  
  theString = PeekS(*lpszString)
  
  theValue = Val(theString)
  theValue = theValue + 1
  
  theString = Str(theValue)
  
  PokeS(*lpszString,theString)
  
  ProcedureReturn #Null
EndProcedure

; Internal Variable
ProcedureDLL.i _hello(*lpszString)
  PokeS(*lpszString,"Hello!")
  
  ProcedureReturn #Null
EndProcedure
All of the UDFs work as they should.

Thankyou for your explanation using PokeS and PeekS, makes things seem easier than they were in PowerBasic.

In answer to your question, the size of the *lpszString inside TCC is 32K.

Many, many thanks again for your guidance on creating a TCC Plugin using PureBasic.

Joe
JoeC4281
User
User
Posts: 29
Joined: Fri Aug 06, 2021 4:47 pm

Re: Plugin For Take Command Console

Post by JoeC4281 »

Mijikai wrote: Fri Aug 27, 2021 4:05 pm
JoeC4281 wrote: Fri Aug 27, 2021 3:32 pm
I've posted the working source code for the plugin on the JPSoftware Forums;
https://jpsoft.com/forums/threads/64-bi ... post-61866

Thanks to you, others can now create 64-bit plugins for TCC using PureBasic.

Regards,

Joe
JoeC4281
User
User
Posts: 29
Joined: Fri Aug 06, 2021 4:47 pm

Re: Plugin For Take Command Console

Post by JoeC4281 »

Posting this mainly for my future reference, but thought others might be interested.

As wprintf cannot be redirected, I used;
viewtopic.php?f=4&t=67329&p=573061&hili ... le#p573061

...as a reference to add the WinAPI GetStdHandle and WriteFile to my TCC plugin, instead of wprintf.

I've added the following to my template;

Code: Select all

Import ""
  GetStdHandle.i(nStdHandle.l)
  WriteFile.i(hConsoleOutput.l, lpBuffer.p-Ascii, nNumberOfCharsToWrite.l, *lpNumberOfCharsWritten, *lpReserved)
EndImport

Code: Select all

Procedure.i WriteConsoleN(String$)
  Protected Written.l
  String$ + #CRLF$
  WriteFile(GetStdHandle(#STD_OUTPUT_HANDLE), String$, Len(String$), @Written, #Null)
  ProcedureReturn Written
EndProcedure
Joe
Post Reply