PDA

View Full Version : Using thincore.dll with 64-bit applications



Joe Caverly
30-01-2025, 16:20
I have created COM DLLs using PowerBASIC,
and Visual Basic 6.0,
and Visual FoxPro 9.0,
that I use with 64-bit applications,
such as 64-bit PowerShell and 64-bit JScript.

I do this by creating a DLLSurrogate for my 32-bit COM DLLs.

Ref: https://jpsoft.com/forums/threads/32-bit-com-dll-surrogate-btm-64-bit-com-dll.11296/

It would be great if thincore.dll could be wrapped in a COM DLL,
so that thincore.dll could be used from 64-bit applications,
such as 64-bit PowerShell and 64-bit JScript,
via a DLLSurrogate.

Joe

ErosOlmi
30-01-2025, 20:56
Thanks a lot Joe.
I'm working to finish and release a new thinBasic version I'm working on
After that I will have a look and study at the method you described.

Joe Caverly
30-01-2025, 23:59
Here is PowerBASIC 10 source code to create a 32-bit COM DLL;

Make sure NOT to use these GUIDs in the code.
Replace the GUIDs with GUIDs that you generate on your own system.

#COMPILE DLL "pstest"
#DIM ALL
#REGISTER NONE
#RESOURCE TYPELIB, 1, "pstest.tlb"

#COM NAME "pstest", 1.0
#COM GUID GUID$("{D8D29A89-A3ED-4DE5-A101-2DAEDB367343}")
#COM TLIB ON

#INCLUDE "WIN32API.inc"

'32-bit regsvr32 /u pstest.dll
'Alt-F9 to compile
'32-bit regsvr32 pstest.dll
'
'Source code for pstest.vbs
'
'Dim Math: Set Math = CreateObject("pbUtils_clsMath")
'dim fso: set fso=CreateObject("Scripting.FileSystemObject")
'dim stdout: set stdout=fso.GetStandardStream(1)
'stdout.WriteLine Math.ppp(6.59)
'stdout.WriteLine Math.Plus2(0)
'stdout.WriteLine Math.Plus2(10)
'Set stdout = Nothing
'set fso = Nothing
'Set Math = Nothing

' 2.99185997486114
' 2
' 12
'*************************************************************************************************
CLASS pbUtils_clsMath GUID$("{F5786800-F9B6-4990-97D1-6DC07AAB9294}") AS COM
'*************************************************************************************************
'
'*************************************************************************************************
INTERFACE ifcMath GUID$("{A4316390-54E3-4D15-8613-BFBBC7A28EA3}")
INHERIT IDISPATCH
'*************************************************************************************************
METHOD ppp(ppk AS STRING) AS STRING
'*************************************************************************************************
'
'*************************************************************************************************

ppk = ACODE$(ppk)

METHOD = UCODE$(STR$(VAL(ppk) * 0.454))

END METHOD

'*************************************************************************************************

METHOD Plus2(theNumber AS STRING) AS STRING
'*************************************************************************************************
'
'*************************************************************************************************

theNumber = ACODE$(theNumber)

METHOD = UCODE$(STR$(VAL(theNumber) + 2))

END METHOD

'*************************************************************************************************

END INTERFACE

'*************************************************************************************************

END CLASS

'*************************************************************************************************
'*************************************************************************************************
'*************************************************************************************************

It is important that,
before compiling,
you un-register this 32-bit COM DLL,
using the 32-bit Registration Server,
located in C:\Windows\SysWOW64\regsvr32.exe


regsvr32.exe /s /u %FileName.dll

I do not use cmd.exe for my console.

