Pointer call is a DLL function call, where function is referred to by its memory location.
See also: examples in
SVN folder; change
history.
Applications of pointer call are when the function is not part of dll map. For example
callback, we are given a function address and need to call it, as in OpenGL shader
function address is a part of a known struct, such as Java JNI and AWT JNI
calling a method from vtable of an interface pointer
Interface pointer is an object reference as implemented in C++ (this pointer) and COM. Given pointer to object p and vt-index (vtable index) i to get function address we say in C p[0][i]. To call a C++ or COM method we say (omitting type casts) r = p[0][i](p,...);.
cd (15!:0) accepts the following signature 'module entry ...', where
module is dll name
entry is function name in dll map or index if it has the form #123
It is proposed to recognize additional meanings:
module=0 then entry is function pointer
module=1 then entry is vtable index from interface pointer in first argument
All other conventions, such as %, etc. would still apply. In summary, cf
Calling DLLs in User Manual.
'name.dll fname i i ...' cd ... NB. by func name 'name.dll #123 i i ...' cd ... NB. by func index 123 '0 12345 i i ...' cd ... NB. by func ptr 12345 '1 123 i i ...' cd p,.. NB. by vt-index 123 of object p
Note: module as other numeric is reserved for future use
Pointer Call Demo
As a proof of concept, but good enough to use in applications, here is an example of such use as emulated by the general/pcall addon. It shows a dll pcall that emulates pointer call, and vtable address resolved in J. We demonstrate how pointer call gives the same fluency in COM programming as in C or C++.
Note: these explicit calls to COM and Automation API are shown here for demonstration. For practical reasons, they could be all wrapped in a nice API in J. In addition, for particular known components J class wrappers can be created.
There is no reason why using this approach the wd 'ole...' could not be replaced with regular J code using the extended DLL calling conventions. The order of complexity should not exceed that of the memory mapped files implementation.
Interface pointers are not confined to COM and Windows only, for example Apple
Plugin API for Mac OS X and
Mozilla XPCOM for Netscape and Firefox use them too.
Calling a J Method
Here we call J method DoR with parameter i.3 4.
Download
general/pcall/test_pcall.ijs
require 'dll convert general/pcall'
VAR1=: 'VtR1R2R3Valu'
b=: #~ [: $&1 0 #
h=: ([: ;:^:_1"1 [: <"1 hfd)@(([: , _4 (_2&(3!:4))@|.\ ])^:(2=3!:0))
mi=: [: {.@memr ,&(0 1,JINT)
mc=: ,&0@] memr@, ,&JCHAR@[
'CLSCTX_INPROC_SERVER CLSCTX_LOCAL_SERVER'=: 16b0001 16b0004
CTX=: CLSCTX_INPROC_SERVER+CLSCTX_LOCAL_SERVER
IUknown=: ;:'QueryInterface AddRef Release'
IDispatch=: IUknown,;:'GetTypeInfoCount GetTypeInfo GetIDsOfNames Invoke'
IJ=: IDispatch,;:'Do Show Log IsBusy Break Quit Get Set GetM SetM ErrorText ErrorTextM Clear Transpose ErrorTextB GetB SetB DoR'
h clsid=. 2{::'ole32 CLSIDFromProgID i *w *c'cd 'JDLLServer';'WWWWXXYYZZZZZZZZ'
EA05EB21 B31ACF11 A2AC8FF7 0874C460
h iid=. 2{::'ole32 CLSIDFromString i *w *c'cd '{21EB05EC-1AB3-11CF-A2AC-8FF70874C460}';'WWWWXXYYZZZZZZZZ'
EC05EB21 B31ACF11 A2AC8FF7 0874C460
h ip=. {._1{::'ole32 CoCreateInstance i *c i i *c *i'cd clsid;0;CTX;iid;,_2
10B4018
h vt=. mi ip NB. vtable of J interface pointer
1012D380
h pDoR=. mi vt+4*IJ i.<'DoR' NB. funcion pointer from vtable
100512D0
res=. i.4%~#VAR1 NB. alloc VARIANT for BSTR
'cp4 i i i *w *i *i' pcall pDoR;ip;'i.3 4';res;,_2
+-+---------+--------+-----+----------+-+
|0|268767952|17514520|i.3 4|8 1 315268|0|
+-+---------+--------+-----+----------+-+
] len=. mi _4+{:res NB. BSTR length
66
b val=. len mc {:res NB. BSTR string value
0 1 2 3
4 5 6 7
8 9 10 11
'oleaut32 VariantClear > i *i' cd <res NB. free BSTR, 'cause we own it
0
h pRelease=. mi vt+4*IJ i.<'Release' NB. Release from IUnknown interface
10051060
'cp1 > i i i' pcall pRelease;ip NB. destroys J object; RefCount=0
0
Dynamic Discovery of Type Information
Here we discover the number of functions in J interface (25) and the name of a specific function IsBusy using
ITypeInfo interface.
Download
general/pcall/test_typeinfo.ijs
require 'dll convert general/pcall'
b=: #~ [: $&1 0 #
h=: ([: ;:^:_1"1 [: <"1 hfd)@(([: , _4 (_2&(3!:4))@|.\ ])^:(2=3!:0))
us=: 0&(3!:4)
mi=: [: {.@memr ,&(0 1,JINT)
mc=: ,&0@] memr@, ,&JCHAR@[
mI=: 4 : '{.memr y,x,1,JINT'
mS=: 4 : '{.us memr y,x,2,JCHAR'
si=: I.@E.~ NB. TYPEATTR si 'Cfun'
GUID=: 'WWWWXXYYZZZZZZZZ'
VAR1=: 'VtR1R2R3Valu'
TYPEATTR=: GUID,'LcidReseCtorDtorScheSinsTpknCfCvCtSvBaTfMjMnAliaIdld'
'CLSCTX_INPROC_SERVER CLSCTX_LOCAL_SERVER'=: 16b0001 16b0004
CTX=: CLSCTX_INPROC_SERVER+CLSCTX_LOCAL_SERVER
IUnknown=: ;:'QueryInterface AddRef Release'
IDispatch=: IUnknown,;:'GetTypeInfoCount GetTypeInfo GetIDsOfNames Invoke'
IJ=: IDispatch,;:'Do Show Log IsBusy Break Quit Get Set GetM SetM ErrorText ErrorTextM Clear Transpose ErrorTextB GetB SetB DoR'
ITypeInfo=: IUnknown,;:'GetTypeAttr GetTypeComp GetFuncDesc GetVarDesc GetNames GetRefTypeOfImplType GetImplTypeFlags GetIDsOfNames Invoke GetDocumentation GetDllEntry GetRefTypeInfo AddressOfMember CreateInstance GetMops GetContainingTypeLib ReleaseTypeAttr ReleaseFuncDesc ReleaseVarDesc'
h clsid=. 2{::'ole32 CLSIDFromProgID i *w *c'cd 'JDLLServer';1#GUID
EA05EB21 B31ACF11 A2AC8FF7 0874C460
h iid=. 2{::'ole32 CLSIDFromString i *w *c'cd '{21EB05EC-1AB3-11CF-A2AC-8FF70874C460}';1#GUID
EC05EB21 B31ACF11 A2AC8FF7 0874C460
h ip=. {._1{::'ole32 CoCreateInstance i *c i i *c *i'cd clsid;0;CTX;iid;,_2
10B4018
h vt=. mi ip NB. vtable of J interface pointer
1012D380
h pDoR=. mi vt+4*IJ i.<'DoR' NB. funcion pointer from vtable
100512D0
res=. i.4%~#VAR1 NB. alloc VARIANT for BSTR
'cp4 > i i i *w *i *i' pcall pDoR;ip;'9!:14$0';res;,_2
0
] len=. mi _4+{:res NB. BSTR length
46
b val=. len mc {:res NB. BSTR string value
j601/2006-11-17/17:05
'oleaut32 VariantClear > i *i' cd <res NB. free BSTR, 'cause we own it
0
h pGetTypeInfo=. mi vt+4*IJ i.<'GetTypeInfo'
100510B0
h ti=. {._1{::'cp4 i i i i i *i' pcall pGetTypeInfo;ip;0;0;,_2
8BE04
h tivt=. mi ti
77126770
pta=. ,_2
h pGetTypeAttr=. mi tivt+4*ITypeInfo i.<'GetTypeAttr'
77126B7D
'cp2 > i i i *i' pcall pGetTypeAttr;ti;pta
0
] cf=. (TYPEATTR si 'Cf') mS pta NB. count of functions in J interface
25
#IJ
25
pfd=. ,_2
h pGetFuncDesc=. mi tivt+4*ITypeInfo i.<'GetFuncDesc'
7712AAC7
'cp3 > i i i i *i' pcall pGetFuncDesc;ti;(IJ i.<'IsBusy');pfd
0
h mid=. 0 mI pfd
60020003
res=. ,_2
h pGetDocumentation=. mi tivt+4*ITypeInfo i.<'GetDocumentation'
77126F0A
h 'cp6 > i i i i *i i i i' pcall pGetDocumentation;ti;mid;res;0;0;0
0
] len=. mi _4+res
12
b val=. len mc res NB. name of function
IsBusy
'oleaut32 SysFreeString > i i' cd res
32
h pRelease=. mi tivt+4*ITypeInfo i.<'Release'
771268EB
'cp1 > i i i' pcall pRelease;ti
1
h pRelease=. mi vt+4*IJ i.<'Release' NB. Release from IUnknown interface
10051060
'cp1 > i i i' pcall pRelease;ip NB. destroys J object, RefCount=0
0
Interface Call
pcall addon has been enhanced to emulate module=1 proposed case. For example, cd signature to call interface with 3-argument function is
'pcall ci3 i i i i i i' types are result,vt-index,this, arg1,arg2,arg3
The call looks like this, where jiDoR is simply vt-index integer
'ci3 > i i i *w *i *i' pcall jiDoR;J;'9!:14$0';res;,_2
Download
general/pcall/test_icall.ijs
require 'general/pcall/ole32'
coinsert 'pole32'
'IJ_'idef IJ=: IDispatch,;:;<;._2(0 : 0)
Do Show Log IsBusy Break Quit Get Set GetM SetM ErrorText
ErrorTextM Clear Transpose ErrorTextB GetB SetB DoR
)
ijDoR=: [:'ci3 > i i i *w *i *i' pcall IJ_DoR ; ;
CLSID_JDLLServer=: '{21EB05EC-1AB3-11CF-A2AC-8FF70874C460}'
h clsid=. 2{::'ole32 CLSIDFromProgID i *w *c'cd 'JDLLServer';1#GUID
h iid=. 2{::'ole32 CLSIDFromString i *w *c'cd CLSID_JDLLServer;1#GUID
h J=. {._1{::'ole32 CoCreateInstance i *c i i *c *i'cd clsid;0;CTX;iid;,_2
res=. i.4%~#VAR1 NB. alloc VARIANT for BSTR
'ci3 > i i i *w *i *i' pcall IJ_DoR;J;'9!:14$0';res;,_2
VariantStr res NB. BSTR string value
'oleaut32 VariantClear > i *i' cd <res NB. free BSTR, 'cause we own it
h IT=. {._1{::'ci3 i i i i i *i' pcall IJ_GetTypeInfo;J;0;0;,_2
pta=. ,_2
'ci1 > i i i *i' pcall IT_GetTypeAttr;IT;pta
] cf=. (TYPEATTR si 'Cf') mS pta NB. count of functions in J interface
#IJ
pfd=. ,_2
'ci2 > i i i i *i' pcall IT_GetFuncDesc;IT;IJ_IsBusy;pfd
h mid=. 0 mI pfd
res=. ,_2
h 'ci5 > i i i i *i i i i' pcall IT_GetDocumentation;IT;mid;res;0;0;0
GetStr res NB. name of function
'oleaut32 SysFreeString > i i' cd res
'ci0 > i i i' pcall IT_Release;IT
'ci0 > i i i' pcall IJ_Release;J NB. destroys J object, RefCount=0
COM Call
Finally we can make wrappers to COM interface functions using interface call and position in the interface table. Now the call looks much like in Visual Basic
J ijDoR '9!:14$0' ; res ; ,_2
Note, such wrapper functions can be automatically generated using the TypeInfo discovery mechanism provided by IDispatch from live object or using COM API from DLL.
Download
general/pcall/test_comcall.ijs
require 'general/pcall/ole32'
coinsert 'pole32'
'IJ_'idef IJ=: IDispatch,;:;<;._2(0 : 0)
Do Show Log IsBusy Break Quit Get Set GetM SetM ErrorText
ErrorTextM Clear Transpose ErrorTextB GetB SetB DoR
)
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
ijDoR=: [:'ci3 > i i i *w *i *i' pcall IJ_DoR ; ;
h J=.CreateObject 'JDLLServer'
10B4018
res=. VariantAlloc '' NB. alloc VARIANT for BSTR
J ijDoR '9!:14$0' ; res ; ,_2
0
VariantStr res NB. BSTR string value
j601/2006-11-17/17:05
VariantClear <res NB. free BSTR, 'cause we own it
0
h IT=.{.IT [ J idGetTypeInfo 0 ; 0 ; IT=.,_2
ACBC4
IT itGetTypeAttr pta=. ,_2
0
] cf=. (TYPEATTR si 'Cf') mS pta NB. count of functions in J interface
25
#IJ
25
IT itReleaseTypeAttr {.pta
96
IT GetDoc _1 NB. type name
+-----------+--------------------------------+
|IJDLLServer|Jsoftware : JDLLServer Interface|
+-----------+--------------------------------+
}:; IT <@(LF ,~ ;:^:_1)@FuncDesc (#IDispatch)([+i.@-~)cf
long Do BSTR input
long Show long b
long Log long b
long IsBusy
long Break
long Quit
long Get BSTR jname VARIANT* v
long Set BSTR jname VARIANT* v
long GetM BSTR jname long* jtype long* jrank long* jshape long* jdata
long SetM BSTR jname long* jtype long* jrank long* jshape long* jdata
long ErrorText long error VARIANT* v
long ErrorTextM long error long* text
long Clear
long Transpose long b
long ErrorTextB long error VARIANT* v
long GetB BSTR jname VARIANT* v
long SetB BSTR jname VARIANT* v
long DoR BSTR input VARIANT* v
IT FuncDoc IJ_DoR
+---+---------------------------------------------------+
|DoR|Execute string and return formatted output in BSTR.|
+---+---------------------------------------------------+
IT iuRelease ''
1
J iuRelease '' NB. destroys J object, RefCount=0
0
IDispatch Class
One more thing... To showcase the true power of interface call, we create a cover class pdisp which replaces the wd'ole...' commands with Visual Basic style calling, i.e. more natural object-oriented instance handling, passing regular parameters (not all as one string) and automatic conversion of between J and COM variant types.
require 'general/pcall/disp'
p=. disp 'MSXML2.DOMDocument'
'async' put__p 0
++
||
++
'loadXml' do__p '<a b="q123">test</a>'
1
'xml' get__p ''
<a b="q123">test</a>
q1=. disp 'documentElement' get__p ''
'getAttribute' do__q1 'b'
q123
q2=. disp 'createElement' do__p 'c'
'setAttribute' do__q2 'b';'z456'
++
||
++
'appendChild' do__q1 <<P__q2
+--------+
|33129552|
+--------+
'xml' get__p ''
<a b="q123">test<c b="z456"/></a>
destroy__q2''
0
destroy__q1''
1
q1=. disp 'selectSingleNode' do__p 'a/c/@b'
'text' get__q1 ''
z456
destroy__q1''
0
q1=. disp 'selectSingleNode' do__p 'qq/zz'
P__q1
0
'text' get__q1 :: (smoutput@(13!:12 bind'')) ''
|disp is null: assert
| ERR_NULL assert P~:0
destroy__q1''
0
info__p''
IXMLDOMDocument2 NB.
BSTR nodeName NB. name of the node
VARIANT nodeValue NB. value stored in the node
void nodeValue VARIANT NB. value stored in the node
DOMNodeType nodeType NB. the node's type
IXMLDOMNode* parentNode NB. parent of the node
...
destroy__p''
0
