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.

LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Creating a pointer to void to pointer

Solved!
Go to solution

Hello!

I've have this C DLL that needs void** targetbuffer. Basically as below

void* p[4]

p[1] = int* buffer0

..

p[3]=int*buffer3

 

Func(void** p)

if that makes any sense..

 

Anyhow I've tried to use the DSNewPtr to first make a void* of size 32 as I need to store 4*64bit pointers. Then I create 4 new pointers with DSNewPtr and all with size 2000 as I want to read 1000 samples per ch later on in 16bit format.

 

I fill these memories with data and then try to read it out but am not successfull.

 

I've made an example VI of what I want to achieve, anyone has any clues how to get this to work?

Best Regards
Jonas Mäki
Systems Developer
Novator Solutions
0 Kudos
Message 1 of 24
(4,513 Views)
Solution
Accepted by topic author JonasM

I do not understand your sample code. Where are you calling your DLL here? Are you trying to demonstrate building up the empty data structure to pass to the DLL, or retrieving the data after the call to the DLL, or both? Be careful about how you configure MoveBlock, you should NEVER be using the "Handles by Value" data format in this sort of situation but that's how you've configured the first instance of MoveBlock. For building up the data structure to pass to the DLL, you don't need MoveBlock at all.

 

Can you share either the source code for the DLL function, or a better description of the data being passed to it?

 

If I've understood correctly, you want a pointer to an array of 4 pointer-sized elements. Each element points at a buffer of 2000 bytes. Is that correct? Also, are you using the 32- or 64-bit version of LabVIEW? That will determine whether you need 32-bit or 64-bit pointers. Let's say you're using the 32-bit version since that's how you appear to have configured it (note, what matters here is whether LabVIEW is 64-bit, not whether the operating system is 64-bit). For building the data structure, all you need to do is take the output of your initial For loop (the array containing 4 pointers) and pass the resulting array of addresses as an array data pointer.

 

To get the data back out, loop through that array with MoveBlock, copying 2000 bytes from each address into a pre-allocated array.

 

I think you want something like this (note, I haven't double-checked all the Call Library Function Node configuration).void pointer.png

Message 2 of 24
(4,468 Views)

Thanks for a quick reply Nathand, as you say I'm trying to demonstrate it but poorly I get.

 

I need to have both 32 bit and 64 bit version of this, so if I can get it working for one I'm just copying it to the other after.

 

The source code for the LabVIEWHelper dll is attached. There you can see how the DLL creates a void pointer to pointer.

 

The example you made is perfect, however I cant just send the array of pointers I need a pointer to this array. This is what the LabVIEW helper should help with and that was what I wanted my example to do. But that helper dll only generates the same pointer value over and over on multiple calls, I need several for another function.

Best Regards
Jonas Mäki
Systems Developer
Novator Solutions
Download All
0 Kudos
Message 3 of 24
(4,457 Views)

Attached you see the end function that will use the pointer that should be passed as value. So this pointer should point to an array of pointers or allocated memory I guess from looking at the labview helper cpp

Best Regards
Jonas Mäki
Systems Developer
Novator Solutions
0 Kudos
Message 4 of 24
(4,446 Views)

JonasM wrote: 

The example you made is perfect, however I cant just send the array of pointers I need a pointer to this array.


An array is passed by pointer, so my code does exactly what you're describing: it passes a pointer to an array of pointers.

0 Kudos
Message 5 of 24
(4,432 Views)

Following the image you provided: if you set up your code the way I did in my example, you need to change the data type of the target_buffers parameter to an array of pointer-sized integers passed as an array data pointer, and then it should work.

0 Kudos
Message 6 of 24
(4,429 Views)

I will give this a try on monday when I have access to the hardware again.

 

Thanks For your help! I'll update here on my progress

Best Regards
Jonas Mäki
Systems Developer
Novator Solutions
0 Kudos
Message 7 of 24
(4,419 Views)

The snipped Nathan gave you works for both 32 bit and 64 bit naturally. You just have to make sure to configure your parameter of that function as Nathan did, as Array of Pointer sized (unsigned) Integer, passed as array pointer and LabVIEW will take care about copying the array of 64 bit pointers to a temporary array of 32 bit pointers before calling the function.

 

Your C code you provide does however not work for 64 bit compilation!

 

    return (unsigned long) p;

Unlike on Linux where a long is actually promoted to 64 bits when compiling for 64 bit targets, Windows keeps longs at 32 bit, so this code would truncate your pointer when you compile your code for 64 bits. Unless you use a dead old C compiler that does not supprt C99, it should come with a stdint.h header file that provides the uintptr_t datatype, which is exactly meant to be large enough to guarantee that any object pointer including void* can be converted into an uintptr_t and back and ending up with the same pointer as you had originally. If you do that on Windows with unsigned long you could end up with a different pointer as you loose the upper 32 bits of the 64 bit pointer on the first conversion.

 

But with the solutions as provided by Nathan you won't need that C file at all!

 

The only improvment you can make to Nathans code is by going into the Call Library Node configuration for DSNewPtr, MoveBlock, and DSDisposePtr and set them to run in any thread. These LabVIEW memory manager functions are fully thread safe!

Rolf Kalbermatter
My Blog
0 Kudos
Message 8 of 24
(4,396 Views)

That was the whole point to get rid of the C dll. This worked perfectly! thanks Nathand!

Best Regards
Jonas Mäki
Systems Developer
Novator Solutions
0 Kudos
Message 9 of 24
(4,381 Views)

I’ve run into some more issues, it worked very well with Nathands example code. Now I’ve run into a bit more complicated function that just will not run just yet. I was hoping to get your input and advice on how to setup the dll call for this function. We have tried many different settings on the dll call and different ways to send data to this dll. We have had some success with just sending int arrays with adapt to type and array by pointer. However we can then not read the data they write to this as we never declared that memory to be accessible to our knowledge.

This is the function that crashes LabVIEW just now
ADQ_GetDataStreaming(adq_cu, adq_num, buf_data, buf_headers, channelsmask, samples_added, headers_added, header_status);

 

See the attached image for documentation for this function. The zipfile contains the dll and the VI made from wizard and modified. I also show how we try to set things up to pass correct data to the dll. We havn't had any success yet. ANY help is very much appreciated.

 

This is an example code written in C ) a lot in here is not important but just how to setup this calls for this function seems to be our roadblocker right now.