Instead, I use TCC.exe (https://jpsoft.com/products/tcc.html) for my console.

Here is a batch file (.btm) that I created to;
unregister the 32-bit COM DLL,
compile the 32-bit COM DLL,
register the 32-bit COM DLL,
create a DLLSurrogate for the 32-bit COM DLL,
so that it can be used from 64-bit applications,
run a test 64-bit VBScript against the 32-bit COM DLL.


@setlocal
@echo off

set FileName=pstest
set basName=%FileName.bas
set ProgID=PBUtils_clsMath

REGDIR /D /F /P /V HKCR\%ProgID

Gosub RegUninstall

if exist pstest.log del /q pstest.log
echo Compiling %basName...
e:\pbwin10\bin\pbwin.exe /I"E:\pbwin10\winapi" /l /q %basName
iff %? ne 0 then
iff exist %FileName.log then
type %FileName.log
else
echo Could not find %FileName.log
endiff
quit
else
dir %FileName.dll
endiff

Gosub RegInstall

REGDIR /D /F /P /V HKCR\%ProgID

Gosub MainProc

drawhline %_row 0 80 2 green on black
echo.

:: Use CScript.exe instead of script
:: if not using this .BTM from TCC
script pstest.vbs
endlocal
quit

:MainProc
alias ClsID=`echo %@regquery[HKEY_CLASSES_ROOT\%ProgID\clsid\]`
function ClsID=`%@regquery[HKEY_CLASSES_ROOT\%ProgID\clsid\]`

iff %@ClsID[%ProgID] eq -1 then
echo %ProgID is an invalid ProgID
quit
endiff

drawhline %_row 0 80 2 green on black
echo.
echo Creating .reg file with DllSurrogate for Registry

Gosub RegScript

drawhline %_row 0 80 2 green on black
echo.

type surrogate.reg

%_wow64dir\regedit.exe surrogate.reg
Return

:RegScript
type <<- endtext > surrogate.reg
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\AppID\%@ClsID[%ProgID]]
"DllSurrogate"=""

[HKEY_CLASSES_ROOT\CLSID\%@ClsID[%ProgID]]
"AppID"="%@ClsID[%ProgID]"
endtext
Return

:RegUnInstall
drawhline %_row 0 80 2 green on black
echo.

%_wow64dir\regsvr32.exe /s /u %FileName.dll
iff %? eq 0 then
echo pstest.dll has been un-registered.
else
echo Error with regsvr32 /u pstest.dll
quit
endiff

drawhline %_row 0 80 2 green on black
echo.

Return

:RegInstall
drawhline %_row 0 80 2 green on black
echo.

%_wow64dir\regsvr32.exe /s %FileName.dll
iff %? eq 0 then
echo %FileName.dll has been registered.
else
echo Error with regsvr32 %FileName.dll
quit
endiff

drawhline %_row 0 80 2 green on black
echo.

Return

This is a very simple example.

Hoping that you can use it as a starting point to turn thincore.dll into ThinCoreCOM.dll

Joe

ErosOlmi
02-02-2025, 10:59
Thanks Joe,

yes, I tested that method some years ago to wrap thinCore.dll into a thinCoreX.dll COM library, but I had some problems still not resolved.
As soon as I will release current new thinBasic version I'm working on, I will retry it and post here sources, so we can work together maybe.

Ciao
Eros

Joe Caverly
03-02-2025, 20:11
Hi Eros,
No rush with this,
I know that you are busy,
but here's the PowerBASIC 10 code that I have created
which wraps the thincore.dll into a COM file,
which I can call from a 64-bit application.

This is just a concept, work-in-progress.

Much more needs to be added to make it truly useful.


#COMPILE DLL "thinCoreCOM"

#DIM ALL
#REGISTER NONE


#RESOURCE TYPELIB, 1, "thinCoreCOM.tlb"


#COM NAME "thinCoreCOM", 1.0
#COM GUID GUID$("{CC2EED1A-FA8D-4C21-8CE8-4647ED56D11D}")
#COM TLIB ON


#INCLUDE "Win32Api.inc"
#INCLUDE "ThinCore.inc"


'32-bit regsvr32 /u thinCoreCOM.dll
'Alt-F9 to compile
'32-bit regsvr32 thinCoreCOM.dll
'surrogate.btm thinCore_COMClass
'
'Dim tc
'Set tc = CreateObject("thinCore_COMClass")
'WScript.Echo tc.tcInit()
'WScript.Echo tc.tcRunScript("U:\kount.tbasicc")
'WScript.Echo tc.tcRelease()
'Set tc = Nothing
'*************************************************************************************************
'
' wrapper for OutputDebugString
SUB OutputDebug(strMsg AS STRING) EXPORT
LOCAL strDebugMsg AS STRING


