Question

I have a C++ dll file which contains an exported function named fn(double* p, int* pLength), where, p is a pointer (is an out array used in C#), pLength which is calculated in this function is the length (size) of p . Code here:

void _stdcall fn(double* p, int* pLength)
{
    int i=0;
    double p_=0;
    do
    {
        p[i]=p_;
        p_=an expression!!!!!;
        i++;
    }
    while (condition with p_);  //Condition of loop
 
    *pLength=i;
 
    return;
}

I compile to dll file successfuly. This file is named "testFile.dll" and move it to System32 folder. Now, I start C# console project and declare that exported function fn() from "testFile.dll", and this code is:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
 
namespace ConsoleApplication1
{
    class Program
    {
        [DllImport("testFile.dll")]
        public static extern void fn(double[] p, ref int pLength);
        static void Main(string[] args)
        {
            // I want to declare an array with unknown length (size),
            // because the length is calculated from fn() function!!!
            // And pass to fn() function to get p[i].....
            double[] p;
            int pLength;
            fn(p, ref pLength);
 
            // Display pLength and p[i]
            Console.WriteLine(pLength);
            for (int i = 0; i < pLength; i++)
                Console.WriteLine(p[i]);
            Console.ReadKey();
        }
    }
}

I run and get two errors:

Error 1 Use of unassigned local variable 'p'

Error 2 Use of unassigned local variable 'pLength'

How to fix them?, and I want to get pLength and p[i] fully from fn() function in "testFile.dll". Thanks in advance.

Was it helpful?

Solution

As I understand it, you will need to call the function twice. Once you will pass an empty array and ask the function to calculate the required length. And then again you call with an allocated array which the function will populate.

The unmanaged code might look like this:

void __stdcall fn(double p[], int* pLength)
{
    int i = 0;
    double p_ = 0;
    do
    {
        if (p) p[i] = p_;
        p_ = ...;
        i++;
    }
    while (...);
    *pLength = i;
}

Note that the code checks whether or not p is assigned, and only writes to the array if it is.

On the managed side the code looks like this:

[DllImport(...)]
public static extern void fn(double[] p, out int pLength);
....
int pLength;
fn(null, out pLength);
double[] p = new double[pLength];
fn(p, out pLength);

A safer version of this would be to pass the length of the array to the function. That would allow the function to make sure that it does not write off the end.

void __stdcall fn(double p[], int* pLength)
{
    int i = 0;
    double p_ = 0;
    do
    {
        if (p)
        {
            if (i >= *pLength) return -1; // or some other error code
            p[i] = p_;
        }
        p_ = ...;
        i++;
    }
    while (...);
    *pLength = i;
}

And the managed side:

[DllImport(...)]
public static extern void fn(double[] p, ref int pLength);
....
int pLength = 0;
fn(null, ref pLength);
double[] p = new double[pLength];
fn(p, ref pLength);

If you only want to call the function once then you need to do the following:

  1. Allocate memory dynamically in the unmanaged code. Use GlobalAlloc so that the managed code can deallocate it.
  2. Be prepared to re-allocate in the unmanaged code if your initial allocation proved to be of insufficient length.
  3. Use an out parameter of type IntPtr in the managed code to get the pointer to unmanaged memory.
  4. Use Marshal.Copy to copy the array contents into your managed double[] object.
  5. Call Marshal.FreeHGlobal to free the unmanaged memory.

This technique is much more complex and involves a lot more boiler-plate. You only take this approach when there is a significant performance overhead in the first approach that I outlined.


An aside. Please don't put your files in the system directory. It belongs to the system and you are expected not to modify it. Place the DLL in the same directory as the host executable.

OTHER TIPS

The error you are getting is to do with the fact that you are not assigning an array any value. Though the function doesn't care how large the array is it still needs to work on valid memory.

Try the following:

int pLength = 20;
double[] p = new double[pLength];
fn(p, ref pLength);

And see if the error goes away.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top