passing float pointer as IntPtr (pinvoke) - c#

Ok I have a DLL function declared as:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, ref IntPtr param_value);
This function takes many different params i.e in C++ this is how you would use it.
int i=2;
setParam("width", &i);
float k=2.5f;
setParam("factor", &f);
So I am trying to declare a C# functions to call this DLL api, I have got one working for the pointer to integer case:
public static void setWidth(int width)
{
IntPtr w = new IntPtr(width);
setParam("width", ref w);
}
But I cannot figure out how to do the second one where I pass a pointer to a float as IntPtr. Any ideas?
public static void setFactor(float f)
{
IntPtr w = new IntPtr(f); // WHAT GOES HERE??
setParam("factor", ref w);
}

Unless there's too many combinations, I'd say the best way is to simply have multiple DllImports for the various argument types. For example:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParamInt32(string param_name, ref int param_value);
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParamSingle(string param_name, ref float param_value);
You can then call them properly as
var intVal = 42;
setParamInt32("param", ref intVal);
var floatVal = 42.0f;
setParamSingle("param", ref floatVal);
Using ref IntPtr is wrong in either case - the only reason it works at all is that in 32-bit applications, IntPtr is a 32-bit integer internally. However, it's supposed to be a pointer. A proper use would be something like this:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, IntPtr param_value);
Note that the ref isn't there - IntPtr is already an indirection.
To call this, you'll need to allocate some memory, and get a pointer to that - or, use a GCHandle to refer directly to a managed object:
var intValue = 42;
var handle = GCHandle.Alloc(intValue, GCHandleType.Pinned);
setParam("param", handle.AddrOfPinnedObject());
Make sure to dispose of the managed handle properly, of course - pinned handles are a huge pain for the GC.
Manually copying the data to unmanaged memory and back also isn't exactly hard:
var ptr = Marshal.AllocCoTaskMem(sizeof(int));
try
{
Marshal.WriteInt32(ptr, 42);
setParam("param", ptr);
// If you need to read the value back:
var result = Marshal.ReadInt32(ptr);
}
finally
{
Marshal.FreeCoTaskMem(ptr);
}
But I'd simply stick with automatic marshalling unless you have a very good reason not to.

If you absolutely have to use IntPtr and can't just pass a floatparameter then you can use unsafe coding to pass a float pointer as IntPtr takes a void pointer as a parameter in one of its constructor. However the corresponding unmanaged function must also take a void pointer.
There is two ways you can deal with this, passing void* or passing IntPtr*. I would say passing void* is probably better, since you in general will be doing the same with IntPtr except for that IntPtr will be passed with the pointer instead of the function.
Option 1 - IntPtr
You first have to correct the p/invoke declaration by removing the erroneous ref
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, IntPtr param_value);
You do not pass IntPtr as ref either, just simply pass the instance of your IntPtr
public static unsafe void setFactor(float f)
{
IntPtr w = new IntPtr(&f);
setParam("factor", w);
}
Or if you want the unsafe declaration in the body
public static void setFactor(float f)
{
unsafe
{
IntPtr w = new IntPtr(&f);
setParam("factor", w);
}
}
Option 2 - void*
[DllImport(mDllName, EntryPoint = "SetParam")]
public unsafe static extern bool setParam(string param_name, void* param_value);
Then you can set it like
public static unsafe void setWidth(int width)
{
int* w = &width;
setParam("width", w);
}
And for the float
public static unsafe void setFactor(float f)
{
float* fptr = &f;
setParam("factor", fptr);
}