strDebugMsg = strMsg & CHR$(13, 10)
OutputDebugString BYCOPY strDebugMsg
' Use DebugView from SysInternals to view OutputDebugString
END SUB


CLASS thinCore_COMClass GUID$("{9611C234-B1AD-4901-9F31-8580FDE604A6}") AS COM
'*************************************************************************************************
'
'*************************************************************************************************
INTERFACE ifcthinCore GUID$("{0672D338-45B5-433D-AC87-65E119F48DCC}")
INHERIT IDISPATCH
'*************************************************************************************************


'METHOD ppp(ppk AS STRING) AS STRING
''*************************************************************************************************
''
''*************************************************************************************************
' ppk = ACODE$(ppk)
'
' METHOD = UCODE$(STR$(VAL(ppk) * 0.454))
'END METHOD
''*************************************************************************************************
'
'METHOD Plus2(theNumber AS STRING) AS STRING
''*************************************************************************************************
''
''*************************************************************************************************
' theNumber = ACODE$(theNumber)
'
' METHOD = UCODE$(STR$(VAL(theNumber) + 2))
'END METHOD




'*************************************************************************************************
' Initialize the thinCore engine
METHOD tcInit() AS BYTE
OutputDebug "[PBWIN] thinCore_Init()"
thinBasic_Init(0, 0, "thinbasic")
METHOD = 1
END METHOD


' Run a thinBasic script
METHOD tcRunScript(theScript AS STRING) AS BYTE
theScript = ACODE$(theScript)
OutputDebug "[PBWIN] thinBasic_RunScript(theScript)"
OutputDebug "[PBWIN] theScript: " + theScript
thinBasic_Run(0&, theScript, %thinBasic_BufferType_IsFile,0,%FALSE, %FALSE, %FALSE, 1&,%FALSE)
OutputDebug "[PBWIN] thinBasic_GetLastError()" + STR$(thinBasic_GetLastError())
METHOD = 1
END METHOD


' Release the thinCore engine
METHOD tcRelease() AS BYTE
OutputDebug "[PBWIN] thinCore_Release()"
thinBasic_Release(0)
METHOD = 1
END METHOD




END INTERFACE
'*************************************************************************************************


END CLASS
'*************************************************************************************************
'*************************************************************************************************
'*************************************************************************************************

I have a thinBASIC script;

uses "console"
dim kount as integer
FOR kount = 1 TO 10
PRINTl kount
NEXT kount

...which runs fine from the console;

E:\...\thincore>e:\thinbasic\thinbasicc.exe u:\kount.tbasicc
1
2
3
4
5
6
7
8
9
10

This is my VBScript code,
to use the thinCore COM Wrapper,

E:\...\thincore>type tctest.vbs
Dim tc
Set tc = CreateObject("thinCore_COMClass")
WScript.Echo tc.tcInit()
WScript.Echo tc.tcRunScript("U:\kount.tbasicc")
WScript.Echo tc.tcRelease()
Set tc = Nothing

...which runs fine using 32-bit cscript.exe


E:\...\thincore>cscript32 tctest.vbs
1
2
3
4
5
6
7
8
9
10


E:\...\thincore>which cscript32
cscript32 is an alias : C:\Windows\SysWOW64\cscript.exe //nologo %$

When I run it using the 64-bit cscript.exe,
it runs, but nothing is output.

E:\...\thincore>cscript.exe //nologo tctest.vbs
1
1
1

Output from DebugView...

[13988] [PBWIN] thinCore_Init()
[13988] [PBWIN] thinBasic_RunScript(theScript)
[13988] [PBWIN] script$: U:\kount.tbasicc
[13988] [PBWIN] thinBasic_GetLastError() 0
[13988] [PBWIN] thinCore_Release()

