From 04:00 PM CDT – 08:00 PM CDT (09:00 PM UTC – 01:00 AM UTC) Tuesday, April 16, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

VirtualBench

cancel
Showing results for 
Search instead for 
Did you mean: 

DLLImport for the C# programming

Solved!
Go to solution

Hi,

 

I'm programming with C#, I have problem at the  niVB_Initialize function,

below sample code compile OK. But when I try to debug, there is error and no respond LibraryHandle value.

 

My environment : WIN7 32bit, VS2013 community(compiled x86)

 

Anybody advice to clear this ?

 

=================================================

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace AUA_niVB
{
class Program
{
    [DllImport("nivirtualbench.lib", CallingConvention = CallingConvention.Cdecl)]
    public unsafe static extern Int32 niVB_Initialize(UInt32 niVB_LIBRARY_VERSION, Int32* libHandle);

    static void Main(string[] args)
        {

            Int32 i;
            UInt32 LibVer = 17874944;
        try
        {
            unsafe
            {
                Int32 LHandle;
                i = niVB_Initialize(LibVer, &LHandle);
            }
        }
        catch (System.BadImageFormatException e)
        {
            int a = 0;
        }

    } //Main
}//class Program
} //namespace AUA_niVB

0 Kudos
Message 1 of 9
(13,246 Views)
Solution
Accepted by topic author hiawath

The only thing I can see in your code that I'm unsure about is the use of "nivirtualbench.lib" as the name of the DLL; nivirtualbench.lib is an import library for "nilcicapi.dll". I think the pointer/address-of syntax that you have with "unsafe" is correct, although I'm more a C++ guy than a C# guy.

 

I've been able to use the following with the VirtualBench C API. It also avoids the use of the "unsafe" keyword.

 

namespace Example

{

class Program

{

public const UInt32 LIBRARY_VERSION = 17874944;
public const string DLL_NAME = "nilcicapi.dll";
[DllImport(DLL_NAME, EntryPoint="niVB_Initialize", CallingConvention=CallingConvention.Cdecl)]
public static extern Int32 Initialize(UInt32 version, out IntPtr libraryHandle);
[DllImport(DLL_NAME, EntryPoint="niVB_Finalize", CallingConvention=CallingConvention.Cdecl)]
public static extern Int32 Finalize(IntPtr libraryHandle);

 

static void Main(string[] args)

{

IntPtr libHandle;

Initialize(LIBRARY_VERSION, out libHandle);

 

// do stuff with libHandle

 

Finalize(libHandle);

}

}

}

 

——
Brandon Streiff
ni.com/compactdaq · ni.com/daq
Message 2 of 9
(13,227 Views)

Thank you for advice Brandon!!

 

By the way, how can I get "nilcicapi.dll"?

 

I try to find at NI.COM & googling but, I can;t find.

 

Please let me know...

0 Kudos
Message 3 of 9
(13,212 Views)
Solution
Accepted by topic author hiawath