You can theoretically declare different DllImport entries for "safe" automatic marshalling:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, ref int param_value);
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, ref float param_value);
And use it like:
public static void setFactor(float f)
{
setParam("factor", ref f);
}
I have done this for other functions using void * as a signature and it works just fine, the ref <value_type> gets correctly passed in as a pointer.
Also, ref IntPtr should be for void ** (you'd use IntPtr for void *, not ref IntPtr)
If you are ok with unsafe, then you can use #Bauss solution
Furthermore, using the constructor IntPtr(int) is giving a location for the pointer, not the value at the location

Related

Wrapping c++ function to c# with total dynamic size of struct

I'm struggling with wrapping C++ function to C#.
I have very basic knowledge about these sort of wrapping but here i'm trying to find the "best solution".
Let's say I only have a .dll that contains C++ function. I only know there is a function with this signature :
static void GetInfos(LibraryInfo& infos);
And of course I know there is a class LibraryInfos
class LIBRARY_EXPORT_CLASS LibraryInfo
{
public:
const char* libVersion;
const char* libName;
const char* libDate;
};
};
Now I try to use this function in a C# test project :
static void Main(string[] args)
{
// Create Pointer
IntPtr ptr;
// Call function
GetInfos(out ptr);
// Get the 100 first byte (used only to demonstrate)
byte[] buffer = new byte[100];
Marshal.Copy(ptr, buffer, 0, 100);
// Display memory content
Console.WriteLine(Encoding.ASCII.GetString(buffer));
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out IntPtr test);
This code give me as output
v.1.2.5 ?I9
First : I know this is the really bad way of doing it, marshalling an
arbitrary length as a byte[] is only here just for demonstration.
Second : I only have the version, but if I'm calling the same dll
from a C++ project, I have the 3 field with data.
Third : Why did I use Marshal copy, and this arbitrary length of 100
? Because I didn't succeed in calling PtrToStruct, here is what I
tried :
[StructLayout(LayoutKind.Sequential)]
private struct LibInformation
{
public IntPtr Version; // I tried, char[], string, and IntPtr
public IntPtr Name;
public IntPtr Date;
}
static void Main(string[] args)
{
// Create Pointer
IntPtr ptr;
// Call function
GetInfos(out ptr);
if (ptr != IntPtr.Zero)
{
LibInformation infos = (LibInformation)Marshal.PtrToStructure(ptr, typeof(LibInformation));
}
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out IntPtr test);
Then I'm not able to retrieve my Version, Name and Date.
If I use IntPtr in my struct, I dont have the length of the String so
I can't realy marshal.Copy, nor PtrToStringAuto.
If I use Char[] or string it doesn't work.
I think my issue is about not knowing the size of the final response. so my best option for now is to make C++ project, calling this function from there then wrap this struct in a better one , that I can Marshal on the other side (with Length of each member as other member).
Any thought ?
[EDITS 1 Based on jdweng comment]
[StructLayout(LayoutKind.Sequential)]
private struct LibInformation
{
public IntPtr Version; // I tried, char[], string, and IntPtr
public IntPtr Name;
public IntPtr Date;
}
static void Main(string[] args)
{
// Create Pointer
IntPtr ptr;
// Call function
GetInfos(out ptr);
var data = Marshal.PtrToStructure<LibInformation>(ptr);
var version = Marshal.PtrToStringAnsi(data.Version);
Console.WriteLine(version) // result : ""
// Use Ptr directly as string instead of struct
var version2 = Marshal.PtrToStringAnsi(ptr);
Console.WriteLine(version2) // result : "v1.2.5" but how can i access other field ?
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out IntPtr test);
[EDITS 2 Based on jdweng 2snd comment]
[StructLayout(LayoutKind.Sequential)]
private struct LibInformation
{
public IntPtr Version;
public IntPtr Name;
public IntPtr Date;
}
static void Main(string[] args)
{
// Create Pointer for my structure
IntPtr ptr;
// Create 3 pointers and allocate them
IntPtr ptrVersion = Marshal.AllocHGlobal(100);
IntPtr ptrName = Marshal.AllocHGlobal(100);
IntPtr ptrDate = Marshal.AllocHGlobal(100);
// Then declare LibInformation and assign
LibInformation infos = new LibInformation();
// Here is probably my missunderstanding (an my error)
// As I need a ptr to call my function I have to get the Ptr of my struct
IntPtr ptr = Marshal.AllocHGlobal(300);
Marshal.StructureToPtr(infos, ptr, false);
// Assign
infos.Version = ptrVersion;
infos.Name = ptrName;
infos.Date = ptrDate;
// Call function
GetInfos(out ptr);
var data = Marshal.PtrToStructure<LibInformation>(ptr);
var version = Marshal.PtrToStringAnsi(data.Version);
Console.WriteLine(version) // result : still ""
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out IntPtr test);
I finally found the way to do it, thanks to the explanation of #jdweng and the link of #PaulF
The only bad news is that I have to arbitrary allocate n bytes before calling my functions
Link :
MSDN : marshaling-classes-structures-and-unions
Explanations (jdweng):
The parameter list of a method is on the execution stack. Once you return from the method the parameters list is not valid because it can be over written by the parent code. Only the return value of a method is valid. So you have to Allocate all the data before calling the method. So you need to allocate the variables version, name, and date in unmangaged memory. Then declare LibInformation and set version, name, and date to the memory locations allocated. Finally call the method. Then to get the three variables you have to call Marshal.PtrToStruct to copy results from unmanaged memory.
Final code :
[StructLayout(LayoutKind.Sequential)]
private struct LibInformation
{
public IntPtr Version;
public IntPtr Name;
public IntPtr Date;
}
static void Main(string[] args)
{
// Create 3 pointers and allocate them
IntPtr ptrVersion = Marshal.AllocHGlobal(100);
IntPtr ptrName = Marshal.AllocHGlobal(100);
IntPtr ptrDate = Marshal.AllocHGlobal(100);
// Then declare LibInformation and assign
LibInformation infos = new LibInformation();
// Assign
infos.Version = ptrVersion;
infos.Name = ptrName;
infos.Date = ptrDate;
// Call function
GetInfos(out infos);
var version = Marshal.PtrToStringAnsi(data.Version);
var name = Marshal.PtrToStringAnsi(data.Name);
var date = Marshal.PtrToStringAnsi(data.Date);
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out LibInformation test); // Changing IntPtr to LibInformation
[Edit 1 based on David Heffernan comment]
Here is the C code of a calling sample :
HComplexCTXPoint::LibraryInfo versionInfo;
HComplexCTXPoint::GetInfos(versionInfo);
std::cout << versionInfo.libName << std::endl;