So, no errors, and my kount.tbasicc script executes,
and produces the output,
when run via the thinCore Com Wrapper,
using a 32-bit application,
such as 32-bit cscript.exe

The problem is running it from a 64-bit app,
as the program is run,
but no output is produces.

I'm not sure what is going on,
but preliminary debugging points to the use of the DLLSurrogate.

I have other PowerBASIC COM Servers that I have developed,
and I have had no problem with them being called from a 64-bit app.

I think the best direction to take,
would be to put the source code from thinCore.dll
directly into thinCoreCOM.bas
and eliminate the thinCore.dll completly.

Not now,
but when you have time.

As it is,
I can now run a thinBASIC script from
32-bit Visual Basic 6.0,
32-bit VBScript,
32-bit JScript,
32-bit Visual FoxPro 9.0,
32-bit PowerShell,
via the thinCoreCOM COM Server,
which is what I have wanted to do for some time now.

Joe

Joe Caverly
03-02-2025, 20:25
The DLLSurrogate applies only to the COM dll it is assigned in the registry.

In this case,
thinCoreCOM.DLL

thinCoreCOM.DLL is treated as a 64-bit dll now,
but when thinCoreCOM.DLL attempts to call a 32-bit dll,
in this case thinCore.dll,
it cannot.

Thus,
all of the thinCore.dll code will have to be placed into thinCoreCOM.DLL,
so that thinCoreCOM.DLL does not have to call an external dll.

Joe

Joe Caverly
03-02-2025, 21:39
No, I was incorrect.

The 32-bit thinCore.dll can be called from the 64-bit thinCoreCOM.dll

I put a WAITKEY in my thinBASIC code,
which showed that the c:\windows\SysWOW64\DLLHost.exe is called,
a new console is created,
and the output is displayed there,
not on the console from where 64-bit CScript.exe was started.

Without the WAITKEY,
it was so fast,
that I never saw what was happening.

Not sure why a new console is created when running from 64-bit CScript.exe,
but the existing console is used with 32-bit CScript.exe.

Still, having all of the code in one place,
thinCoreCOM.dll,
should hopefully fix the console issue with 64-bit activation.

Joe

Joe Caverly
03-02-2025, 22:04
Of course!

When running the thinCoreCOM.dll from a 32-bit application,
no dllSurrogate is required,
which is why output goes to the console from which CScript.exe was started.

When running the thinCoreCOM.dll from a 64-bit application,
a dllSurrogate is required,
which is why output does not go to the console from which CScript.exe was started,
and instead,
goes to a new console that has to be created.

Joe

Joe Caverly
04-02-2025, 20:51
I've added a new method to thinCoreCOM...

' Run a thinBasic scriptline
METHOD tcRunScriptLine(theScript AS STRING)
theScript = ACODE$(theScript)
OutputDebug "[PBWIN] thinBasic_RunScriptLine(theScript)"
OutputDebug "[PBWIN] thescript: " + theScript
thinBasic_Run(0&, theScript, %thinBasic_BufferType_IsScript,0,%FALSE, %FALSE, %FALSE, 1&,%FALSE)
OutputDebug "[PBWIN] thinBasic_GetLastError()" + STR$(thinBasic_GetLastError())
END METHOD

As an example, from VBScript, I can do the following...

WScript.Echo tc.tcRunScriptLine("uses " + CHR(34) + "console" + CHR(34) + " : dim a as long = 10 : print a")
...which displays 10 on the console.

DebugView output...

[13104] [PBWIN] thinCore_Init()
[13104] [PBWIN] thinBasic_RunScriptLine(theScript)
[13104] [PBWIN] thescript: uses "console" : dim a as long = 10 : print a
[13104] [PBWIN] thinBasic_GetLastError() 0
[13104] [PBWIN] thinCore_Release()

Joe

Joe Caverly
04-02-2025, 20:55
In my build.btm I have changed to using the 32-bit CScript.exe instead of the 64-bit CScript.exe

:: which cscript32
:: cscript32 is an alias : C:\Windows\SysWOW64\cscript.exe //nologo %$
cscript32 tcTest.vbs

Joe