Delphi - OLE variant passing problem (RsLinx OPC, Group Adding working with only from constants)
We got OPC job. I cannot installed RsLinx to my Win7 (and XP mode too) because of errors, so I send my test app to the real place, and somebody testing it.
Because I don't have DLL, I cannot make Delphi interface, so I need to do OLE Calls only.
I got an interesting problem with Group Add.
I demonstrate it:
procedure TForm1.Button8Click(Sender: TObject);
var
r, g : variant;
s : string;
v : variant;
ws : WideString;
begin
Log('Connect');
r := CreateOleObject('RSI.OPCAutomation');
r.Connect('RSLinx OPC Server');
Log('Add as constant');
g := r.OPCGroups.Add('MONKEY_C');
Log('N开发者_运维百科ame ' + g.Name);
Log('Add as string');
s := 'MONKEY_S';
g := r.OPCGroups.Add(s);
Log('Name ' + g.Name);
Log('Add as variant');
s := 'MONKEY_V';
v := s;
g := r.OPCGroups.Add(v);
Log('Name ' + g.Name);
Log('Add as ole variant');
s := 'MONKEY_OV';
v := VarAsType(s, varOleStr);
g := r.OPCGroups.Add(v);
Log('Name ' + g.Name);
Log('Add as widestring');
s := 'MONKEY_WS';
ws := WideString(s);
g := r.OPCGroups.Add(ws);
Log('Name ' + g.Name);
Log('Add as widestring var');
s := 'MONKEY_WSV';
ws := WideString(s);
v := ws;
g := r.OPCGroups.Add(v);
Log('Name ' + g.Name);
r := 0;
end;
The result was:
Connect
Add as constant
Name MONKEY_C
Add as string
Name _Group0
Add as variant
Name _Group1
Add as ole variant
Name _Group2
Add as widestring
Name _Group3
Add as widestring var
Name _Group4
So the problem that I cannot add any Group than constant defined...
I need to know HOW Delphi compile this constant to I can convert my variant value to this format.
Can anybody help me in this theme?
Thanks: dd
Hi!
So the problem is mysterious. I found another errors in the pure OLE calls.
function TDDRsOPCObject.IndexOfGroup(GroupName: string): integer;
var
ogs, g : variant;
i : integer;
s : string;
begin
CheckObject;
Result := -1;
ogs := FObj.OPCGroups;
s := '';
for i := 1 to ogs.Count do begin
g := ogs.Item(i); // This is working
if AnsiCompareText(g.Name, GroupName) = 0 then begin
Result := i;
Exit;
end;
end;
end;
function TDDRsOPCObject.GetGroupByName(GroupName: string): variant;
var
idx : integer;
ogs, g : variant;
begin
CheckObject;
VarClear(Result);
idx := IndexOfGroup(GroupName);
ogs := FObj.OPCGroups;
if idx <> -1
then begin
g := ogs.Item(idx); // HERE I GOT: The parameter is incorrect
Result := g;
end;
end;
So it is interesting: the IndexOfGroup with same call is working, the GetGroupByName is not... :-(
So I determined I do not continue my fighting with windmills (Don Q). I got TLB from a dear user that have Delphi7 (in Win7 the Delphi6 cannot produce OLE interface), and I found Kassl.
May these interfaces can help me...
Thanks: dd
As far as I know the constant and the strings are all converted to an OleString/BSTR (WideString). But since you are having these problems... probably not.
- What does the documentation of OPCGroups.Add say? What is expected?
- Do you have a type library? Maybe you can import them and use the interface directly.
Edit:
The documentation isn't very clear.
There are a few things you can try:
- Check in CPU view what the Delphi compiler made of the code with the constant, maybe you see some hints there about what to do with your strings.
- Try this code.
code:
const
OPC_GROUP_NAME: WideString = 'MONKEY_C';
<...>
g := r.OPCGroups.Add(OPC_GROUP_NAME);
Log('Name ' + g.Name);
When above code works, try this:
const
{$J+} //writable constants on
OPC_GROUP_NAME: WideString = 'dummy';
{$J-}
<...>
OPC_GROUP_NAME := 'MONKEY_BLA';
g := r.OPCGroups.Add(OPC_GROUP_NAME);
Log('Name ' + g.Name); //should be: 'Name MONKEY_BLA'
Note: I don't like step 2, but if it works.. why not. To me it seems like there is a bug in the com-library you use.
Edit2:
I looked at the code generated by using the constant and using a normal string. With the constant I see the address of the first character being pushed on the stack, with the string I see the address of a pointer to a string being pushed on the stack.
With the code below I can simulate the same behaviour as with the constant:
var
lWideArray: array[0..40] of WideChar;
s: string;
i: Integer;
<..>
s := 'MONKEY_FOO';
for i := 0 to Length(lWideArray) - 1 do
begin
if i < Length(s) then
lWideArray[i] := WideChar(s[i+1])
else
lWideArray[i] := #0;
end;
g := r.OPCGroups.Add(WideString(lWideArray));
Log('Name ' + g.Name);
There are some issues in your code, also it would be nice to know which version of Delphi you're using, and what parameter type the Add() call use. Anyway some hints:
ws := WideString(s);
That's a wrong typecast. It won't convert your string to a WideString, it will just force the memory to be interpreted as such. Use
ws := s;
The compile will take care to call the conversion routine.
You do not have to invent the wheel. There are a lot of libraries, examples and sample code how to use OPC with Delphi. For free Delphi OPC servers and clients, take a look here: http://www.opcconnect.com/delphi.php.
精彩评论