Convert C# to delphi - c#

please i need help to port this c# code into delphi ,
I tried but the issue with delphi is that
TArray is a genric type look at my delphi code procedure WriteVector(array: TArray);
internal static void WriteValue(object value, Type valueType)
{
var type = value.GetType();
switch (Type.GetTypeCode(type))
{
// the following are already implemented into delphi
case TypeCode.Int32: Write((int)value); break;
case TypeCode.Int64: Write((long)value); break;
case TypeCode.UInt32:Write((uint)value); break;
case TypeCode.UInt64:Write((ulong)value); break;
case TypeCode.Double:Write((double)value); break;
case TypeCode.Object:
if (type.IsArray)
// the issue is here
WriteVector((Array)value);
break;
default:
break;
}
}
internal static void WriteVector(Array array)
{
if (array == null) { return; }
int count = array.Length;
// save count value
var elementType = array.GetType().GetElementType();
for (int i = 0; i < count; i++)
WriteValue(array.GetValue(i), elementType);
}
// My delphi code
procedure WriteValue(Value: TValue; valueType: PTypeInfo);
begin
case Value.Kind of
tkArray:
WriteVector(Value.AsType<TArray>);
end;
end;
procedure WriteVector(array: TArray);
var
count: Integer;
i: Integer;
begin
if array = nil then
begin
Exit;
end;
// count := Length(array);
// save count value
// how the rest can be correctly written ?
end;

UPD. Sorry, I haven't updated the page before posting. I'll leave it unchaged.
Well... I see some kind of cyclic/recursive stuff, where WriteValue calls WriteVector, WriteVector calls WriteValue and that's all. valueType param is not used, non-array values are ignored. Without additional details it's hard to advise good solution. As an option you may use Variant type.
procedure WriteValue(vValue: Variant);
begin
if ( VarIsArray(vValue) ) then begin
WriteVector(vValue);
end
else if ( VarIsStr(vValue) ) then
begin
end
else if ( VarIsType(vValue, [varShortInt, varDate]) ) then
begin
end
else begin
case VarType(vValue) of
varByte: begin
end;
varDouble: begin
end;
else
// default
end;
end;
end;
procedure WriteVector(vArray: Variant);
var
i: Integer;
begin
if ( not VarIsArray(vArray) ) then Exit;
if ( VarArrayDimCount(vArray) <> 1 ) then Exit;
for i := VarArrayLowBound(vArray, 1) to VarArrayHighBound(vArray, 1) do
WriteValue( VarArrayGet(vArray, [i]) );
end;
procedure Test();
var
v: variant;
begin
v := VarArrayOf( ['0', Now(), Byte(1), Double(2), VarArrayOf([3, 4, 5])] );
WriteValue(v);
WriteVector(Null);
end;

Related

C# pointer to struct array

Hi I would like to create a pointer to struct array
I have a 2 structs like this
public struct TRecord
{
string Key;
string Value;
}
public struct TDB
{
public int Count;
TRecord[] Records;
}
but because I am writing a plugin for Delphi app which passes DBPointer pointer (DBPointer has an array of data) I need to point DBPointer to TDB so I can get the data out of it. When trying to do this like
public unsafe TDB* DBPointer; //DBPointer points to TDB struct which has struct array
I get
Error CS0208 Cannot take the address of, get the size of, or declare a pointer to a managed type
and as I read I can't declare a pointer to a string and a struct
So I don't know what to do now because if writing this in pascal it works fine
type TRecord = record
Key: shortstring;
Value: shortstring;
end;
type TPRecord = record
Key: PChar;
Value: PChar;
end;
type TDB = record
Count: integer;
Records: array [0..255] of TRecord;
end;
PTDB = ^TDB;
function ReadValue(Key: PChar; DBPointer: PTDB): PChar; stdcall;
var a,i: integer;
SKey: shortstring;
begin
Result:='';
SKey:=shortstring(Key);
a:=DBPointer^.Count;
if (a>length(DBPointer^.Records)) then a:=length(DBPointer^.Records); //ochrana
for i:=1 to a do
begin
if (DBPointer^.Records[i-1].Key=SKey) then
begin Result:=StrPCopy(Param1,DBPointer^.Records[i-1].Value); exit; end;
end;
end;
Thanks for Anwsering and Best Regards