PInvoke with a void * versus a struct with an IntPtr

Imagine I have a function called
Myfunction(const void * x);
My C# declaration could be
MyFunction(IntPtr x);
Is this functionally and technically equivalent to
struct MyStruct { IntPtr P; }
MyFunction(MyStruct x);
Or will there be a difference in how they are marshalled.
I'm asking this because the library I'm calling is all void *, typedef'd to other names, and in C# I'd like to get type safety, for what it's worth.
If your StructLayout is Sequential, then it is indeed identical.
Easiest way to verify this for yourself is to try it out, of course:
Make a C++ Win32 DLL project:
extern "C"
{
__declspec(dllexport) void MyFunction(const void* ptr)
{
// put a breakpoint and inspect
}
}
Make a C# project:
public struct Foo
{
public IntPtr x;
}
[DllImport(#"Win32Project1.dll", EntryPoint = "MyFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern void MyFunctionWithIntPtr(IntPtr x);
[DllImport(#"Win32Project1.dll", EntryPoint = "MyFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern void MyFunctionWithStruct(Foo x);
static void Main(string[] args)
{
IntPtr j = new IntPtr(10);
var s = new Foo();
s.x = new IntPtr(10);
MyFunctionWithIntPtr(j);
MyFunctionWithStruct(s);
}
In your debug settings, make sure you select Native debugging is enabled.
You'll see both values to be 0xA.
Note, however, if you use out/ref parameters for your IntPtr vs Struct, they will be different values.

how to call a C .dll from C#?

I have tried to call the C struct from C#, but I failed to make it work.
My struct define in C is as follow:
struct GHX{
double output[28];
int val;
};
And a function that I’m using to calculate the output is
__declspec(dllexport) void GHXfunction(
double *XIN, double *parameter,
int mode, int hour, GHX *result)
{
/*...*/
}
In C#,
I tried the following method.
[StructLayout(LayoutKind.Sequential)]
public struct GHX
{
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 28)]
public double[] output;
public int val;
}
[DllImport("GHXDLL.dll", EntryPoint = "GHXfunction",
CallingConvention = CallingConvention.Cdecl)]
public static extern void GHXfunction(
IntPtr XIN, IntPtr parameter, int mode,
int hour , ref GHX result);
So far, I believe that everything is OK, because someone from stackoverflow has the same problem as me.
Then, I just test a simple scenario as follow.
static void Main()
{
IntPtr XIN= IntPtr.Zero;
IntPtr Par=IntPtr.Zero;
int mode=1;
int hour=0;
GHX test = new GHX();
GHXfunction(XIN, Par, mode, hour, ref test);
}
It then till me that
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
Does anyone have idea about it?
Thanks.
Edit, I actually don't know how does IntPtr work. So I guessed IntPtr.Zero will return memory location with 0, maybe i was wrong.
In my original C code I call the function like this
double par[42], xin[5];
ptrxin = &xin[0];
ptrpara = &par[0];
struct GHX result;
GHXfunction(ptrxin, ptrpara, 0, 0,result);
I dont know how to call the function in C# with the pointer input.
Thanks.
Working code:
Basing on Ben's suggestion. I can call the struct in C#.
[StructLayout(LayoutKind.Sequential)]
public struct GHX
{
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 28)]
public double[] output;
public int val;
}
[DllImport("GHXDLL.dll", EntryPoint = "GHXfunction", CallingConvention = CallingConvention.Cdecl)]
public static extern void GHXfunction(double[] XIN, double[] parameter, int mode, int hour,ref GHX result);
static void Main()
{
double result;
double[] XIN= new double[5];
double[] Par = new double[42];
int mode = 1;
int hour=0;
GHX test = new GHX();
GHXfunction(XIN, Par, mode, hour,ref test);
result = test.output[0];
}
A C type of double* should be translated as either
ref double
or
double[]
Since your comments say those are arrays, try:
[DllImport("GHXDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GHXfunction(double[] XIN, double[] parameter, int mode,
int hour , ref GHX result);
P/invoke will take care of pinning the array and passing the address of the first element.

Marshalling PDouble (C# -> Delphi)

I've got an unmanaged DLL file written in Delphi, containing a function with the following definition:
function F(tgran: integer; inputs: PDouble; goutp, outputs, toutp: PDouble): integer; stdcall; external 'mydll.dll';
I've written an Adapter in C# that should help me consume it.
[UnmanagedFunctionPointer(APIAdapter.Convention)]
public delegate int FDelegate(int tgran, IntPtr inputs, IntPtr goutp, IntPtr outputs, IntPtr toutp);
public class APIAdapter : IDisposable
{
public const string DllName = "mydll.dll";
public const CallingConvention Convention = CallingConvention.StdCall;
public FDelegate F;
[DllImport("kernel32")]
private static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("kernel32")]
private static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, String procname);
private IntPtr _dllHandle;
public APIAdapter()
{
_dllHandle = LoadLibrary(DllName);
F = (FDelegate)GetFunction<CalcCavSpDelegate>("F");
}
private Delegate GetFunction<T>(string procName)
{
IntPtr procAddress = GetProcAddress(_dllHandle, procName);
return Marshal.GetDelegateForFunctionPointer(procAddress, typeof(T));
}
~APIAdapter()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing)
{
}
while (FreeLibrary(_dllHandle))
{
}
}
}
The usage is pretty straightforward:
using(var api = new APIAdapter())
{
// Call API functions
}
The problem is an AccessViolationException that happens within the DLL file.
I've tried to pass the PDouble variables as double[], double* (unsafe code), IntPtr. It's the same story regardless of the method I choose. I've tried to substantially increase the size of the arrays passed in to exclude errors with array indexing - AccessViolation exception again.
What is the proper way of passing a PDouble into an unmanaged Delphi DLL file?
I think you can do away with all the LoadLibrary, GetProcAddress and FreeLibrary complexity by simply using DllImport attribute. Though, I can say that I am not aware about any specific reason of why you chose to go this way.
Anyway, you can simply include ref double doubleArg in your declaration to pass PDouble. There should be no need of IntPtr here.

