I have this piece of code and it generates an error:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyItem
{
[MarshalAs(UnmanagedType.LPWStr)]
public string Name;
public int ID;
public double ID1;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MyItem[] items = new MyItem[6];
items[0].Name = "JFK";
items[0].ID = 35;
items[1].Name = "LBJ";
items[1].ID = 36;
items[2].Name = "Tricky Dicky";
items[2].ID = 37;
items[3].Name = "Gerald Ford";
items[3].ID = 38;
items[4].Name = "Jimmy Carter";
items[4].ID = 39;
items[5].Name = "Ronald Reagan";
items[5].ID = 40;
IntPtr itemsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyItem)) *
items.Length);
try
{
IntPtr item = new IntPtr(itemsPtr.ToInt32());
for (int i = 0; i < items.Length; i++)
{
Marshal.StructureToPtr(items[i], item, true);
item = new IntPtr(item.ToInt32() + Marshal.SizeOf(typeof(MyItem)));
}
}
finally
{
Marshal.FreeHGlobal(itemsPtr);
}
When I run this code, I am getting write protection error in
Marshal.StructureToPtr(items[i], item, true);
What is the problem and how do I solve it?
You should be passing false to the fDeleteOld parameter of StructureToPtr().
By passing true you are asking the marshaller to delete the contents of item. Since you are filling this out for the first time, this results in a memory access failure because the memory is not yet valid.
The documentation states:
StructureToPtr copies the contents of
structure to the pre-allocated block of
memory that the ptr parameter points
to. If the fDeleteOld parameter is true,
the pre-allocated buffer is deleted with
the appropriate deletion method on the embedded pointer, but the buffer
must contain valid data.
The emphasis is mine.
Incidentally I think the loop code looks neater like this:
Int32 addr = itemsPtr.ToInt32();
for (int i = 0; i < items.Length; i++)
{
Marshal.StructureToPtr(items[i], new IntPtr(addr), false);
addr += Marshal.SizeOf(typeof(MyItem));
}
Related
I'm working on a driver for a scanner. Got dll and header files from provider, aside of a manual in PDF, written in native C++ (dont have source code). Need to use it within a C# project but I'm having problems with the structs (either trying to read or send them).
I got the dll methods using the Command Prompt, demangling them in a website (since it has +100). Of course, I wont use all of them, just the ones I need. Didn't have problems with the ones using primitive data types, in fact, made the scanner turn on/off, scan and such.
My main problem is the following: I need to set some parameters, because with the default ones I'm not getting the needed info (and that's the MOST important thing I need, actually). The only way to do it is with a method that include 2 params: the ID (just an int) and the setting (an struct). That struct has internally not one, but 2 different structs instances (and one of them is an array, within another kind of struct as one of the params). In other words, gonna need to work with 4 different structs.
I declared all my structs, following the template provided in the .h file, and inmported the method. When I try to do a test it keeps giving me an error. I believe the problem is the array of structs. I tried with a normal passing, Marshaling, using a pin to array, changing data type, adding a "MarshalAs" with all bool vars... nothing seems to work.
Been trying to solve this for days already. Dont know what am I doing wrong (because this is my first time importing methods). I read about the C++/Cli thing but never worked with that either.
See code below (I kinda modified a little because of confidentiality of the information)
This is how is defined in the .h file (C++)
// The structs (won't add all parameters, but are basically the same type)
typedef struct _ImParam
{
UINT Format;
UINT Resolution;
UINT ColorDepth;
} IM_PARAM;
typedef struct _sValues
{
UINT Xpos;
UINT Ypos;
UINT Width;
UINT Height;
BOOL Milli;
} S_VALUES;
typedef struct _sProperties
{
BOOL Enable;
S_VALUES Properties;
} S_PROPERTIES;
typedef struct _DevParam
{
BOOL Enable;
UINT Font;
char Symbol;
IM_PARAM Image1;
IM_PARAM Image2;
S_PROPERTIES Properties[10];
UINT FeedMode;
} DevParam;
// more code, comments, etc. etc.
// This is how is defined
BOOL SetParameters( DWORD ID, DevParams DParam )
This is how I build the structs in C#
[StructLayout(LayoutKind.Sequential)]
public struct ImParam
{
public uint Format;
public uint Resolution;
public uint ColorDepth;
public ImParam(uint n)
{
Format = n;
Resolution = 300;
ColorDepth = 256;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct sValues
{
public uint Xpos;
public uint Ypos;
public uint Width;
public uint Height;
public bool Milli;
public sValues(uint n)
{
Xpos = n;
Ypos = n;
Width = n;
Height = n;
Milli = false;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct sProperties
{
public bool Enable;
public sValues Properties;
public sProperties(int n)
{
Enable = false;
Front = false;
Properties = new sValues(n);
}
};
// Commented code is from another attemp
[StructLayout(LayoutKind.Sequential)]
public struct DevParam
{
public bool Enable;
public uint Font;
public char Symbol;
public ImParam Image1;
public ImParam Image2;
public IntPtr Properties;
//public sProperties[] Properties;
public uint FeedMode;
public DeviceParameters(IntPtr SnP) //(int n)
{
Enable = true;
Font = 0;
Symbol = '?';
Image1 = new ImParam(3);
Image2 = new ImParam(3);
Properties = SnP;
/*Properties = new sProperties[n];
*for(int i = 0; i < n; i++)
* Properties[i] = new sProperties(0);*/
FeedMode = 1;
}
};
// .dll file path definition, some methods imported, etc. etc.
[DllImport(path, EntryPoint = "?SetParameters##YGHKU_DevParam###Z")]
public static extern bool SetParameters(int ID, DevParam dParam);
And this is when I do call it (added commented code to show you my attemps)
static void Main(string[] args)
{
bool res = false;
int ID;
sProperties[] SnP = new sProperties[10];
for (int i = 0; i < 10; i++)
SnP[i] = new sProperties(0);
try
{
// Some code to turn on scanner, get ID value and such
/* Attemp1: Passing the struct normaly.
* Result: ArgumentException [HRESULT: 0x80070057 (E_INVALIDARG))]
* try
* {
* DevParam dParam = new DevParam(10);
* res = Class1.SetParameters(ID, dParam);
* Console.WriteLine(res);
* }
* catch (Exception e) { Console.WriteLine(e); }*/
/* Attemp2: Marshaling each element of the array.
* Result: The managed PInvoke signature doesnt mach the destination one
* int S = Marshal.SizeOf(typeof(sProperties));
* DevParam dParam = new DevParam(Marshal.AllocHGlobal(SnP.Length*S));
* IntPtr ptr = dParam.Properties;
* for (int i = 0; i < SnP.Length; i++)
* {
* Marshal.StructureToPtr(SnP[i], ptr, false);
* ptr += S;
* }
* try
* {
* res = Class1.SetDevParam(ID, dParam);
* Console.WriteLine(res);
* }
* finally { Marshal.FreeHGlobal(dParam.sProperties); }*/
/* Attemp3: Adding a Pin Pointer to struct
* Result: Exception (Object has no primitive data and it can't
* be transfered into bits blocks) */
GCHandle SpHandle = GCHandle.Alloc(SnP, GCHandleType.Pinned);
try
{
DevParam dParam = new DevParam(SpHandle.AddrOfPinnedObject());
res = Class1.SetParameters(ID, dParam);
Console.WriteLine(res);
}
catch (Exception e) { Console.WriteLine(e); }
finally { SpHandle.Free(); }
// More code for testing other methods and blahblahblah
}
catch (Exception e) { Console.WriteLine(e); }
Console.WriteLine("Finished");
Console.ReadKey();
}
What do I expect? Getting just a boolean result to see if method executed sucessfully (and of course, if true, scanner should have defined the new parameters)
What do I get? A bunch of exceptions.
Please, any help would be great. Thanks in advance.
PD: Sorry for that long post.
PD2: I'm quite rockie, so please try to explain it "for dummies"
Thanks Hans. Seems it worked!
Just modiffied the struct as suggested:
[StructLayout(LayoutKind.Sequential)]
public struct DevParam
{
public bool Enable;
public uint Font;
public char Symbol;
public ImParam Image1;
public ImParam Image2;
//public IntPtr Properties;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public sProperties[] Properties;
public uint FeedMode;
public DeviceParameters(int n) //(IntPtr SnP)
{
Enable = true;
Font = 0;
Symbol = '?';
Image1 = new ImParam(3);
Image2 = new ImParam(3);
//Properties = SnP;
Properties = new sProperties[n];
for(int i = 0; i < n; i++)
Properties[i] = new sProperties(0);
FeedMode = 1;
}
};
And used the "Attemp1" code.
Hello I'm wrapping C++ library with C#. Next function in C++:
SCREENCAPTUREDLL_API wchar_t** getAudioDeviceList(int* listSize) {
static std::vector<wchar_t*> descriptionList;
AudioCaptureList::getInstance().Update();
AudioCaptureList::getInstance().getList(&descriptionList);
*listSize = descriptionList.size();
return &descriptionList[0];
}
Wrapping with next C# code:
[DllImport(screenCaptureDLLPath, CallingConvention = callConversion)]
private static extern IntPtr getAudioDeviceList(ref int arrayCount);
public static string[] GetAudioDeviceList()
{
IntPtr outputStr;
int length = 0;
outputStr = getAudioDeviceList(ref length);
string[] resultArray = new string[length];
for (int j = 0; j < length; j++)
{
resultArray[j] = Marshal.PtrToStringUni(Marshal.ReadIntPtr(outputStr, 4 * j));
}
return resultArray;
}
That works perfect, exactly as I expected, but I was about to change the way I returning value from function itself to variable by reference, so I changing my code to:
C++
SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t** list, int* listSize) {
static std::vector<wchar_t*> descriptionList;
AudioCaptureList::getInstance().Update();
AudioCaptureList::getInstance().getList(&descriptionList);
*listSize = descriptionList.size();
list = &descriptionList[0];
}
C#
[DllImport(screenCaptureDLLPath, CallingConvention = callConversion)]
private static extern void getAudioDeviceList(out IntPtr listRef, ref int arrayCount);
public static string[] GetAudioDeviceList()
{
IntPtr outputStr;
int length = 0;
getAudioDeviceList(out outputStr, ref length);
string[] resultArray = new string[length];
for (int j = 0; j < length; j++)
{
resultArray[j] = Marshal.PtrToStringUni(Marshal.ReadIntPtr(outputStr, 4 * j));
}
return resultArray;
}
But I got error, returned memory address is zero. What is the problem here? Please help me understood what cause the problem and how to fix that, thanks!
Why doesn't Pinvoke work? Because you are trying to interpret a pointer to a string as a pointer to a set of strings. But there is nothing wrong with PInvoke - it happens because there is actually a problem with new function signature and its internal code.
See:
SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t** listRef, int* listSize);
can't provide the same data like
DLL_API wchar_t** getAudioDeviceList(int* listSize)
Because original definition basically returned pointer to a set of pointers to strings(C style strings, I mean), while wchar_t** listRef can only allow to return a single pointer to a string.
SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t** listRef, int* listSize)
{
...
*listRef = "string";
I don't know what is going inside new version of the function(you didn't show the code), but listRef = &descriptionList[0]; will compile though won't do anything, and even if *listRef = &descriptionList[0]; somehow compiles it won't contain what you want.
So function signature should containt triple pointer to allow return of a set of strings.
SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t*** listRef, int* listSize)
{
...
*listRef = &descriptionList[0];
}
Then your PInvoke would work correctly because it will have the same pointer to a set of string pointers.
I am using the following line to convert my WTS_PROCESS_INFO object's ProcessName property to a string:
string name = Marshal.PtrToStringUni(processInfos[I].processName)
This seems to be quite reliable, and all the process names are successfully converted.
However, if I make this same call after passing WTS_PROCESS_INFO to another class and method, each attempt at converting to a string results in "" apart from the first attempt.
This only is only happening when using .NET-3.5 but seems to work fine in .NET-4.0
Example:
Working - each name is converted to the correct string before passing the entire list.
public static List<Proc> WTSEnumerateProcesses(string servername)
{
IntPtr pProcessInfo = IntPtr.Zero;
int processCount = 0;
var hServer = OpenServer(servername);
if (!Native.WTSEnumerateProcesses(hServer, 0, 1, ref pProcessInfo, ref processCount))
return null;
IntPtr pMemory = pProcessInfo;
WTS_PROCESS_INFO[] processInfos = new WTS_PROCESS_INFO[processCount];
for (int i = 0; i < processCount; i++)
{
processInfos[i] = (WTS_PROCESS_INFO)Marshal.PtrToStructure(pProcessInfo, typeof(WTS_PROCESS_INFO));
pProcessInfo = (IntPtr)((int)pProcessInfo + Marshal.SizeOf(processInfos[i]));
}
List<Proc> asd = new List<Proc>();
foreach (var item in processInfos)
{
if (item.SessionID == 5)
{
string procname = Marshal.PtrToStringUni(item.ProcessName);
var proc = new Proc(servername, 5, item.SessionID, procname);
asd.Add(proc);
}
}
Native.WTSFreeMemory(pMemory);
return asd;
}
Not working - the conversions which take place after passing the WTS_PROCESS_INFOs out result in empty strings (apart from the first one).
public static WTS_PROCESS_INFO[] WTSEnumerateProcesses(string servername)
{
IntPtr pProcessInfo = IntPtr.Zero;
int processCount = 0;
var hServer = OpenServer(servername);
if (!Native.WTSEnumerateProcesses(hServer, 0, 1, ref pProcessInfo, ref processCount))
return null;
IntPtr pMemory = pProcessInfo;
WTS_PROCESS_INFO[] processInfos = new WTS_PROCESS_INFO[processCount];
for (int i = 0; i < processCount; i++)
{
processInfos[i] = (WTS_PROCESS_INFO)Marshal.PtrToStructure(pProcessInfo, typeof(WTS_PROCESS_INFO));
pProcessInfo = (IntPtr)((int)pProcessInfo + Marshal.SizeOf(processInfos[i]));
}
Native.WTSFreeMemory(pMemory);
return processInfos;
}
Can anyone explain why this is happening?
Because you are calling Native.WTSFreeMemory(pMemory); that is freeing the memory associated with the strings. This call is freeing both the pMemory that is an array of WTS_PROCESS_INFO AND the memory pointed from the various pProcessName. So the solution is to copy somewhere those strings BEFORE calling Native.WTSFreeMemory(pMemory);
There are quite a few questions on here about MemberwiseClone, but I can't find anything covering this exactly.
As I understand it, MemberwiseClone basically just copies an area of memory for an object, dumps it somewhere else and calls it a new instance of that object. Obviously very quick, and for large objects it is the quickest way to make a copy. For small objects with simple constructors, it's quicker to create an instance and copy the individual properties.
Now, I have a tight loop in which I'm using MemberwiseClone. As MemberwiseClone always creates a new instance this leads to a lot of memory allocation, and reallocation which isn't great for performance. I've written a custom copy for a very large object that copies all the properties individually onto an existing object, which overall is marginally quicker than using MemberwiseClone,
I feel that if I could grab the whole chunk of memory and dump it onto the existing object, I'd achieve a decent performance boost as the GC wouldn't have to worry about anything. Hopefully I'll also get rid of this message:
Just to clarify, I want to copy the properties from one existing object to another as quickly as possible. A shallow copy is all I need.
Message 2 DA0023: (Average)% Time in GC = 19.30;
% Time in GC is relatively high. This indication of excessive amount of garbage collection overhead could be impacting the
responsiveness of your application. You can gather .NET memory
allocation data and object lifetime information to understand the
pattern of memory allocation your application uses better.
I have no restrictions on code safety, speed is the aim of the game here.
No comments asking if this is really necessary or talking about premature optimisation please.
Thanks for your help.
Answer
Taking the advise below, of embedding a struct within the object and storing all properties in that. I could then take a copy of that struct to copy all the properties in a single assignment. This yielded more than a 50% speed increase compared to copying field by field, and about a 60% speed increase over creating a new object every time with MemberwiseClone.
If your class is not inheritable, you could store the entire state of your class in an exposed-field structure (expose fields, not properties!). This would require that you prefix all field names with the name of the structure, which would be ugly in the source code, but accessing fields in that structure will be just as fast as would be accessing fields that were directly placed in the enclosing class. Doing this, however, will gain you the ability to copy the structure from one object instance into another using a simple assignment statement.
In general you can't be faster than doing a copy field by field.
This in general. There is a single exception: if your class is blittable (so doesn't contain any reference to non-blittable classes) AND it's declared with the [StructLayout(LayoutKind.Sequential)] (or with the [StructLayout(LayoutKind.Explicit)], but it's more complex to test) then you can pin it
GCHandle handle = GCHandle.Alloc(refToYourClass, GCHandleType.Pinned);
from there a new world opens... You can directly copy byte-by-byte the content of your class with some unsafe (both as unsafe the keyboard and unsafe as "running-with-scissors") code.
BUT for this to work, your class MUST be blittable!
The fastest method is UnsafeCopy8 that copies blocks of 8 bytes (both at 32bits and at 64bits). The fastest PInvoke method is memcpy.
[StructLayout(LayoutKind.Sequential)]
class MyClass
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
public int D { get; set; }
public int E { get; set; }
public int F { get; set; }
public int G { get; set; }
public byte H { get; set; }
}
class Program
{
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
static extern IntPtr memcpy(IntPtr dest, IntPtr src, UIntPtr count);
[DllImport("kernel32.dll", SetLastError = false)]
static extern void CopyMemory(IntPtr dest, IntPtr src, UIntPtr count);
static void Main()
{
MyClass mc = new MyClass { A = 1, B = 2, C = 3, D = 4, E = 5, F = 6, G = 7, H = 8 };
MyClass mc2 = new MyClass();
int size = Marshal.SizeOf(typeof(MyClass));
var gc = GCHandle.Alloc(mc, GCHandleType.Pinned);
var gc2 = GCHandle.Alloc(mc2, GCHandleType.Pinned);
IntPtr dest = gc2.AddrOfPinnedObject();
IntPtr src = gc.AddrOfPinnedObject();
// Pre-caching
memcpy(dest, src, (UIntPtr)size);
CopyMemory(dest, src, (UIntPtr)size);
UnsafeCopy(dest, src, size);
UnsafeCopy8(dest, src, size);
int cycles = 10000000;
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
var sw1 = Stopwatch.StartNew();
for (int i = 0; i < cycles; i++)
{
memcpy(dest, src, (UIntPtr)size);
}
sw1.Stop();
var sw2 = Stopwatch.StartNew();
for (int i = 0; i < cycles; i++)
{
CopyMemory(dest, src, (UIntPtr)size);
}
sw2.Stop();
var sw3 = Stopwatch.StartNew();
for (int i = 0; i < cycles; i++)
{
UnsafeCopy(dest, src, size);
}
sw3.Stop();
var sw4 = Stopwatch.StartNew();
for (int i = 0; i < cycles; i++)
{
UnsafeCopy8(dest, src, size);
}
sw4.Stop();
gc.Free();
gc2.Free();
Console.WriteLine("IntPtr.Size: {0}", IntPtr.Size);
Console.WriteLine("memcpy: {0}", sw1.ElapsedTicks);
Console.WriteLine("CopyMemory: {0}", sw2.ElapsedTicks);
Console.WriteLine("UnsafeCopy: {0}", sw3.ElapsedTicks);
Console.WriteLine("UnsafeCopy8: {0}", sw4.ElapsedTicks);
Console.ReadKey();
}
static unsafe void UnsafeCopy(IntPtr dest, IntPtr src, int size)
{
while (size >= sizeof(int))
{
*((int*)dest) = *((int*)src);
dest = (IntPtr)(((byte*)dest) + sizeof(int));
src = (IntPtr)(((byte*)src) + sizeof(int));
size -= sizeof(int);
}
if (size >= sizeof(short))
{
*((short*)dest) = *((short*)src);
dest = (IntPtr)(((byte*)dest) + sizeof(short));
src = (IntPtr)(((byte*)src) + sizeof(short));
size -= sizeof(short);
}
if (size == sizeof(byte))
{
*((byte*)dest) = *((byte*)src);
}
}
static unsafe void UnsafeCopy8(IntPtr dest, IntPtr src, int size)
{
while (size >= sizeof(long))
{
*((long*)dest) = *((long*)src);
dest = (IntPtr)(((byte*)dest) + sizeof(long));
src = (IntPtr)(((byte*)src) + sizeof(long));
size -= sizeof(long);
}
if (size >= sizeof(int))
{
*((int*)dest) = *((int*)src);
dest = (IntPtr)(((byte*)dest) + sizeof(int));
src = (IntPtr)(((byte*)src) + sizeof(int));
size -= sizeof(int);
}
if (size >= sizeof(short))
{
*((short*)dest) = *((short*)src);
dest = (IntPtr)(((byte*)dest) + sizeof(short));
src = (IntPtr)(((byte*)src) + sizeof(short));
size -= sizeof(short);
}
if (size == sizeof(byte))
{
*((byte*)dest) = *((byte*)src);
}
}
}
I need to use a native dll struct in my application.
struct da_i2k_input_file_info in the dll .h file is
struct DA_I2K_EXPORT_API da_i2k_input_file_info {
const WDCHAR * image_path;
const WDCHAR ** image_files;
int num_images;
};
and this is what user code might look like if written in C++
in_file_info.num_images = 3;
in_file_info.image_files = new const WDCHAR*[in_file_info.num_images];
in_file_info.image_files[0] = WSTR("IMG_8670.JPG");
in_file_info.image_files[1] = WSTR("IMG_8671.JPG");
in_file_info.image_files[2] = WSTR("IMG_8672.JPG");
But this C# code
[StructLayout(LayoutKind.Sequential)]
public struct da_i2k_input_file_info
{
[MarshalAs(UnmanagedType.LPTStr)]
public string image_path;
public IntPtr image_files;
public int num_images;
}
var openFileDialog = new OpenFileDialog{Multiselect = true};
da_i2k_input_file_info in_file_info;
in_file_info.image_path = null; // use full path in .image_files
in_file_info.num_images = openFileDialog.FileNames.Length;
in_file_info.image_files = openFileDialog.FileNames;
Causes this error
Cannot implicitly convert type 'string[]' to 'System.IntPtr'
Casting openFileDialog.FileNames as IntPtr does not help.
How can I load in_file_info.image_files from openFileDialog.FileNames?
Edit: OP cross posted this here
I believe you need to change you struct to be as follows:
[StructLayout(LayoutKind.Sequential)]
public struct da_i2k_input_file_info
{
[MarshalAs(UnmanagedType.LPTStr)]
public string image_path;
[MarshalAs(UnmanagedType.LPArray)]
public string[] image_files;
public int num_images;
}
A working answer was posted here.
Code follows.
(included by tomfanning)
private static void DoTest()
{
var openFileDialog = new OpenFileDialog { Multiselect = true };
da_i2k_input_file_info in_file_info;
openFileDialog.ShowDialog();
in_file_info.image_path = null; // use full path in .image_files
in_file_info.num_images = openFileDialog.FileNames.Length;
// Create an array of IntPtrs.
IntPtr[] image_files_array = new IntPtr[in_file_info.num_images];
// Each IntPtr array element will point to a copy of a
// string element in the openFileDialog.FileNames array.
for (int i = 0; i < openFileDialog.FileNames.Length; i++)
{
image_files_array[i] = Marshal.StringToCoTaskMemUni(openFileDialog.FileNames[i]);
}
// In order to obtain the address of the IntPtr array,
// we must fix it in memory. We do this using GCHandle.
GCHandle gch = GCHandle.Alloc(image_files_array, GCHandleType.Pinned);
// pimage_files will point to the head of the IntPtr array.
IntPtr pimage_files = gch.AddrOfPinnedObject();
// Set pimage_files as the value of the "image_files" field/
in_file_info.image_files = pimage_files;
// Call a Test API.
TestStructure(in_file_info);
// After the API is called, free the GCHandle.
gch.Free();
// Free each string contained in the IntPtr array.
for (int i = 0; i < openFileDialog.FileNames.Length; i++)
{
Marshal.FreeCoTaskMem(image_files_array[i]);
}
}