C# Dll library does not return output parameters value to a Delphi application

I've written a Dll with C# with an exported function that save a file.
Here's the C# code
using RGiesecke.DllExport;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.IO;
namespace ClassLibrary1
{
public class Class1
{
[DllExport("Funcion", CallingConvention = CallingConvention.StdCall)]
public static void Funcion(IntPtr pDataIn, Int32 pSize, [Out, MarshalAs(UnmanagedType.I4)] int pArchivo)
{
byte[] documento = new byte[pSize];
Marshal.Copy(pDataIn, documento, 0, pSize);
File.WriteAllBytes("Document2.pdf", documento);
pArchivo = 25;
}
}
}
In Delphi, I load the library and call the exported function and it works fine.
Here's the Delphi Code
procedure TForm1.Button1Click(Sender: TObject);
var
lStream : TMemoryStream;
lArBytes : array of Byte;
lInDocSize : Integer;
lHndle : THandle;
Funcion : procedure(pDataIn : array of Byte;
pInSize : Integer;
var pDocumento : Integer
); stdcall;
begin
try
lHndle := LoadLibrary('ClassLibrary1.dll');
if (lHndle <> 0) then
begin
Funcion := GetProcAddress(lHndle, 'Funcion');
if Assigned(Funcion) then
begin
try
lStream := TMemoryStream.Create;
lStream.LoadFromFile('Document1.PDF');
lStream.Position := 0;
SetLength(lArBytes, lStream.Size);
lStream.Read(lArBytes[0], lStream.Size);
lInDocSize := 0;
Funcion(lArBytes, lStream.Size, lInDocSize);
Label1.Caption := IntToStr(lInDocSize);
except on E : Exception do
begin
RaiseLastOSError;
ShowMessage(e.Message);
end;
end;
end;
end;
finally
end;
end;
I have an error with the output parameter, it is that always returns cero (0) value, not matter what value I assign to parameter, it always has cero value.
I've changing the parameter like this
out int pArchivo
and
ref int pArchivo
But when the function finish I get a memory exception.
With Marshal, function finish fine, without memory errors, but the output paramerter value is always cero (0).
[Out, MarshalAs(UnmanagedType.I4)] int pArchivo
I've read about this problem in this post in Stackoverflow
Passing array of struct from c# to Delphi
But in my case, it doesn't work
What I'm doing wrong?
I hope that you could help... thank you so much
On the Delphi side, a function parameter directly declared as array of ... is known as an "open array". An "open array" gets passed by the compiler using 2 parameters - a pointer to the first array element, and the high index (not the length!) of the array. This allows the calling code to pass either a static array or a dynamic array to the same parameter, and the compiler will pass the array data accordingly.
But, your .NET code is expecting only 1 parameter for the array - a raw pointer to the 1st array element. That is why you are not getting your output value correctly. Your lStream.Size parameter value gets passed where the .NET code expects the 3rd parameter to be, so the Size value gets misinterpreted as the memory address where the .NET code writes its output pArchivo value to, hence the memory error. Which won't matter anyway since you are corrupting the call stack! You end up pushing 4 parameter values onto the stack, and then stdcall on the .NET side pops off only 3 parameter values during stack cleanup when the function exits.
You need to change the declaration of your Funcion variable, either by:
keeping the pDataIn parameter declared as array of Byte, but removing the explicit pInSize parameter. Let the compiler pass it implicitly:
Funcion : procedure(pDataIn : array of Byte;
//pInSize : Integer;
var pDocumento : Integer
); stdcall;
You will then have to change the call of Funcion() to allocate +1 more byte for the lArBytes array so the compiler passes the correct size value to the pSize parameter:
SetLength(lArBytes, lStream.Size+1); // <-- +1 here!
lStream.Read(PByte(lArBytes)^, High(lArBytes)); // <-- no +1 here!
...
Funcion(lArBytes{, High(lArBytes)}, lInDocSize);
Needless to say, this is not intuitive, though it should work since the behavior of open arrays is well-known, though it is a private implementation detail of the Delphi compiler.
using PByte (or just Pointer) instead of array of Byte:
Funcion : procedure(pDataIn : PByte; // <-- here
pInSize : Integer;
var pDocumento : Integer
); stdcall;
You will then have to change the call of Funcion() to pass a pointer to the 1st array element, and pass the array length explicitly:
SetLength(lArBytes, lStream.Size); // <-- no +1 here!
lStream.Read(PByte(lArBytes)^, Length(lArBytes)); // <-- or here!
...
Funcion(#lArBytes[0]{or: PByte(lArBytes)}, Length(lArBytes), lInDocSize);
This is more intuitive, and closer to what the .NET code is expecting.
Alternatively, I suggest you simply get rid of your lArBytes variable altogether. You don't actually need it. Since the .NET code is expecting a raw pointer to the byte data, simply pass your TMemoryStream data directly:
procedure TForm1.Button1Click(Sender: TObject);
var
lStream : TMemoryStream;
lInDocSize : Integer;
lHndle : THandle;
Funcion : procedure(pDataIn : Pointer;
pInSize : Integer;
var pDocumento : Integer
); stdcall;
begin
lHndle := LoadLibrary('ClassLibrary1.dll');
if (lHndle <> 0) then
try
Funcion := GetProcAddress(lHndle, 'Funcion');
if Assigned(Funcion) then
begin
lInDocSize := 0;
lStream := TMemoryStream.Create;
try
lStream.LoadFromFile('Document1.PDF');
Funcion(lStream.Memory, lStream.Size, lInDocSize);
finally
lStream.Free;
end;
Label1.Caption := IntToStr(lInDocSize);
end;
finally
FreeLibrary(lHndle);
end;
end;

Cast of a C# object to string in delphi

I'm converting a c# project to delphi XE5 and I'm stuck on a (i believe) simple task.
public void TransferFile(object o, Stream stream)
{
string s = o as string;
if (s != null)
{ do something }
else { do something else }
}
where o contains an handle of an object (in this case its value is 689520929) and the cast to string reports null, which is the expected result.
I translated it in delphi as for the following:
procedure TransferFile(o: OleVariant; stream: TFileStream);
var
s: string;
begin
s:= IntToStr(o);
if (s <> '') then do something
else do something else
end;
In this case casting the variant as string reports the number representation (689520929), which leads the program flow to a different behaviour.
Help appreciated :)
Use VarIsStr() to check if an (Ole)Variant holds a string value or not, eg:
procedure TransferFile(o: OleVariant; stream: TFileStream);
var
s: string;
begin
if VarIsStr(o) then begin
s := VarToStr(o);
// do something...
end else begin
// do something else...
end;
end;

