https://www.purebasic.fr/english/viewtopic.php?p=540316
and sample code plus some fairly intensive research over the last week or so, I'm now able to call a method from a managed class within a .NET assembly created in C# with Visual Studio.
The code for my test .NET assembly (built in VS2022 17.2.5) and the .DLL are included below, along with the PB main code I'm using to open the DLL, create an instance of the Class and invoke the Method. Using the pythonnet module I have confirmed that the .NET Method is functioning as it should in Python (script below).
Parameters are passed to the method via a SafeArray structure. The .NET function return value is passed back as the return value from InvokeMember(). So far, so good.
However, I'm not getting back the modified arguments for parameters passed by reference and I don't know how to solve this. Initially, I expected the same SafeArray holding the In arguments would be automatically updated with the Out arguments by the CLR hosting, but this does not happen.
Does anyone have any thoughts on how to solve this please?
C# Source
Code: Select all
using System;
using System.Windows.Forms;
namespace MyNameSpace
{
public class MyClass
{
public static int MyMethod(ref int Param1, ref int Param2)
{
Param1++;
Param2 = 1234;
return Param1;
}
}
}
https://www.dropbox.com/s/mciprpplkv3ae ... y.dll?dl=0
PureBasic Include file clr.pbi
Code: Select all
EnableExplicit
Interface ICLRMetaHost Extends IUnknown
GetRuntime.l(wstr,struct,ptr)
GetVersionFromFile.l(ptr,ptr,ptr)
EnumerateInstalledRuntimes.l(ptr)
EnumerateLoadedRuntimes.l(ptr,ptr)
RequestRuntimeLoadedNotification.l(ptr,ptr,ptr)
QueryLegacyV2RuntimeBinding.l(ptr,ptr)
ExitProcess.l(INT.l)
EndInterface
Interface ICLRRuntimeInfo Extends IUnknown
GetVersionString.l(ptr,ptr)
GetRuntimeDirectory.l(ptr,ptr)
IsLoaded.l(ptr,ptr)
LoadErrorString.l(ptr,ptr,ptr,ptr)
LoadLibrary.l(ptr,ptr)
GetProcAddress.l(ptr,ptr)
GetInterface.l(ptr,ptr,ptr)
IsLoadable.l(*Bool)
SetDefaultStartupFlags.l(ptr,ptr)
GetDefaultStartupFlags.l(ptr,ptr,ptr)
BindAsLegacyV2Runtime.l()
IsStarted.l(ptr,ptr)
EndInterface
Interface ICLRRuntimeHost Extends IUnknown
Start.l()
Stop.l()
SetHostControl.l(ptr)
GetCLRControl.l(*ptr)
UnloadAppDomain.l(ptr,ptr)
ExecuteInAppDomain.l(ptr,ptr,ptr)
GetCurrentAppDomainId.l(ptr)
ExecuteApplication.l(ptr,ptr,ptr,ptr,ptr,ptr)
ExecuteInDefaultAppDomain.l(wstr,wstr,wstr,wstr,*ptr)
EndInterface
Interface ICorRuntimeHost Extends IUnknown
CreateLogicalThreadState.l()
DeleteLogicalThreadState.l()
SwitchInLogicalThreadState.l()
SwitchOutLogicalThreadState.l()
LocksHeldByLogicalThread.l()
MapFile.l()
GetConfiguration.l()
Start.l()
Stop.l()
CreateDomain.l()
GetDefaultDomain.l(*ptr)
EnumDomains.l()
NextDomain.l()
CloseEnum.l()
CreateDomainEx.l()
CreateDomainSetup.l()
CreateEvidence.l()
UnloadDomain.l()
CurrentDomain.l()
EndInterface
Interface AppDomain Extends IDispatch
get_ToString.l()
Equals.l()
GetHashCode.l()
GetType.l(ptr)
InitializeLifetimeService.l()
GetLifetimeService.l()
get_Evidence.l()
add_DomainUnload.l()
remove_DomainUnload.l()
add_AssemblyLoad.l()
remove_AssemblyLoad.l()
add_ProcessExit.l()
remove_ProcessExit.l()
add_TypeResolve.l()
remove_TypeResolve.l()
add_ResourceResolve.l()
remove_ResourceResolve.l()
add_AssemblyResolve.l()
remove_AssemblyResolve.l()
add_UnhandledException.l()
remove_UnhandledException.l()
DefineDynamicAssembly.l()
DefineDynamicAssembly_2.l()
DefineDynamicAssembly_3.l()
DefineDynamicAssembly_4.l()
DefineDynamicAssembly_5.l()
DefineDynamicAssembly_6.l()
DefineDynamicAssembly_7.l()
DefineDynamicAssembly_8.l()
DefineDynamicAssembly_9.l()
CreateInstance.l()
CreateInstanceFrom.l()
CreateInstance_2.l()
CreateInstanceFrom_2.l()
CreateInstance_3.l()
CreateInstanceFrom_3.l()
Load.l()
Load_2.l(bstr,*ptr)
Load_3.l(ptr,*ptr)
Load_4.l()
Load_5.l()
Load_6.l()
Load_7.l()
ExecuteAssembly.l()
ExecuteAssembly_2.l()
ExecuteAssembly_3.l()
get_FriendlyName.l()
get_BaseDirectory.l()
get_RelativeSearchPath.l()
get_ShadowCopyFiles.l()
GetAssemblies.l()
AppendPrivatePath.l()
ClearPrivatePath.l()
SetShadowCopyPath.l()
ClearShadowCopyPath.l()
SetCachePath.l()
SetData.l()
GetData.l()
SetAppDomainPolicy.l()
SetThreadPrincipal.l()
SetPrincipalPolicy.l()
DoCallBack.l()
get_DynamicDirectory.l()
EndInterface
Interface IAssembly Extends IDispatch
get_ToString.l(*bstr)
Equals.l()
GetHashCode.l()
GetType.l(*ptr)
get_CodeBase.l()
get_EscapedCodeBase.l()
GetName.l()
GetName_2.l()
get_FullName.l(*bstr)
get_EntryPoint.l(*ptr)
GetType_2.i(bstr.p-bstr, *type)
GetType_3.l()
GetExportedTypes.l()
GetTypes.l(*ptr)
GetManifestResourceStream.l()
GetManifestResourceStream_2.l()
GetFile.l()
GetFiles.l()
GetFiles_2.l()
GetManifestResourceNames.l()
GetManifestResourceInfo.l()
get_Location.l()
get_Evidence.l()
GetCustomAttributes.l()
GetCustomAttributes_2.l()
IsDefined.l()
GetObjectData.l()
add_ModuleResolve.l()
remove_ModuleResolve.l()
GetType_4.l()
GetSatelliteAssembly.l()
GetSatelliteAssembly_2.l()
LoadModule.l()
LoadModule_2.l()
CreateInstance.l(bstr,*variant)
CreateInstance_2.l(bstr.p-bstr,bool.l,*variant)
CreateInstance_3.l(bstr,bool.l,INT.l,ptr,ptr,ptr,ptr,*variant)
GetLoadedModules.l()
GetLoadedModules_2.l()
GetModules.l()
GetModules_2.l()
GetModule.l()
GetReferencedAssemblies.l()
get_GlobalAssemblyCache.l()
EndInterface
Interface IMethodInfo Extends IDispatch
ToString.l()
Equals.l()
GetHashCode.l()
GetType.l()
MemberType.l()
name.l(*bstr)
DeclaringType.l()
ReflectedType.l()
GetCustomAttributes.l()
GetCustomAttributes_2.l()
IsDefined.l()
GetParameters.l()
GetMethodImplementationFlags.l()
MethodHandle.l()
Attributes.l()
CallingConvention.l()
Invoke_2.l()
IsPublic.l()
IsPrivate.l()
IsFamily.l()
IsAssembly.l()
IsFamilyAndAssembly.l()
IsFamilyOrAssembly.l()
IsStatic.l()
IsFinal.l()
IsVirtual.l()
IsHideBySig.l()
IsAbstract.l()
IsSpecialName.l()
IsConstructor.l()
Invoke_3.l(ptr,ptr,ptr,ptr,ptr,ptr)
returnType.l()
ReturnTypeCustomAttributes.l()
GetBaseDefinition.l()
EndInterface
Interface IType Extends IDispatch
get_ToString(bstr)
Equals(variant,short)
GetHashCode(INT)
GetType(ptr)
get_MemberType(ptr)
get_name(bstr)
get_DeclaringType(ptr)
get_ReflectedType(ptr)
GetCustomAttributes(ptr,short,ptr)
GetCustomAttributes_2(short,ptr)
IsDefined(ptr,short,short)
get_Guid(ptr)
get_Module(ptr)
get_Assembly(ptr)
get_TypeHandle(ptr)
get_FullName(bstr)
get_Namespace(bstr)
get_AssemblyQualifiedName(bstr)
GetArrayRank(INT)
get_BaseType(ptr)
GetConstructors(ptr,ptr)
GetInterface(bstr,short,ptr)
GetInterfaces(ptr)
FindInterfaces(ptr,variant,ptr)
GetEvent(bstr,ptr,ptr)
GetEvents(ptr)
GetEvents_2(INT,ptr)
GetNestedTypes(INT,ptr)
GetNestedType(bstr,ptr,ptr)
GetMember(bstr,ptr,ptr,ptr)
GetDefaultMembers(ptr)
FindMembers(ptr,ptr,ptr,variant,ptr)
GetElementType(ptr)
IsSubclassOf(ptr,short)
IsInstanceOfType(variant,short)
IsAssignableFrom(ptr,short)
GetInterfaceMap(ptr,ptr)
GetMethod(bstr,ptr,ptr,ptr,ptr,ptr)
GetMethod_2(bstr,ptr,ptr)
GetMethods(INT,ptr)
GetField(bstr,ptr,ptr)
GetFields(INT,ptr)
GetProperty(bstr,ptr,ptr)
GetProperty_2(bstr,ptr,ptr,ptr,ptr,ptr,ptr)
GetProperties(ptr,ptr)
GetMember_2(bstr,ptr,ptr)
GetMembers(INT,ptr)
InvokeMember(bstr,ptr,ptr,variant,ptr,ptr,ptr,ptr,variant)
get_UnderlyingSystemType(ptr)
InvokeMember_2(bstr,INT,ptr,variant,ptr,ptr,variant)
InvokeMember_3(bstr.p-bstr,INT,ptr,ptr,ptr,ptr,ptr,ptr,variant)
GetConstructor(ptr,ptr,ptr,ptr,ptr,ptr)
GetConstructor_2(ptr,ptr,ptr,ptr,ptr)
GetConstructor_3(ptr,ptr)
GetConstructors_2(ptr)
get_TypeInitializer(ptr)
GetMethod_3(bstr,ptr,ptr,ptr,ptr,ptr,ptr)
GetMethod_4(bstr,ptr,ptr,ptr)
GetMethod_5(bstr,ptr,ptr)
GetMethod_6(bstr,ptr)
GetMethods_2(ptr)
GetField_2(bstr,ptr)
GetFields_2(ptr)
GetInterface_2(bstr,ptr)
GetEvent_2(bstr,ptr)
GetProperty_3(bstr,ptr,ptr,ptr,ptr)
GetProperty_4(bstr,ptr,ptr,ptr)
GetProperty_5(bstr,ptr,ptr)
GetProperty_6(bstr,ptr,ptr)
GetProperty_7(bstr,ptr)
GetProperties_2(ptr)
GetNestedTypes_2(ptr)
GetNestedType_2(bstr,ptr)
GetMember_3(bstr,ptr)
GetMembers_2(ptr)
get_Attributes(ptr)
get_IsNotPublic(short)
get_IsPublic(short)
get_IsNestedPublic(short)
get_IsNestedPrivate(short)
get_IsNestedFamily(short)
get_IsNestedAssembly(short)
get_IsNestedFamANDAssem(short)
get_IsNestedFamORAssem(short)
get_IsAutoLayout(short)
get_IsLayoutSequential(short)
get_IsExplicitLayout(short)
get_IsClass(short)
get_IsInterface(short)
get_IsValueType(short)
get_IsAbstract(short)
get_IsSealed(short)
get_IsEnum(short)
get_IsSpecialName(short)
get_IsImport(short)
get_IsSerializable(short)
get_IsAnsiClass(short)
get_IsUnicodeClass(short)
get_IsAutoClass(short)
get_IsArray(short)
get_IsByRef(short)
get_IsPointer(short)
get_IsPrimitive(short)
get_IsCOMObject(short)
get_HasElementType(short)
get_IsContextful(short)
get_IsMarshalByRef(short)
Equals_2(ptr,short)
EndInterface
Prototype.i CLRCreateInstance(*CLSID, *IID, *Inter)
Global mscoree_dll = OpenLibrary(#PB_Any, "mscoree.dll")
Global CLRCreateInstance.CLRCreateInstance = GetFunction(mscoree_dll, "CLRCreateInstance")
Prototype SafeArrayCreate(a,b,c)
Prototype SafeArrayAccessData(a,b)
Prototype SafeArrayUnaccessData(a)
Prototype SafeArrayDestroy(a)
Prototype SafeArrayGetElement(a,b,c)
Global OleAut32 = OpenLibrary(#PB_Any,"OleAut32.dll")
Global SafeArrayCreate.SafeArrayCreate = GetFunction(OleAut32, "SafeArrayCreate")
Global SafeArrayAccessData.SafeArrayAccessData = GetFunction(OleAut32, "SafeArrayAccessData")
Global SafeArrayUnaccessData.SafeArrayUnaccessData = GetFunction(OleAut32, "SafeArrayUnaccessData")
Global SafeArrayDestroy.SafeArrayDestroy = GetFunction(OleAut32, "SafeArrayDestroy")
Global SafeArrayGetElement.SafeArrayGetElement=GetFunction(OleAut32,"SafeArrayGetElement")
Procedure __CLR_InvokeMember(pAssemblyIType.IType, Member.s, psa)
If pAssemblyIType
Protected pObject.VARIANT
If pAssemblyIType\InvokeMember_3(Member,$158,0,0,0,0,0,psa,@pObject)=0
ProcedureReturn pObject\byref
EndIf
EndIf
EndProcedure
Procedure __CLR_GetClass(pdllAssembly.IAssembly, Class.s)
If pdllAssembly
Protected.IType gpAssemblyType, pAssemblyIType
If pdllAssembly\GetType_2(Class,@gpAssemblyType) = 0
gpAssemblyType\QueryInterface(?IID_IType,@pAssemblyIType)
gpAssemblyType\Release()
EndIf
EndIf
ProcedureReturn pAssemblyIType
EndProcedure
Procedure __CLR_Free_Interface(pinterface.IUnknown)
If pinterface <> 0 : pinterface\Release() : EndIf
EndProcedure
Procedure __CLR_Load_Dll(NetDllPath.s,RuntimeVersion.s)
Macro OnErrorGo(V,erp)
If V <> 0 : errorpos = erp: Goto __Error : EndIf
EndMacro
Protected pClrHost.ICLRMetaHost
Protected pRunInfo.ICLRRuntimeInfo
Protected pRuntimeHost.ICLRRuntimeHost
Protected pCorRuntimeHost.ICorRuntimeHost
Protected.AppDomain gpAppDomain,pAppDomain
Protected pMethodInfo.IMethodInfo
Protected.IType gpType, pIType
Protected IsLoadable
Protected.IAssembly gpIAssembly,pAssembly
Protected.IType gpAssemblyType, pAssemblyIType
Protected.IAssembly gpdllAssembly,pdllAssembly
Protected psa,ppData.i,*v.VARIANT,rgsabound.SAFEARRAYBOUND,pObject.VARIANT
Protected errorpos
OnErrorGo(CLRCreateInstance(?CLSID_CLRMetaHost,?IID_ICLRMetaHost,@pClrHost), 1)
OnErrorGo(pClrHost\GetRuntime(@RuntimeVersion,?IID_ICLRRuntimeInfo,@pRunInfo), 2)
OnErrorGo(pRunInfo\IsLoadable(@IsLoadable), 3)
OnErrorGo(IsLoadable-1, 3)
OnErrorGo(pRunInfo\GetInterface(?CLSID_CLRRuntimeHost,?IID_ICLRRuntimeHost,@pRuntimeHost), 4)
OnErrorGo(pRuntimeHost\Start(), 5)
OnErrorGo(pRunInfo\GetInterface(?CLSID_CorRuntimeHost,?IID_ICorRuntimeHost,@pCorRuntimeHost), 6)
OnErrorGo(pCorRuntimeHost\Start(), 7)
OnErrorGo(pCorRuntimeHost\GetDefaultDomain(@gpAppDomain), 8)
OnErrorGo(gpAppDomain\QueryInterface(?IID_AppDomain,@pAppDomain), 9)
OnErrorGo(pAppDomain\GetType(@gpType), 10)
OnErrorGo(gpType\QueryInterface(?IID_IType,@pIType), 11)
OnErrorGo(gpType\get_Assembly(@gpIAssembly), 12)
OnErrorGo(gpIAssembly\QueryInterface(?IID_IAssembly,@pAssembly), 13)
OnErrorGo(pAssembly\GetType(@gpAssemblyType), 14)
OnErrorGo(gpAssemblyType\QueryInterface(?IID_IType,@pAssemblyIType), 15)
rgsabound\lLBound = 0
rgsabound\cElements = 1
psa = SafeArrayCreate(#VT_VARIANT,1,@rgsabound)
If psa
SafeArrayAccessData(psa, @ppData)
*v = ppData
*v\vt = #VT_BSTR
*v\byref = SysAllocString_(NetDllPath)
SafeArrayUnaccessData(psa)
Else
OnErrorGo(0, 16)
EndIf
If pAssemblyIType\InvokeMember_3("LoadFrom",$158,0,0,0,0,0,psa,pObject) <> 0
OnErrorGo(pAssemblyIType\InvokeMember_3("LoadWithPartialName",$158,0,0,0,0,0,psa,pObject),17)
EndIf
gpdllAssembly = pObject\byref
OnErrorGo(gpdllAssembly\QueryInterface(?IID_IAssembly,@pdllAssembly), 18)
__Error:
If errorpos
Debug "error line : " + errorpos
EndIf
__CLR_Free_Interface(pClrHost)
__CLR_Free_Interface(pRunInfo)
__CLR_Free_Interface(pRuntimeHost)
__CLR_Free_Interface(pCorRuntimeHost)
__CLR_Free_Interface(gpAppDomain)
__CLR_Free_Interface(pAppDomain)
__CLR_Free_Interface(pMethodInfo)
__CLR_Free_Interface(gpType)
__CLR_Free_Interface(pIType)
__CLR_Free_Interface(gpIAssembly)
__CLR_Free_Interface(pAssembly)
__CLR_Free_Interface(gpAssemblyType)
__CLR_Free_Interface(pAssemblyIType)
__CLR_Free_Interface(gpdllAssembly)
If psa
SafeArrayDestroy(psa)
EndIf
ProcedureReturn pdllAssembly
EndProcedure
Procedure __CLR_GetObject(pdllAssembly.IAssembly, object.s)
Protected pObject.VARIANT
If pdllAssembly
pdllAssembly\CreateInstance_2(object,1,pObject)
EndIf
ProcedureReturn pObject\byref
EndProcedure
Structure Var
SafeArray.i
type.l
STR.s
*SystemStr
INT.l
double.d
quad.q
float.f
word.w
EndStructure
Procedure __CLR_FreeArguments(Array Arguments.Var(1))
Protected arsize = ArraySize(Arguments()), i
If Arguments(0)\SafeArray
SafeArrayDestroy(Arguments(0)\SafeArray)
EndIf
For i = 1 To arsize
If Arguments(i)\type = #PB_String
If Arguments(i)\SystemStr
SysFreeString_(Arguments(i)\SystemStr)
EndIf
EndIf
Next
EndProcedure
; __CLR_PopArgs()
; This function takes the a SafeArray descriptor and returns each argument as an element in a dynamic array of type .Var
; The member \SafeArray of the first element (index 0) of the dynamic array must contain the pointer to the SafeArray itself.
; To ensure precise compatibility, the SafeArray descriptor should have been created by the complement function __CLR_PushArgs().
;
Procedure __CLR_PopArgs(Array Arguments.Var(1))
Protected arsize=ArraySize(Arguments())
Protected ppData.i,*v.VARIANT,rgsabound.SAFEARRAYBOUND,pObject.VARIANT,i
rgsabound\lLBound=0
rgsabound\cElements=arsize
Protected psa=Arguments(0)\SafeArray
; Check pointer to safe array is valid
If psa
; Retrieves pointer to the array data (in ppData)
SafeArrayAccessData(psa,@ppData)
*v=ppData
For i = 1 To arsize
Select *v\vt
Case #VT_BSTR
Arguments(i)\type=*v\vt
Arguments(i)\SystemStr=SysAllocString_(*v\byref)
Arguments(i)\STR=PeekS(Arguments(i)\SystemStr)
Case #VT_R8
Arguments(i)\type=*v\vt
Arguments(i)\double=PeekD(*v\byref)
Case #VT_R4
Arguments(i)\type=*v\vt
Arguments(i)\float=PeekF(*v\byref)
Case #VT_I8
Arguments(i)\type=*v\vt
Arguments(i)\quad=PeekQ(*v\byref)
Case #VT_I4
Arguments(i)\type=*v\vt
Arguments(i)\INT=*v\byref
Case #VT_I2
Arguments(i)\type=*v\vt
Arguments(i)\word=PeekW(*v\byref)
EndSelect
*v + SizeOf(VARIANT)
Next
SafeArrayUnaccessData(psa)
EndIf
EndProcedure
; __CLR_PushArgs()
; This function takes a dynamic array (Type .Var) of arguments, creates a SafeArray descriptor and returns a pointer to that
; descriptor. The first element (index 0) of the dynamic array is reserved for the pointer to the SafeArray itself.
;
; SafeArray descriptors are required by the Microsoft Unmanaged Host API For method argument passing. Calling code
; should initialise each element of the dynamic array correctly with both the data type and value for the associated argument.
;
Procedure __CLR_PushArgs(Array Arguments.Var(1))
Protected psa,ppData.i,*v.VARIANT,rgsabound.SAFEARRAYBOUND,pObject.VARIANT, i
Protected arsize = ArraySize(Arguments())
rgsabound\lLBound = 0
rgsabound\cElements = arsize
psa = SafeArrayCreate(#VT_VARIANT,1,@rgsabound)
Arguments(0)\SafeArray = psa
If psa
SafeArrayAccessData(psa,@ppData)
*v = ppData
For i = 1 To arsize
Select Arguments(i)\type
Case #PB_String
*v\vt = #VT_BSTR
Arguments(i)\SystemStr=SysAllocString_(Arguments(i)\STR)
*v\byref = Arguments(i)\SystemStr
Case #PB_Long
*v\vt = #VT_I4
*v\byref = Arguments(i)\INT
Case #PB_Double
*v\vt = #VT_R8
*v\byref = Arguments(i)\double
Case #PB_Quad
*v\vt = #VT_I8
*v\byref = Arguments(i)\quad
Case #PB_Float
*v\vt = #VT_R4
*v\byref = Arguments(i)\float
Case #PB_Word
*v\vt = #VT_I2
*v\byref = Arguments(i)\word
EndSelect
*v + SizeOf(VARIANT)
Next
SafeArrayUnaccessData(psa)
EndIf
ProcedureReturn psa
EndProcedure
DisableExplicit
DataSection
IID_ICLRMetaHost:
Data.l $D332DB9E
Data.w $B9B3, $4125
Data.b $82, $07, $A1, $48, $84, $F5, $32, $16
CLSID_CLRMetaHost:
Data.l $9280188d
Data.w $e8e, $4867
Data.b $b3, $c, $7f, $a8, $38, $84, $e8, $de
IID_ICLRRuntimeInfo:
Data.l $BD39D1D2
Data.w $BA2F, $486a
Data.b $89, $B0, $B4, $B0, $CB, $46, $68, $91
CLSID_CLRRuntimeHost:
Data.l $90F1A06E
Data.w $7712,$4762
Data.b $86,$B5,$7A,$5E,$BA,$6B,$DB,$02
IID_ICLRRuntimeHost:
Data.l $90F1A06C
Data.w $7712,$4762
Data.b $86,$B5,$7A,$5E,$BA,$6B,$DB,$02
CLSID_CorRuntimeHost:
Data.l $cb2f6723
Data.w $ab3a, $11d2
Data.b $9c, $40, $00, $c0, $4f, $a3, $0a, $3e
IID_ICorRuntimeHost:
Data.l $CB2F6722
Data.w $AB3A,$11D2
Data.b $9C,$40,$0,$C0,$4F,$A3,$A,$3E
IID_AppDomain:
Data.l $05F696DC
Data.w $2B29,$3663
Data.b $AD,$8B,$C4,$38,$9C,$F2,$A7,$13
IID_IAssembly:
Data.l $17156360
Data.w $2F1A
Data.w $384A
Data.b $BC,$52,$FD,$E9,$3C,$21,$5C,$5B
IID_MethodInfo:
Data.l $FFCC1B5D
Data.w $ECB8,$38DD
Data.b $9B,$01,$3D,$C8,$AB,$C2,$AA,$5F
IID_IType:
Data.l $BCA8B44D
Data.w $AAD6,$3A86
Data.b $8A,$B7,$03,$34,$9F,$4F,$2D,$A2
EndDataSection
Code: Select all
; This PureBasic code uses the .NET Unmanaged hosting API to call
; managed functions in a .NET DLL.
;
EnableExplicit
IncludeFile "CLR.PBI"
#NET_Runtime="v2.0.50727" ;"v4.0.30319" ; This must be compatible with the Runtime version required by the DLL
Dim Method_InArgs.Var(2) ; argument array (to pass args in to .NET methods)
Dim Method_OutArgs.Var(2) ; argument array (to pass args out from .NET methods)
dllAssembly=__CLR_Load_Dll("MyLibrary.dll",#NET_Runtime) ; Load the .NET assembly (.dll) And invoke specified .NET Runtime version
class=__CLR_GetClass(dllAssembly,"MyNameSpace.MyClass") ; Load the class
Method_InArgs(1)\type=#PB_Long ; parameter Type
Method_InArgs(1)\INT=$98 ; parameter value
Method_InArgs(2)\type=#PB_Long ; parameter Type
Method_InArgs(2)\INT=$101 ; parameter value
psa=__CLR_PushArgs(Method_InArgs()) ; Push arguments to a SafeArray
Debug "psa=0x"+Hex(psa) ;
ShowMemoryViewer(psa,100) ;
iResult=__CLR_InvokeMember(class,"MyMethod",psa) ; Call function of the class and get result as returncode
Debug "iResult=0x"+Hex(iResult) ; Display result (returncode)
Method_OutArgs(0)\SafeArray=psa ; Load target array element 0\SafeArray with address of SafeArray
__CLR_PopArgs(Method_OutArgs()) ; Copy args from SafeArray to OutArgs()
For i=1 To ArraySize(Method_InArgs())
Debug "Method_InArgs("+Str(i)+")\INT=0x"+Hex(Method_InArgs(i)\INT)
Debug "Method_OutArgs("+Str(i)+")\INT=0x"+Hex(Method_OutArgs(i)\INT)
Next
Main_Exit:
__CLR_FreeArguments(Method_InArgs()) ; Free memory used by the arguments In array
__CLR_FreeArguments(Method_OutArgs()) ; Free memory used by the arguments Out array
__CLR_Free_Interface(class) ; Free memory used by the Class
__CLR_Free_Interface(dllAssembly) ; Free memory used by the Interface
Code: Select all
# Python Script - Testing .NET CLR access to a DLL
import clr
import System
from System import IO
from System import String
from System import Array
from System import Byte
from System import Boolean
from System import UInt32
from System import Text
from System import Int32
clr.AddReference("MyLibrary.dll")
from MyNameSpace import MyClass
NewMyClass=MyClass()
Result=0
OutParam1=0
OutParam2=0
InParam1=50
InParam2=55
[Result,OutParam1,OutParam2]=NewMyClass.MyMethod(InParam1,InParam2)
print("Result="+str(Result))
print("OutParam1="+str(OutParam1))
print("OutParam2="+str(OutParam2))