You get nilcicapi.dll with the "VirtualBench Runtime" subfeature in the NI-VirtualBench installer. (It's also installed as part of the "Typical" set of options.)

 

It installs into the system directory (e.g., c:\Windows\System32 and/or c:\Windows\SysWOW64 depending on OS bitness), so it should be part of the default library path. If you have nivirtualbench.lib, you probably already have it installed.

——
Brandon Streiff
ni.com/compactdaq · ni.com/daq
0 Kudos
Message 4 of 9
(13,208 Views)

Thank you Brandon again!!.

 

 

After installing the driver file, I found  "nilcicapi.dll" and work fine!

 

I was confusing Application file(NIVirtualBench111.exe) and Driver file (NIVirtualBench111 (1).exe).

 

At the first time I only install Application file, so I can't find  "nilcicapi.dll".

 

Thank you again!

0 Kudos
Message 5 of 9
(13,194 Views)

Dear Everyone,

 

After DllImport function working  very well, I almost finished except for the "MSO_ReadAnalog"

 

This is somewhat different method to convert pointer to IntPtr.

I try to convert pointer to IntPtr, array, but there is many error at the runtime.

I used internal retagular wave to capture, so analogDataSize is assign for the memory allocation.

 

I attached file,  please advice for this problem.

 

 

Thanks,

 

 

    [DllImport(DLL_NAME, EntryPoint = "niVB_MSO_ReadAnalog", CallingConvention = CallingConvention.Cdecl)]
    public static extern short MSO_ReadAnalog(IntPtr MSO_instrumentHandle,
         out double[] data,
           uint dataSize,
        out uint dataSizeOut,
         out uint dataStride,
        out CniVB.niVB_Timestamp initialTimestamp,
        out CniVB.niVB_Timestamp triggerTimestamp,
        out CniVB.niVB_MSO_TriggerReason triggerReason);

/*----------------------------------------------------------------------------*/
analogDataSize = 80002;

CniVB.MSO_ReadAnalog(
msoHandle_1,
out analogData,
analogDataSize,
out dataSizeOut,
out analogDataStride,
out analogT0,
out triggerTimestamp,
out triggerReason);
/*----------------------------------------------------------------------------*/

 

Download All
0 Kudos
Message 6 of 9
(13,157 Views)

Using "out double[]" tells the CLR that the native side of the call is going to allocate an array of doubles using the COM allocator (that is, using CoTaskMemAlloc) and pass ownership of it to the .NET runtime.

 

The VirtualBench C API uses a more traditional C model of memory ownership, in that it expects the caller to allocate a buffer and pass a pointer to the library to fill it.

 

In C, this would look like:

double* data = NULL;
size_t dataSize = 0;
size_t stride = 0;
niVB_Timestamp initialTimestamp;
niVB_Timestamp triggerTimestamp;
niVB_MSO_TriggerReason triggerReason;
// ... niVB_MSO_ReadAnalog( instrumentHandle, NULL, /* data */ 0, /* dataSize */ &dataSize, NULL, /* stride */ NULL, /* initial timestamp */ NULL, /* trigger timestamp */ NULL); /* trigger reason */ // niVB_MSO_ReadAnalog returns number of elements to allocate in "dataSizeOut" data = (double*)calloc(dataSizeOut, sizeof(double)); niVB_MSO_ReadAnalog( instrumentHandle, data, dataSize, &dataSize, &stride, &initialTimestamp, &triggerTimestamp, &triggerReason);
// ... do stuff with "data" ...
free(data);

In your case, the approach I would take would be to turn the out-array parameters into IntPtr in the P/Invoke signature (which tells the CLR to avoid doing anything special). Then, on the managed side, call ReadAnalog once (with IntPtr.Zero as "NULL"), create a new double[dataSize], and either using the fixed keyword or a GCHandle to get the address of that array to pass into a second call to ReadAnalog.

 

(Sorry, I don't have C# example code ready for this one.)

——
Brandon Streiff
ni.com/compactdaq · ni.com/daq
Message 7 of 9
(13,148 Views)
    [DllImport(DLL_NAME, EntryPoint = "niVB_MSO_ReadAnalog", CallingConvention = CallingConvention.Cdecl)]
    public static extern short MSO_ReadAnalog(IntPtr MSO_instrumentHandle,
           out double[] data,
           uint dataSize,
         uint dataSizeOut,
          uint dataStride,
          CniVB.niVB_Timestamp initialTimestamp,
         CniVB.niVB_Timestamp triggerTimestamp,
         CniVB.niVB_MSO_TriggerReason triggerReason);

////////////////////////////////////////////////////////////////////////////////////////////////
                double[] analogData = new double[20];
                analogDataSize = 20;
                try
                {
                    CniVB.MSO_ReadAnalog(
                         msoHandle_1,
                         out analogData,
                         analogDataSize,
                         dataSizeOut,
                          analogDataStride,
                           analogT0,
                           triggerTimestamp,
                           triggerReason);

                }

Dear bstreiff,

 

I did as follow your advice, but still generated error message during runtime.

 

The message was " protected memory damage...."

 

It's very helpful to me if possible to prepare for this C# sample code.

I spent a whole week, but loose my path...

 

Sorry for bothering you again.

 

Thanks,

Hiawath

0 Kudos
Message 8 of 9
(13,114 Views)

As I said before, using "out double[] data" in your P/Invoke signature tells the CLR that niVB_MSO_ReadAnalog is going to allocate memory on your behalf to be returned to the CLR, however, niVB_MSO_ReadAnalog doesn't do that.

 

You need to call niVB_MSO_ReadAnalog once to determine the size of the buffer that you need to create, then you call it again, providing a pointer to your buffer (using fixed or by using GCHandle pinning), in order to have niVB_MSO_ReadAnalog fill it. See the attached example.

 

(You should also be using "UIntPtr" for size_t parameters, otherwise you'll have problems on x64. Also, the return type for all API methods is Int32 (or int), not short.)

——
Brandon Streiff
ni.com/compactdaq · ni.com/daq
0 Kudos
Message 9 of 9
(13,102 Views)