Passing a list or array from Arden MLM to C#

I'm having a pretty hard time finding documentation on Lists and Arrays in Arden MLM.
I am trying to pass a list of 4 digit numbers from Arden using ObjectsPlus to a C# DLL that can take that list as an argument and do what the function is designed for.
Here is what I have in Arden MLM but does not work as I get the .net error "Object reference not set to an instance of an object"
Here is the MLM:
list_id_object := OBJECT [id_list_holder];
id_list := new list_id_object with "1154", "1155", "1158";
try
send_alert_start := new net_object 'Webservices';
result := call send_alert_start.'pageToMultipleIds' with
((sender_name as string) as 'String'),
((sender_message as string) as 'String'),
((list_id_object as list) as 'List<Int32>');
endtry;
catch Exception ex
error_occured := true;
error_message := "Error message here\n" || ex.Message || "\n\n";
endcatch;
And here is the C# method that receives that list:
public string testMethod(string sender_name, string sender_message, List<Int32> IdToPage)
{
try
{
testMethod2(sender_name, sender_message, IdToPage);
return "Success";
}
catch(WebException e)
{
return e.ToString();
}
}
This is written for Allscripts SCM but it should show how to build a List. Unfortunately you have to create a standard Arden list and then loop through it and add each item to the List.
std_include_libs := mlm'std_include_libs';
include std_include_libs;
id_list := 1154, 1155, 1158;
try;
using "System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
using namespace "System.Collections.Generic";
idList := new net_object 'List<Int32>';
for i in id_list do
void := call idList.'Add' with i as 'Int32';
enddo;
send_alert_start := new net_object 'Webservices';
result := call send_alert_start.'pageToMultipleIds' with ((sender_name as string) as 'String'),
((sender_message as string) as 'String'),
idList;
endtry;
catch Exception ex;
error_occurred := true;
error_message := "Error message here\n" || ex.Message || "\n\n";
endcatch;
see change below.
((list_id_object as list) as 'List<Int32>');
changed to
((list_id as list) as 'List<Int32>');
.
list_id_object := OBJECT [id_list_holder];
id_list := new list_id_object with "1154", "1155", "1158";
try
send_alert_start := new net_object 'Webservices';
result := call send_alert_start.'pageToMultipleIds' with
((sender_name as string) as 'String'),
((sender_message as string) as 'String'),
((list_id as list) as 'List<Int32>');
endtry;
catch Exception ex
error_occured := true;
error_message := "Error message here\n" || ex.Message || "\n\n";
endcatch;

