Thread: Problem with marshalling strings in C++ for VS .NET 2003

  1. #1
    Registered User
    Join Date
    Jan 2011
    Posts
    10

    Problem with marshalling strings in C++ for VS .NET 2003

    Hello,

    I am working on a program for instrumentation hardware using Visual Studio C++ on the .NET 2003 framework. The code would work fine on XP, but we're trying to get everything on Windows 7. Almost all the conversion problems have been handled, but a particular string still has its problems. They compile fine, but when I run tests using the hardware, it throws up a buffer overflow exception. I have tried converting the String class back to a character array (using Marshalling attribute of the System::Runtime::InteropServices) so that the function can take in the character array parameter as defined in its arguments. But on compiling I now get the following error(s):

    Code:
    21462 error LNK2022: metadata operation failed (80131188) : Inconsistent field declarations in duplicated types (types: Pxi6509; fields: DeviceDescriptorChar): (0x04000014).
    
    21462 fatal error LNK1255: link failed because of metadata errors
    I believe this has something to do with the ordering of header files, but I can't put a finger on which one and where. Hoping somebody would know.


    The code is as:

    Code:
    #include "stdafx.h"
    #include "PXI6509.h"
    #include <stdio.h>
    
    using namespace System;
    using namespace System::Runtime::InteropServices;
    
    Pxi6509::Pxi6509(int DeviceNo)
    {
                    char ErrBuf [2048];
    	Char ErrorString[] = new Char __gc[2048];
                    int err;
    
    	Pxi6509::DeviceDescriptor = String::Format(S"PXI1Slot{0}", DeviceNo.ToString());
    	Pxi6509::DeviceDescriptorChar = (char*)(void*)Marshal::StringToHGlobalAnsi(DeviceDescriptor);
    	Marshal::FreeHGlobal(DeviceDescriptorChar);
    	
    	PortDirection = new int __gc[12];
    	DioPort = new TaskHandle __gc[12];
    	TaskHandle TempHandle;
    
    	for(int idx = 0; idx < 12; idx++)
    	{
    		err =  DAQmxCreateTask ("", &TempHandle);
    		if(err)
    		{
    			DAQmxGetExtendedErrorInfo(ErrBuf,2048);
    			for(int iy = 0; iy < 2048; iy++)
    				ErrorString[iy] = ErrBuf[iy];
    			String* Error = String::Format(S"Error Encountered in DAQmxCreateTask() for DioPort[{0}]: {1}", idx.ToString(), ErrorString->ToString());
    			throw new System::Exception(Error);
    		}
    		else
    		{
    			DioPort[idx] = TempHandle;
    		}
    	}
    
    	if((DeviceNo >= 0) && (DeviceNo < 9))
    	{
    		DeviceNumber = DeviceNo;
    	}
    	else
    	{
    		throw new Pxi6509DioException(S"Pxi6509::Pxi6509(). Device Number falls outside the range of this PXI Chassis");
    	}
    
    }
    	
    
    	Pxi6509::~Pxi6509()
                   {
    	for(int idx = 0; idx < 12; idx++) {
    		DAQmxStopTask(DioPort[idx]);
    		DAQmxClearTask(DioPort[idx]);
    	}
    	DAQmxResetDevice(Pxi6509::DeviceDescriptorChar);
    }
    
    
    void Pxi6509::ConfigureDioDirection(Pxi6509Direction Direction, Ports Port)
    {
    	char ErrBuf [2048];
    	Char ErrorString[] = new Char __gc[2048];
                    int err;
    	Pxi6509::DeviceDescriptor = String::Format(S"PXI1Slot{0}", DeviceNo.ToString());
    	Pxi6509::DeviceDescriptorChar = (char*)Marshal::StringToHGlobalAnsi(DeviceDescriptor).ToPointer();
    	Marshal::FreeHGlobal(IntPtr(DeviceDescriptorChar));
    	PortDirection = new int __gc[12];
    	DioPort = new TaskHandle __gc[12];
    	TaskHandle TempHandle;
    
    	for(int idx = 0; idx < 12; idx++)
    	{
    		err =  DAQmxCreateTask ("", &TempHandle);
    		if(err)
    		{
    			DAQmxGetExtendedErrorInfo(ErrBuf,2048);
    			for(int iy = 0; iy < 2048; iy++)
    				ErrorString[iy] = ErrBuf[iy];
    			String* Error = String::Format(S"Error Encountered in DAQmxCreateTask() for DioPort[{0}]: {1}", idx.ToString(), ErrorString->ToString());
    			throw new System::Exception(Error);
    		}
    		else
    		{
    			DioPort[idx] = TempHandle;
    		}
    	}
    
    	if((DeviceNo >= 0) && (DeviceNo < 9))
    	{
    		DeviceNumber = DeviceNo;
    	}
    	else
    	{
    		throw new Pxi6509DioException(S"Pxi6509::Pxi6509(). Device Number falls outside the range of this PXI Chassis");
    	}
    
    }
    	
    
    	Pxi6509::~Pxi6509()
    	{
    	for(int idx = 0; idx < 12; idx++) {
    		DAQmxStopTask(DioPort[idx]);
    		DAQmxClearTask(DioPort[idx]);
    	}
    	DAQmxResetDevice(Pxi6509::DeviceDescriptorChar);
    
    
    void Pxi6509::ConfigureDioDirection(Pxi6509Direction Direction, Ports Port)
    {
    	char ErrBuf [2048];
    	Char ErrorString[] = new Char __gc[2048];
                    int err;
    	char* PhyPortChar = (char*)Marshal::StringToHGlobalAnsi(PhyPort).ToPointer();
    	Marshal::FreeHGlobal(IntPtr(PhyPortChar));
    
    	if(Direction == Pxi6509Direction::Output) {
    		err = DAQmxCreateDOChan(DioPort[int(Port)], PhyPortChar,"",DAQmx_Val_ChanForAllLines);
    		PortDirection[int(Port)] = 1;
    	}
    	else {
    		err = DAQmxCreateDIChan(DioPort[int(Port)], PhyPortChar,"",DAQmx_Val_ChanForAllLines);
    		PortDirection[int(Port)] = 0;
    	}
    	if(err)
    	{
    		DAQmxGetExtendedErrorInfo(ErrBuf,2048);
                                    for(int idx = 0; idx < 2048; idx++)
    			ErrorString[idx] = ErrBuf[idx];
    		String* Error = String::Format(S"Error Encountered in Pxi6509::ConfigureDioDirection() DAQmxCreateDIOChan: {0}", ErrorString->ToString());
    		throw new System::Exception(Error);
    	}
    
    	err = DAQmxStartTask(DioPort[int(Port)]);
    	if(err) {
    		DAQmxGetExtendedErrorInfo(ErrBuf,2048);
    		for(int idx = 0; idx < 2048; idx++)
    			ErrorString[idx] = ErrBuf[idx];
    		String* Error = String::Format(S"Error Encountered in Pxi6509::ConfigureDioDirection() DAQmxStartTask(): {0}", ErrorString->ToString());
    		throw new System::Exception(Error);
    	}
    
    }
    
    void Pxi6509::DigitalOutput(Ports Port, uInt8 Data) 
    {
    	char ErrBuf [2048];
    	Char ErrorString[] = new Char __gc[2048];
                    int err;
    	int32 sampsWritten;
    	if(Pxi6509::PortDirection[int(Port)]) {
    		err = DAQmxWriteDigitalU8 (DioPort[int(Port)], 1, 1, 10.0, DAQmx_Val_GroupByChannel, &Data, &sampsWritten, NULL);
    		if(err) {
    			DAQmxGetExtendedErrorInfo(ErrBuf,2048);
    			for(int idx = 0; idx < 2048; idx++)
    				ErrorString[idx] = ErrBuf[idx];
    			String* Error = String::Format(S"Error Encountered in Pxi6509::DigitalOutput() DAQmxWriteDigitalScalarU32(): {0}", ErrorString->ToString());
    			throw new System::Exception(Error);
    		}
    	}
    	else {
    		throw new System::Exception(S"Pxi6509::DigitalOutput(): You are trying to write to a DIO port that's configured as an Input!");
    	}
    
    
    uInt8 Pxi6509::DigitalInput(Ports Port)
    {
    	char ErrBuf [2048];
    	Char ErrorString[] = new Char __gc[2048];
        int err;
    	int32 sampsRead =0;
    	uInt8 Data;
    
    	if(PortDirection[int(Port)] == 0) {
    		err = DAQmxReadDigitalU8 (DioPort[int(Port)], 1, 1.0, DAQmx_Val_GroupByChannel, &Data, 1, &sampsRead, NULL);
    		if(err) {
    			DAQmxGetExtendedErrorInfo(ErrBuf,2048);
    			for(int idx = 0; idx < 2048; idx++)
    				ErrorString[idx] = ErrBuf[idx];
    			String* Error = String::Format(S"Error Encountered in Pxi6509::DigitalInput() DAQmxReadDigitalScalarU32(): {0}", ErrorString->ToString());
    			throw new System::Exception(Error);
    		}
    	}
    	else {
    		throw new System::Exception(S"Pxi6509::DigitalInput(): You are trying to Read from a DIO port that's configured as an Output!");
    	}
    	return (Data);
    
    } // Pxi6509::DigitalInput()

  2. #2
    Registered User
    Join Date
    Jan 2011
    Posts
    10
    The code for the header file is:

    Code:
    #pragma once
    
    #include "NIDAQmx.h"
    
    #using <mscorlib.dll>
    #using <system.dll>
    #using <system.windows.forms.dll>
    
    using namespace System;
    using namespace System::IO;
    
    
    public __value enum Ports {Port0, Port1, Port2, Port3, Port4, Port5, Port6, Port7, Port8, Port9, Port10, Port11};
    
    public __value enum Pxi6509Direction {Input, Output};
    
    public __gc class Pxi6509DioException : public System::Exception
    	{
    	public:
    		Pxi6509DioException(String* Msg) : Exception(Msg) {}
    	};
    
    
    
    public __gc class Pxi6509 {
    public:
    	Pxi6509(int DeviceNo);
    	~Pxi6509();
    
    	__property void set_DeviceNumber(Int16 Slot)
    	{	deviceNumber = Slot; }
    	__property Int16 get_DeviceNumber(void)
    	{	return deviceNumber; }
    
    
    	void ConfigureDioDirection(Pxi6509Direction Direction, Ports Port);
    	void DigitalOutput(Ports Port, uInt8 Data);
    	uInt8 DigitalInput(Ports Port);
    	
    
    private:
    	TaskHandle DioPort __gc[];
    	Int16 deviceNumber;
    	static String* DeviceDescriptor;
    	char* DeviceDescriptorChar;
    	static int PortDirection __gc[];
    
    };

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    I think you need to take a few steps back and have a serious think about where this code is going (yes, I've followed your other thread as well).

    For me, you have a confusing mix of C, C++ and Managed C++ all in the same files!

    This is not good for a long term maintenance perspective.
    Sure, you might struggle through to an answer (that stops crashing), but it's going to be the same mine-field for the next person that comes along.

    For example, running to sprintf() because it gives you an easy answer is not what you should be doing in the C++ code. Figure out how to do the job "the C++ way" and go from there.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Registered User
    Join Date
    Jan 2011
    Posts
    10
    Thanks Salem for the input. The reason why I'm having to pass unmanaged objects to Managed C++ and back, is because functions like the DAQmxCreateDOChan and DAQmxCreateDIChan won't take in a .NET String class. Hence I tried marshalling the managed string to unmanaged character array. But that came with its own problems.

    I have tried my best to avoid sprintf(), and am using 'String::Format' in its place. I'm looking at other options, but nothing seems to be of much help.

  5. #5
    the hat of redundancy hat nvoigt's Avatar
    Join Date
    Aug 2001
    Location
    Hannover, Germany
    Posts
    3,130
    Code:
    Pxi6509::DeviceDescriptorChar = (char*)Marshal::StringToHGlobalAnsi(DeviceDescriptor).ToPointer();
    	Marshal::FreeHGlobal(IntPtr(DeviceDescriptorChar));
    This doesn't make sense to me. Why marshall it just to free it a line later? Do you need DeviceDescriptorChar, or not?

    Edit: same goes for this line:

    Code:
    	char* PhyPortChar = (char*)Marshal::StringToHGlobalAnsi(PhyPort).ToPointer();
    	Marshal::FreeHGlobal(IntPtr(PhyPortChar));
    Edit2:

    A hint for organizing managed and unmanaged code in a way that the next coder will not want to kill you: write an unmanaged class that does what you want. Then write a managed class that holds an object of the first class and only handles all the translations from managed to unmanaged and back. This way you have seperated your logic from all the marshalling stuff.
    hth
    -nv

    She was so Blonde, she spent 20 minutes looking at the orange juice can because it said "Concentrate."

    When in doubt, read the FAQ.
    Then ask a smart question.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. MSVC 2003 break point problem
    By Bajanine in forum Windows Programming
    Replies: 6
    Last Post: 02-18-2008, 02:39 AM
  2. Problem with seperating strings with delimiters..
    By hykyit in forum C Programming
    Replies: 2
    Last Post: 03-29-2006, 06:51 PM
  3. Multiple include problem
    By VirtualAce in forum Game Programming
    Replies: 13
    Last Post: 02-04-2006, 06:09 PM
  4. release vs debug in msvs .net 2003
    By axr0284 in forum Windows Programming
    Replies: 1
    Last Post: 01-10-2006, 01:52 AM
  5. problem with strings (explained)
    By dune911 in forum C Programming
    Replies: 9
    Last Post: 02-11-2002, 06:02 PM