I want to call a COM object method which takes an argument that is an array from PowerBuilder. The method works fine from other OLE automation controllers (VB6, VBScript, VBA, Delphi, PHP, to name just some of them), and in IDL, it looks like this:
void MyMethod([in, out] SAFEARRAY(VARIANT)* argumentsArray);
So far I have not succeeded calling this method from PowerBuilder. I always get runtime error similar to "Invalid parameter type calling external object function MyMethod at line XX in XXXXXX". As far as I can tell, this error is generated by PowerBuilder before my method gets called, i.e. it is not a kind of transformed HRESULT from the method itself, and the method does not get called at all.
I have no problems calling other methods of the same COM object from PowerBuilder - those that do not use arrays as arguments.
Examples of PowerBuilder code that I have tried and does not work are:
Any Arguments[]
Arguments[1] = MyArgument
MyObject.MyMethod(Arguments)
Or
OLEObject Arguments[]
Arguments[1] = MyArgument
MyObject.MyMethod(Arguments)
Or
OLEObject Arguments[0 to 0]
Arguments[0] = MyArgument
MyObject.MyMethod(Arguments)
Or
OLEObject Arguments[0 to 0]
Arguments[0] = MyArgument
MyObject.MyMethod(REF Arguments)
I have searched StackOverflow and the Web, and found only answers to the opposite problem - how to pass the arrays that are results of COM methods back to the PowerBuilder.
How to pass the array then?
Related
I'm working with the NativeCall interface.
The library is going to call my callback function a bunch of times.
That works fine. I can just declare my callback with the right
signature, pass it in as &callback and the library calls the sub just
fine.
It also has the capability to set a payload void *pointer to anything
I want, and it will include that in the call to my callback function.
Can I hide a Perl Str, for example, in the payload and successfully round trip it?
sub set_userdata(Pointer) returns int32 is native { ... }
sub set_callback(&callback(Pointer $userdata --> int32)) returns int32 is native { ... }
sub callback(Pointer $userdata) returns int32 {
my Str $mystring = ???
...
}
my Str $my-userdata-string;
set_userdata(???);
set_callback(&callback);
It seems like it could work with some incantation of binding, "is rw", nativecast() and/or .deref.
You can only use a native representation in such a case (such as CStruct, CArray, and CPointer), or alternatively a Blob. You are also responsible for ensuring that you keep a reference to the thing you pass as userdata alive from Perl 6's perspective also, so the GC doesn't reclaim the memory that was passed to the C function.
Memory management is the reason you can't pass any old Perl 6 object off to a C function: there's no way for the GC to know whether the object is still reachable through some C data structure it can't introspect. In a VM like MoarVM objects are moved around in memory over time as part of the garbage collection process also, meaning that the C code could end up with an out-dated pointer.
An alternative strategy is not not pass a pointer at all, but instead pass an integer and use that to index into an array of objects. (That's how the libuv binding inside of MoarVM tracks down the VM-level callbacks, fwiw.)
I got around this by just ignoring the userdata and making a new closure referencing the Perl object directly for every callback function. Since there is a new closure created every time I set the callback, I think this will leak memory over time.
I'm working on a vb.net application that interacts with a (third party provided) web app to provide additional functionality (e.g. removing menu items, pulling information from the pages, etc.). The web app is completely driven by javascript but is hosted in asp.net and is only used with Internet Explorer.
I'm trying to read properties from a javascript object and execute some of it's functions. I've managed to get hold of the javascript object by getting the mshtml.HTMLDocument of the iframe the script resides in and using the following code:
Dim jsObject as Object
jsObject = htmldoc.Script.jsObject
jsObject exists as a {System.__ComObject} and i can use it to execute any of it's functions or read it's properties as follows:
Dim value as String = jsObject.FunctionThatReturnsAString()
jsObject.FunctionTHatDoesSomethingInWebApp("Param1", "Param2")
This works great. However, when I leave the page/frame with jsObject in and return to it, the same code throws an exception when getting the javascript object from the frame again (i.e. executing the following line):
jsObject = htmldoc.Script.jsObject
Exception: Member not found. (Exception from HRESULT: 0x80020003 (DISP_E_MEMBERNOTFOUND))
If I stop debugging and restart, it works again (until i leave the page, etc.). I'm not sure what's happening that's causing the javascript object to disappear as far as my app's concerned. I'm presuming it's due to my app holding a reference to the COM object and i need to release it in some way (particulary as it's got a base type of MarshalByRefObject - which makes sense as it's being passed between app domains).
Why is this happening? Is there a better way of accessing a javascript object, it's properties and functions?
I've found what is, in my case, a better way of achieving what I need. Instead of accessing the jsObject directly as a COM Object (and worrying about Marshaling, etc.), I either use:
execScript to call functions with no return value or
create a hidden div element in the frame i'm working in, set the innerHTML of that div equal to whatever javascript variable/function return value that i'm interested in using execScript and then read that value seperately from the DOM
To read a variable/function return i use the following vb.net function:
Private Function getJScriptVariable(ByVal JScript As String)
Dim command As New StringBuilder()
command.Append("var e = document.getElementById('Matt_JScriptReturn');")
command.Append("if (e == null) {")
command.Append("var e = document.createElement('div');")
command.Append("e.id = 'Matt_JScriptReturn';")
command.Append("e.type = 'hidden';")
command.Append("document.body.appendChild(e);")
command.Append("}")
command.Append("e.innerHTML = ")
command.Append(JScript)
command.Append(";")
'fMaster is the frame containing the javascript's mshtml.IHTMLWindow2
fMaster.execScript(command.ToString(), "JScript")
'N.B. fMaster_Document is the fMaster's mshtml.HTMLDocument
Return fMaster_Document.getElementById("Matt_JScriptReturn").innerHTML
'Optionally execScript to remove element from DOM at this point
End Function
Then i would use that function as follows (respecting my example in the original question):
Dim value as String = getJScriptVariable("jsObject.FunctionThatReturnsAString()")
To execute javascript code without needing to return a value I simply execute it as follows:
fMaster.execScript("jsObject.FunctionTHatDoesSomethingInWebApp('Param1', 'Param2')")
I'd still be interesting in finding out why i had the problem earlier with the javascipt object being unable to access after leaving the page and returning, however this solves my problem so i'm happy for now! I hope this helps someone else at some point.
I'm a C++ programming who was tapped to write a small application in Visual Basic. The application hosts an IronPython runtime and I am attempting to define some function in python and then call them from VB. I have written a simple test function in python
def test():
print "Test was Called"
Then I use the iron python to create a ScriptSource from the python file. I am able to look up the "test" variable through object operations but I can not figure out how to call the object that. For example (in VB):
pyScope = engine.CreateScope()
pySource = engine.CreateSourceFromFile("C:\Some\File\path\test.py")
pySource.Execute(pyScope)
' Now I expect the function test() to be defined in pyScope
Dim tmp as Object
pyScope.TryGetVariable("test", tmp)
At this point in my code tmp is defined as an object of type PythonFunction. I can't figure out how to call this function.
tmp()
Is not valid VB syntax. I've gotten this far, now how do I perform this seemingly simple task?
Edit: By calling
pyEngine.Operations.Invoke(tmp)
I am able to call the function and I see the expected output at stdout. I am still under the impression that there is some function-pointer-like type that I can cast objects of type PythonFunction to which will let me invoke temp directly without calling to the Python engine.
Not sure this will work, but try casting it to an Action type:
DirectCast(tmp, Action)()
Based on the comment, try this:
engine.ObjectOperations.Invoke(tmp, Nothing)
VB in .NET 4 should have the same dynamic support as C#. According to http://msdn.microsoft.com/en-us/library/ee461504.aspx#Y5108 (near the bottom), you should be able to do:
Dim tmp As Object = scope.GetVariable("test")
... which is what you're already doing, so make sure you're targeting .NET 4.
If that doesn't work you should be able to cast it with the generic version of GetVariable:
Dim tmp As Action = scope.GetVariable(Of Action)("test")
Finally, you already discovered Invoke on ObjectOperations.
(You may need to tweak the syntax, since I don't know VB.)
In a MATLAB class I am writing, if the constructor is given 0 arguments, the user is asked to provide a file using uigetfile. If the user cancels the prompt, uigetfile returns 0. In that case it makes no sense to make the object. Is there some way of cancelling the object construction without throwing an exception? If I do an early return I get a malformed object that cannot be used. Here is what the code looks like:
classdef MyClass
methods
function self = MyClass(filename)
if nargin == 0
filename = uigetfile;
if filename == 0
%cancel construction here
return; %I still get a MyClass object with self.filename == []
end
end
self.filename = filename;
end
end
properties
filename;
end
end
However I am unsure if using uigetfile in the constructor is the right thing to do. Maybe it should be the resposibility of another part of my code.
In modern Matlab objects, I don't think it's possible to get out of the constructor without either returning a constructed object or throwing an error. (In the old style classes, the constructor was actually allowed to return whatever it wanted, including objects or primitives of other types, and oh man could that turn in to a mess.) When a constructor is called, the output argument is already intialized with an object with the default property values, so when you call return there, it just skips the rest of initialization and returns the object. If you try to replace out with something besides a MyClass object, that's an error.
Just reorganize the control flow to pull the GUI code out of the constructor, like you're speculating on at the end. Mixing it in to the constructor, especially conditionally, can cause problems. In particular, Matlab expects the zero-arg constructor to always return a scalar object with some sort of default values, because the zero-arg gets called implicitly when filling in elements during array expansion and so on. It's basically used as a prototype.
I'm having trouble with a .NET Assembly that is com visible, and calling certain methods from VB6.
What I have found is that if the parameters are well defined types, (e.g. string), calls work fine. If they are higher level objects, it raises a runtime error '438' suggesting that the property or method is not present. I suspect that this is a question of having the correct signature on the call, but I can't see how to do this correctly.
I believe that I've done everything correct on the .NET side (ComVisible, public interfaces, etc. and even have it down to a simple enough case).
Looking at the output from the typelib viewer, I have the following:
dispinterface ISimple {
properties:
methods:
[id(0x60020000)]
void Add([in] ISimpleMember* member);
[id(0x60020001)]
ISimpleMember* Create();
};
OK. So I have 2 methods in my ISimple interface. One takes an ISimpleMember (Add), whilst the other, returns an ISimpleMember.
The corresponding code in VB looks like this:
Dim item As ISimpleMember
Dim simple As simple
Set item = New SimpleMember
item.S1 = "Hello"
item.S2 = "World"
Set simple = New simple
simple.Add (item) <---- This raised the run time error 438
Set item = simple.Create <---- This works fine, returning me an ISimpleMember
I've tried a couple of things:
1. Dim item as SimpleMember (makes no difference)
2. simple.Add(ObjPtr(item)) - Syntax error
3. simple.Add(ByRef item) - Syntax error
Basically, The run time error is the same as if I had
simple.AMethodThatIHaventWritten()
Also, If I browse References in the VB6 Environment, The Add method is well defined:
Sub Add(member As SimpleMember)
I've found the answer I believe. It was very simple:
When calling a SubRoutine, I shouldn't put the name in braces. the call should have been:
simple.add member
rather than
simple.add(member)
If I change it to a function (i.e. return a value rather than void) the braces are necessary
This seems to work
(Probably) The top 3 VB6 coding mistakes made by devs who now mainly code in C#, Javascript etc. Are:-
Placing ; at the end of lines. Its a syntax error very easily spotted and picked up the compiler.
Not placing Then on the other side of an If condition expression. Again its a syntax error.
Calling a method without retrieving a value and yet using ( ) to enclose the parameter list. With multiple parameters this is a syntax error and easily found. With only one parameter the use of ( ) is interpreted as an expression. Its the result of the ( ) expression which is passed as parameter. This causes problems when ByRef is expected by the callee.