C# P/Invoke structure problem

I am trying to write a C# P/Invoke wrapper for a C API (a native Win dll), and generally this is working fine. The only exception is a specific method which takes a struct as a parameter in the C code. The function is invoked without any exceptions, but it returns false indicating that something failed in the execution.
In the API header file the involved method and structs are defined as follows:
#define MAX_ICE_MS_TRACK_LENGTH 256
typedef struct tagTRACKDATA
{
UINT nLength;
BYTE TrackData[MAX_ICE_MS_TRACK_LENGTH];
} TRACKDATA, FAR* LPTRACKDATA;
typedef const LPTRACKDATA LPCTRACKDATA;
BOOL ICEAPI EncodeMagstripe(HDC /*hDC*/,
LPCTRACKDATA /*pTrack1*/,
LPCTRACKDATA /*pTrack2*/,
LPCTRACKDATA /*pTrack3*/,
LPCTRACKDATA /*reserved*/);
I have made an attempt to create a C# P/Invoke wrapper using the following code:
public const int MAX_ICE_MS_TRACK_LENGTH = 256;
[StructLayout(LayoutKind.Sequential)]
public class MSTrackData {
public UInt32 nLength;
public readonly Byte[] TrackData = new byte[MAX_ICE_MS_TRACK_LENGTH];
}
[DllImport("ICE_API.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool EncodeMagstripe(IntPtr hDC,
[In]ref MSTrackData pTrack1,
[In]ref MSTrackData pTrack2,
[In]ref MSTrackData pTrack3,
[In]ref MSTrackData reserved);
Then I try to invoke the EncodeMagstripe method using the following C# code:
CardApi.MSTrackData trackNull = null;
CardApi.MSTrackData track2 = new CardApi.TrackData();
byte[] trackBytes = Encoding.ASCII.GetBytes(";0123456789?");
track2.nLength = (uint)trackBytes.Length;
Buffer.BlockCopy(trackBytes, 0, track2.TrackData, 0, trackBytes.Length);
if (!CardApi.EncodeMagstripe(hDC, ref trackNull, ref track2, ref trackNull, ref trackNull)) {
throw new ApplicationException("EncodeMagstripe failed", Marshal.GetLastWin32Error());
}
This causes an ApplicationException to be thrown, and the error code is 801 which according to the documentation means "Data includes too many characters for the selected Track 2 format.". However the selected track format should allow up to 39 characters (I have also tried shorter strings).
I suspect the problem occurrs due to something I did wrong in the MSTrackData definition, but I cannot see what this may be. Does anyone have any suggestions?
All the answers given so far have a bit of the answer but are incomplete. You need the MarshalAs - ByValArray as well as the new, your MSTrackDatas are already references so you do not need to pass them by ref and you must check what calling convention ICEAPI represents, if it is StdCall you don't need to change anything but if it is cdecl you will need to add the CallingConvention to your DllImport attribute. Also, you may need to add a MarshalAs attribute to your bool return value to make sure it is marshaled as 4 byte WinApi style bool. Here are the declares you'll (probably) need:
public const int MAX_ICE_MS_TRACK_LENGTH = 256;
[StructLayout(LayoutKind.Sequential)]
public class MSTrackData {
public UInt32 nLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public Byte[] TrackData = new byte[MAX_ICE_MS_TRACK_LENGTH];
}
[DllImport("ICE_API.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EncodeMagstripe(IntPtr hDC,
[In] MSTrackData pTrack1,
[In] MSTrackData pTrack2,
[In] MSTrackData pTrack3,
[In] MSTrackData reserved);
I would define the BYTE array not with new, but use the following code instead to initialise the right size:
[MarshalAs(UnmanagedType.byValTSt, SizeConst =256)]
public readonly Byte[] TrackData;
I have used this successfully on char arrays in the past.
Looks to me like the problem is that you're passing a reference by reference. Since MSTrackData is a class (i.e. reference type), passing it by reference is like passing a pointer-to-pointer.
Change your managed prototype to:
public static extern bool EncodeMagstripe(IntPtr hDC,
MSTrackData pTrack1,
MSTrackData pTrack2,
MSTrackData pTrack3,
MSTrackData reserved);
See the MSDN article about passing structures.
I had almost exactly the same problem - but with ReadMagstripe. And the solution provided here for EncodeMagstripe did not work for ReadMagstripe! I think the reason it did not work was that ReadMagstripe has to return data into the TRACKDATA structure/class, while EncodeMagstripe only passes data to the dll and data in TRACKDATA does not need to be changed. Here is the implementation that eventually worked for me - both with EncodeMagstripe and ReadMagstripe:
public const int MAX_ICE_MS_TRACK_LENGTH = 256;
[StructLayout(LayoutKind.Sequential)]
public struct TRACKDATA
{
public UInt32 nLength;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string szTrackData;
}
[DllImport("ICE_API.dll", EntryPoint="_ReadMagstripe#20", CharSet=CharSet.Auto,
CallingConvention=CallingConvention.Winapi, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ReadMagstripe(int hdc, ref TRACKDATA ptrack1, ref TRACKDATA ptrack2,
ref TRACKDATA ptrack3, ref TRACKDATA reserved);
[DllImport("ICE_API.dll", EntryPoint="_EncodeMagstripe#20", CharSet=CharSet.Auto,
CallingConvention = CallingConvention.Winapi, SetLastError=true)]
public static extern bool EncodeMagstripe(int hdc, [In] ref TRACKDATA ptrack1, [In] ref TRACKDATA ptrack2,
[In] ref TRACKDATA ptrack3, [In] ref TRACKDATA reserved);
/*
....
*/
private void EncodeMagstripe()
{
ICE_API.TRACKDATA track1Data = new ICE_API.TRACKDATA();
ICE_API.TRACKDATA track2Data = new ICE_API.TRACKDATA();
ICE_API.TRACKDATA track3Data = new ICE_API.TRACKDATA();
ICE_API.TRACKDATA reserved = new ICE_API.TRACKDATA();
//if read magstripe
bool bRes = ICE_API.ReadMagstripe(printer.Hdc, ref track1Data, ref track2Data,
ref track3Data, ref reserved);
//encode magstripe
if (bRes)
{
track2Data.szTrackData = "1234567890";
track2Data.nLength = 10;
bRes = ICE_API.EncodeMagstripe(printer.Hdc, ref track1Data, ref track2Data, ref track3Data, ref reserved);
}
}

Categories