delphi return array of integer from a COM object function

A typelibrary is created from C # and used that to in Delphi 5.
there is a method in type library that is returning a array or string. In Delphi same array of string I have to get but when I compiled the code following error is comming.
Incompatible types: 'tagSAFEARRAY' and 'Array'
Here is C # code
public int[] sqrRootUpto(int num)
{
int[] result={0};
int tempVal=0;
for (int i = 2; num < tempVal; i++)
{
tempVal = i * i;
result[i] = tempVal;
}
return result;
}
Here is Delphi code Where I am calling those above function.
procedure TForm1.BtnSqrtClick(Sender: TObject);
var
num :Integer;
result : array of Integer;
begin
num := StrToInt(EditSqrtInput.text);
result := newObj.sqrRootUpto(num); //Here I am calling the above method
end;
end.
EDIT
var
result : variant;
begin
result := VarArrayCreate([0, 20], varInteger);
Error!! Incompatible types: 'tagSAFEARRAY' and 'Array'
TBL.pas having this signature for the function
function TMathClass.sqrRootUpto(rNum: Integer): PSafeArray;
begin
Result := DefaultInterface.sqrRootUpto(rNum);
end;
what is diff b/w TSafeArray and TSafeArray
There is already TSafeArray record defined in ActiveX.pas. Maybe this can help you.
try declaring
var
result: TIntegerDynArray;
Edit: After your edit, it's clear that your COM method returns a PSafeArray. An easiest way to use an array of Integer would be to convert it to a variant array first:
function IntSafeArrayToVarArray(const P: PSafeArray): OleVariant;
begin
if Assigned(P) then
begin
VarClear(Result);
tagVariant(Result).vt := varInteger or varArray;
tagVariant(Result).PArray := P;
end
else
Result := Null;
end;
You can then use this function because variant array of integers is assignment-compatible with a TIntegerDynArray:
result := IntSafeArrayToVarArray(newObj.sqrRootUpto(num));
for I := Low(result) to High(Result) do ...

Categories