void adq14_triggered_streaming(void *adq_cu, int adq_num) {
  //Setup ADQ
  int trig_mode;
  int trig_level;
  int trig_flank;
  unsigned int samples_per_record;
  unsigned int number_of_records;
  unsigned int pretrig_samples;
  unsigned int holdoff_samples;
  unsigned int success;
  unsigned int nofrecords[4];
  unsigned int nofrecords_masked;
  unsigned char channelsmask;
  unsigned int maskinput;
  unsigned int trig_channel;
  unsigned int tr_buf_size = 64*1024;
  unsigned int tr_buf_no = 4;
  unsigned int trig_freq = 0;
  unsigned int trig_period = 0;
  void* dest_data;
  void* dest_headers;
  unsigned int timeout_ms = 1000;

  time_t t0;
  time_t t1;

  unsigned int tlocal = ADQ_GetTemperature(adq_cu, adq_num, 0)/256;
  unsigned int tr1 = ADQ_GetTemperature(adq_cu, adq_num, 1)/256;
  unsigned int tr2 = ADQ_GetTemperature(adq_cu, adq_num, 2)/256;
  unsigned int tr3 = ADQ_GetTemperature(adq_cu, adq_num, 3)/256;
  unsigned int tr4 = ADQ_GetTemperature(adq_cu, adq_num, 4)/256;
  FILE* outfile[4] = {NULL, NULL, NULL, NULL};
  char *serialnumber;
  int exit=0;
  unsigned int ch = 0;
  unsigned int buffers_filled = 0;
  unsigned int record_samples = 0;

  // Buffers to handle the stream output (both headers and data)
  short* buf_data[4] = {NULL, NULL, NULL, NULL};
  short* buf_extradata[4] = {NULL, NULL, NULL, NULL};
  unsigned int buf_extradata_samples[4] = {0, 0, 0, 0};
  void* buf_headers[4] = {NULL, NULL, NULL, NULL};

  // Variables to handle the stream output (both headers and data)
  unsigned int header_status[4] = {0, 0, 0, 0};
  unsigned int headers_done = 0;
  unsigned int samples_added[4] = {0, 0, 0, 0};
  unsigned int headers_added[4] = {0, 0, 0, 0};
  unsigned int headers_completed[4] = {0, 0, 0, 0};

  printf("Temperatures:\n\tLocal: %u\n\tADC0: %u\n\tADC1: %u\n\tFPGA: %u\n\tPCB diode: %u\n\n",
    tlocal, tr1, tr2, tr3, tr4);
  
  serialnumber = ADQ_GetBoardSerialNumber(adq_cu, adq_num);

  printf("Device Serial Number: %s\n",serialnumber);

  printf("\nChoose trig mode.\n %d = SW Trigger Mode\n %d = External Trigger Mode\n %d = Level Trigger Mode\n %d = Internal trigger mode\n",
    ADQ_SW_TRIGGER_MODE, ADQ_EXT_TRIGGER_MODE, ADQ_LEVEL_TRIGGER_MODE, ADQ_INTERNAL_TRIGGER_MODE);
  scanf("%d", &trig_mode);

  switch (trig_mode)
  {
  case ADQ_SW_TRIGGER_MODE : {
    CHECKADQ(ADQ_SetTriggerMode(adq_cu,adq_num, trig_mode));
    break;
                             }
  case ADQ_EXT_TRIGGER_MODE : {
    CHECKADQ(ADQ_SetTriggerMode(adq_cu,adq_num, trig_mode));
    break;
                              }
  case ADQ_LEVEL_TRIGGER_MODE : {
    CHECKADQ(ADQ_SetTriggerMode(adq_cu, adq_num, trig_mode));
    printf("\nChoose trig level.\n -32768 <= level <= 32767\n");
    scanf("%d", &trig_level);
    CHECKADQ(ADQ_SetLvlTrigLevel(adq_cu, adq_num, trig_level));
    printf("\nChoose trig edge.\n 1 = Rising edge\n 0 = Falling edge\n");
    scanf("%d", &trig_flank);
    CHECKADQ(ADQ_SetLvlTrigEdge(adq_cu, adq_num, trig_flank));
    scanf("%d", &trig_channel);
    CHECKADQ(ADQ_SetLvlTrigChannel(adq_cu, adq_num, trig_channel));
    printf("\n");
    break;
                                }
  case ADQ_INTERNAL_TRIGGER_MODE : {
    CHECKADQ(ADQ_SetTriggerMode(adq_cu,adq_num, trig_mode));
    printf("\nChoose trigger frequency in Hz\n");
    scanf("%d", &trig_freq);
    trig_period = 1000000000/trig_freq;
    CHECKADQ(ADQ_SetInternalTriggerPeriod(adq_cu, adq_num, trig_period));
    printf("\n");
    break;
                              }
  default :
    return;
    break;
  }

  printf("\nChoose number of records (-1 for infinite mode).\n");
  scanf("%d", &number_of_records);

  printf("\nChoose number of samples per record.\n");
  scanf("%d", &samples_per_record);
  
  printf("\nChoose number of milliseconds for flush timeout (proposal 1000 ms).\n");
  scanf("%d", &timeout_ms);

  channelsmask = 0x00;
  printf("\nEnable channel A data collection? (0 or 1)\n");
  scanf("%d", &maskinput);
  if(maskinput > 0)
    channelsmask |= 0x01;

  printf("\nEnable channel B data collection? (0 or 1)\n");
  scanf("%d", &maskinput);
  if(maskinput > 0)
    channelsmask |= 0x02;

  printf("\nEnable channel C data collection? (0 or 1)\n");
  scanf("%d", &maskinput);
  if(maskinput > 0)
    channelsmask |= 0x04;

  printf("\nEnable channel D data collection? (0 or 1)\n");
  scanf("%d", &maskinput);
  if(maskinput > 0)
    channelsmask |= 0x08;

  // Allocate buffers
  for (ch = 0; ch < 4; ch++)
  {
    if (channelsmask & (1 << ch))
    {
      // Data and header buffer must be equal in size to transfer buffer to guarantee room for all data
      buf_data[ch] = malloc(tr_buf_size);
      buf_headers[ch] = malloc(tr_buf_size);

      // The extra "temp" data buffer must be equal in size to one record size to guarantee room for data
      buf_extradata[ch] = malloc(sizeof(short)*samples_per_record);

      if ((buf_data[ch] == NULL) || (buf_headers[ch] == NULL) || (buf_extradata[ch] == NULL))
      {
        printf("ERROR: Failed to allocate memory for required buffers.\n");
        goto error;
      }
    }
  }

  // Allocate space for the destination data (where data is handled)
  dest_data = malloc(tr_buf_size);
  dest_headers = malloc(tr_buf_size);

  if ((dest_data == NULL) || (dest_headers == NULL))
  {
        printf("ERROR: Failed to allocate memory for required destination buffers.\n");
        goto error;  
  }

  pretrig_samples = 0;
  holdoff_samples = 0;
  
  // Use triggered streaming for data collection.
  CHECKADQ(ADQ_TriggeredStreamingSetup(adq_cu,adq_num,number_of_records,samples_per_record, pretrig_samples, holdoff_samples, channelsmask));

  // Commands to start the triggered streaming mode after setup
  CHECKADQ(ADQ_SetStreamStatus(adq_cu, adq_num, 1));
  CHECKADQ(ADQ_SetTransferBuffers(adq_cu, adq_num, tr_buf_no, tr_buf_size));
  CHECKADQ(ADQ_StopStreaming(adq_cu, adq_num));
  CHECKADQ(ADQ_StartStreaming(adq_cu, adq_num));
  // When StartStreaming is issued device is armed and ready to accept triggers
  for (ch = 0; ch < 4; ch++)
    nofrecords[ch] = 0;
  nofrecords_masked = 0;

  t0 = time(NULL);
  t1 = t0;
  success = 1;
  while (success && (nofrecords_masked < number_of_records)) // nofrecords_masked will contain max of used channels
  {
    buffers_filled = 0;
    while (success && (buffers_filled == 0))
    {
      success = success && ADQ_GetTransferBufferStatus(adq_cu, adq_num, &buffers_filled);
      t1 = time(NULL);
      if (difftime(t1, t0) > ((double)timeout_ms / 1000.0))
      {
        printf("Timeout reached. Flushing buffers.\n");
        success = success && ADQ_FlushDMA(adq_cu, adq_num);
        t0 = t1;
      }
    }

    if (success && (buffers_filled > 0))
    {
      t0 = t1; // Update base time for flush check, as we had at least one filled buffer here
    }

    if (ADQ_GetStreamOverflow(adq_cu, adq_num))
    {
      printf("Data transfer has overflowed.\n");
      success = 0;
    }

    // We know here that at least one buffer is filled with data
    success = success && ADQ_GetDataStreaming(adq_cu, adq_num, buf_data, buf_headers, channelsmask, samples_added, headers_added, header_status);
    if (success)
    {
      for (ch = 0; ch < 4; ch++)
      {
        headers_done = 0;
        if (headers_added[ch] > 0)
        {
          if (header_status[ch])
          {
            headers_done = headers_added[ch];
          }
          else
          {
            // One incomplete header
            headers_done = headers_added[ch]-1;
          }
          headers_completed[ch] += headers_done;

          // Here we need to read out all header info as we potentially alter it soon

          // Copy the finished headers to destination
          memcpy(dest_headers, buf_headers, headers_done * sizeof(typeHeaderADQ14));

          if (headers_done != headers_added[ch])
          {
            // Copy the incomplete header to the beginning of the buffer (will destroy info in buffer!)
            memcpy((void*)buf_headers[ch], (void*)((typeHeaderADQ14*)buf_headers[ch]+headers_done-1), sizeof(typeHeaderADQ14));
          }
        }
        
        // headers_done now contains the number of finished records available for this channel
        // and dest_headers contain the headers for the finished records.
        // Remains to copy the data for the finished records.

        // Copy all data (for finished records) to destination (which will use the data).
        // Save "extra data", contents from next unfinished record to next iteration
        record_samples = headers_done * samples_per_record;
        nofrecords[ch] += headers_done;
        if ((channelsmask & (1 << ch)) && (nofrecords_masked < nofrecords[ch]))
          nofrecords_masked = nofrecords[ch];

        if (headers_done > 0)
        {
          unsigned int tocopy = 0;

          memcpy(dest_data, buf_extradata[ch], buf_extradata_samples[ch]*sizeof(short));
          record_samples -= buf_extradata_samples[ch];
          tocopy = MIN(samples_added[ch], record_samples);
          memcpy((void*)((short*)dest_data+buf_extradata_samples[ch]), buf_data[ch], tocopy*sizeof(short));
          buf_extradata_samples[ch] = 0;
          samples_added[ch] -= tocopy;
        }

        // Here, destination pointer contains record data for all records for this channel
        // for instance call a separate function to handle data here
        // HandleData(ch, headers_done, dest_data, dest_headers);
        // HandleData(Channel, NumberOfRecords, data_ptr, header_ptr)
        if (headers_done > 0)
          printf("%02u records from channel %u retrieved (total on channel %05u records).\n", headers_done, ch, nofrecords[ch]);
        // Are there still data left to store to next time (data for yet unfinished records)?
        // We have to store both the data and the size of the data to know how to use it in 
        // next iteration.
        if (samples_added[ch] > 0)
        {
          // Store data in slatt buffers
          memcpy(buf_extradata[ch], buf_data[ch], samples_added[ch]*sizeof(short));
          buf_extradata_samples[ch] = samples_added[ch];
        }

      }
    }
  }

  if (success)
    printf("Done.\n");
  else
    printf("Error occurred.\n");

error:
  ADQ_StopStreaming(adq_cu, adq_num);

  for(ch = 0; ch < 4; ch++)
  {
    if (buf_data[ch] != NULL)
      free(buf_data[ch]);
    if (buf_headers[ch] != NULL)
      free(buf_headers[ch]);
    if (buf_extradata[ch] != NULL)
      free(buf_extradata[ch]);
  }

  if (dest_data != NULL)
    free(dest_data);
  if (dest_headers != NULL)
    free(dest_headers);

  printf("Press 0 followed by ENTER to exit.\n");
  scanf("%d", &exit);

  return;

}
Best Regards
Jonas Mäki
Systems Developer
Novator Solutions
Download All
0 Kudos
Message 10 of 24
(4,353 Views)