none
use of SafeArray pointer in vc++ dll to access structure Array passing from vb.net RRS feed

  • Question

  • hello guys ..

    i am trying to implement some coding to pass a structure array from vb.net to a vc++ dll. To access the array i am using the SafeArray pointer in vc++. I don't have a depth knowledge about the SafeArray structure, i tried to implement the program using SafeArray but i am not getting the value from the vc++ dll while i am trying to print the value from the dll that is passing from the vb.net program.

    i hv just tried to implement the program and i am not sure also that i am going towards the right way implementation or not.  please help me to execute the program and get the proper value.

    And please guide me if there are some other option instead of using SafeArray pointer to get the value from the vb.net structure array to a vc++ dll.

    the following code i am using in vb.net 2008

    '-----------------module1.vb------------------------
    
    Public Module Module1
    
        Structure stickypara
            Public PID As Integer      'Parameter Id
            Public groupid As Integer      'Group Id
            Public wordNo As Integer      'Word Number
            Public subframeCode As Integer      'Subframe Code
        End Structure
    
        Structure ConfigTable
            Public datatype As Integer      'Datatype i.e
            Public dispType As Integer      'Display Type
            Public startBit As Integer      'Start Bit
            Public stopBit As Integer      'Stop bit
            
        End Structure
    
        Public para As ConfigTable() = New ConfigTable(50) {}  'Array of ConfigTable structure
        public spara As stickypara() = New stickypara(50) {}
        Dim indx As Integer = 0
        Dim stickyindex As Integer = 0
    
    
    
        Public Declare Sub DatabaseInitialization Lib "foqadll.dll" (ByVal para() As ConfigTable, ByVal spara() As stickypara)
    
    
    End Module
    '-----------------------from1.vb--------------------
    
    Public Class Form1
    
    
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    
            Try
    
                While (indx <= 50)
                    
                    para(indx).datatype = 0
    		para(indx).dispType = 5
                    para(indx).startBit = 3
                    para(indx).stopBit = stpbt - 1
                    indx = indx + 1
                End While
    
                While (stickyindex <= 50)
                    spara(stickyindex).PID = stickyindex
                    spara(stickyindex).groupid = 9
                    spara(stickyindex).wordNo = 1
                    spara(stickyindex).subframeCode = 115
                    stickyindex = stickyindex + 1
                End While
            Catch ex As Exception
                MsgBox("Error!" & ex.Message)
            End Try
    
          
    
    
            DatabaseInitialization(para, spara)
    
        End Sub
    End Class

    and for the dll i am using the vc++ 2008 , and the code as following

    //---------------header file (struct.h)-----------------
    
    #include <ole2.h>
    #include <OAIdl.h>
    #include <stdio.h>
    
    typedef struct 
    {
    	
    	 int		datatype      ; 
    	 int    	disptype      ;
    	 int		startbit      ;
    	 int		stopbit       ;
    	
    
    }PARAMETERS; 
    
    typedef struct
    {
    	 int		pid           ; 
    	 int     	groupid       ;  
    	 int		wordno        ; 
    	 int		subframecode  ; 
    
    }STICKYPARAMETERS;
    
    
    PARAMETERS				*PARATable      ;
    STICKYPARAMETERS			*STICKYTable    ;
    //-------------------------foqadll.cpp--------------------
    #include "struct.h"
    
    void _stdcall DatabaseInitialization(LPSAFEARRAY *ptrParamData, LPSAFEARRAY *ptrStickyData)
    {
    
    
    	PARAMETERS		*ParaFiles	;
    
    	STICKYPARAMETERS 	*StickyFiles	;
    
    
    	char str4[255];
    
    	sprintf(str4,"(*ptrParamData)->rgsabound->cElements is %d\n\n(*StickyFiles)->rgsabound->cElements is %d ",(*ptrParamData)->rgsabound->cElements,(*StickyFiles)->rgsabound->cElements);
    	MessageBoxA(NULL, str4, " NULL ", MB_OK );
    
    	ParaFiles=(PARAMETERS *)((*ptrParamData)->pvData);
    	StickyFiles=(STICKYPARAMETERS*)((*StickyFiles)->pvData);
    	
    		for(int i=0;i<(*ptrParamData)->rgsabound->cElements;i++)
    			{
    				sprintf(str4,"%d\n%d\n%d\n%d\n",
    					ParaFiles[i].datatype,
    					ParaFiles[i].disptype,
    					ParaFiles[i].startbit,
    					ParaFiles[i].stopbit);
    				MessageBoxA(NULL, str4, " NULL ", MB_OK );
    			}
    		for(int j=0;j<(*StickyFiles)->rgsabound->cElements;j++)
    			{
    				sprintf(str4,"%d\n%d\n%d\n%d\n",
    					StickyFiles[j].pid,
    					StickyFiles[j].groupid,
    					StickyFiles[j].wordno,
    					StickyFiles[j].subframecode);
    				MessageBoxA(NULL, str4, " NULL ", MB_OK );
    			}
    
    
    	MessageBoxA(NULL, "dll function  executed", " NULL ", MB_OK );
    }
    //--------------foqadll.def---------------------
    
    LIBRARY	"foqadll"
    
    EXPORTS
    
    DatabaseInitialization	

    Friday, April 25, 2014 5:51 AM

Answers

  • Hello sumlal,

    1. I am not v familiar with VB.NET but I did some experiments after reading your post.

    2. The following is a summary of points (some I know to be true and some are discovered as I experimented) :

    2.1 The "stickypara" and "ConfigTable" structures must be declared with the ComVisibleAttribute :

        <ComVisible(True)> _
        Structure stickypara
            Public PID As Integer      'Parameter Id
            Public groupid As Integer      'Group Id
            Public wordNo As Integer      'Word Number
            Public subframeCode As Integer      'Subframe Code
        End Structure
    
        <ComVisible(True)> _
        Structure ConfigTable
            Public datatype As Integer      'Datatype i.e
            Public dispType As Integer      'Display Type
            Public startBit As Integer      'Start Bit
            Public stopBit As Integer      'Stop bit
        End Structure

    This is a must in order that they be insertable into a SAFEARRAY. What happens is that GUIDs will be assigned to each structure and this is a necessity.

    2.2 You need to indicate to the interop marshaler to marshal the arrays as SAFEARRAYs. I tried to do this :

    Public Declare Sub DatabaseInitialization Lib "foqadll.dll" (<MarshalAs(UnmanagedType.SafeArray)> ByRef para() As ConfigTable, <MarshalAs(UnmanagedType.SafeArray)> ByRef spara() As stickypara)

    but it did not work. Hence I used the DllImportAttribute instead :

    <DllImport("foqadll.dll", EntryPoint:="DatabaseInitialization", _
        CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Sub DatabaseInitialization( _
        <MarshalAs(UnmanagedType.SafeArray)> ByVal para() As ConfigTable, _
        <MarshalAs(UnmanagedType.SafeArray)> ByVal spara() As stickypara)
    End Sub

    which worked for me.

    2.3 Do not declare the parameters to DatabaseInitialization() as LPSAFEARRAY*, declare them as LPSAFEARRAY instead. Also, it is not wise to directly access the SAFEARRAY's "pvData" field, use SafeArrayAccessData() to access the array data. After that, use SafeArrayUnaccessData() to unaccess the data. The following is a sample re-write of DatabaseInitialization() :

    void _stdcall DatabaseInitialization2(LPSAFEARRAY ptrParamData, LPSAFEARRAY ptrStickyData)
    {
    	PARAMETERS			*ParaFiles;
    	STICKYPARAMETERS 	*StickyFiles;
    
    	char str4[255];
    
    	sprintf(str4,"(*ptrParamData)->rgsabound->cElements is %d\n\n(*StickyFiles)->rgsabound->cElements is %d ",(ptrParamData->rgsabound)->cElements,(ptrStickyData->rgsabound)->cElements);
    	MessageBoxA(NULL, str4, " NULL ", MB_OK );
    	
    	// Added by Bio. Start.
    	SafeArrayAccessData(ptrParamData, (void**)&ParaFiles);
    	SafeArrayAccessData(ptrStickyData, (void**)&StickyFiles);
    	// Added by Bio. End.
    
    	// Removed by Bio. Start.
    	//ParaFiles=(PARAMETERS *)(ptrParamData->pvData);
    	//StickyFiles=(STICKYPARAMETERS*)(ptrStickyData->pvData);
    	// Removed by Bio. End.
    	
    		for(int i=0;i<(ptrParamData->rgsabound->cElements);i++)
    			{
    				sprintf(str4,"%d\n%d\n%d\n%d\n",
    					ParaFiles[i].datatype,
    					ParaFiles[i].disptype,
    					ParaFiles[i].startbit,
    					ParaFiles[i].stopbit);
    				MessageBoxA(NULL, str4, " NULL ", MB_OK );
    			}
    		for(int j=0;j<ptrStickyData->rgsabound->cElements;j++)
    			{
    				sprintf(str4,"%d\n%d\n%d\n%d\n",
    					StickyFiles[j].pid,
    					StickyFiles[j].groupid,
    					StickyFiles[j].wordno,
    					StickyFiles[j].subframecode);
    				MessageBoxA(NULL, str4, " NULL ", MB_OK );
    			}
    
    	MessageBoxA(NULL, "dll function  executed", " NULL ", MB_OK );
    	
    	// Added by Bio. Start.
    	SafeArrayUnaccessData(ptrParamData);
    	ParaFiles = NULL;
    	SafeArrayUnaccessData(ptrStickyData);
    	StickyFiles = NULL;	
    	// Added by Bio. End.
    }
    

    Hope the above helps.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Friday, April 25, 2014 11:49 AM

All replies

  • Try using ByRef:

        Public Declare Sub DatabaseInitialization Lib "foqadll.dll" (ByRef para() As ConfigTable, ByRef spara() As stickypara)

    Friday, April 25, 2014 7:41 AM
  • Hello sumlal,

    1. I am not v familiar with VB.NET but I did some experiments after reading your post.

    2. The following is a summary of points (some I know to be true and some are discovered as I experimented) :

    2.1 The "stickypara" and "ConfigTable" structures must be declared with the ComVisibleAttribute :

        <ComVisible(True)> _
        Structure stickypara
            Public PID As Integer      'Parameter Id
            Public groupid As Integer      'Group Id
            Public wordNo As Integer      'Word Number
            Public subframeCode As Integer      'Subframe Code
        End Structure
    
        <ComVisible(True)> _
        Structure ConfigTable
            Public datatype As Integer      'Datatype i.e
            Public dispType As Integer      'Display Type
            Public startBit As Integer      'Start Bit
            Public stopBit As Integer      'Stop bit
        End Structure

    This is a must in order that they be insertable into a SAFEARRAY. What happens is that GUIDs will be assigned to each structure and this is a necessity.

    2.2 You need to indicate to the interop marshaler to marshal the arrays as SAFEARRAYs. I tried to do this :

    Public Declare Sub DatabaseInitialization Lib "foqadll.dll" (<MarshalAs(UnmanagedType.SafeArray)> ByRef para() As ConfigTable, <MarshalAs(UnmanagedType.SafeArray)> ByRef spara() As stickypara)

    but it did not work. Hence I used the DllImportAttribute instead :

    <DllImport("foqadll.dll", EntryPoint:="DatabaseInitialization", _
        CallingConvention:=CallingConvention.StdCall)> _
    Public Shared Sub DatabaseInitialization( _
        <MarshalAs(UnmanagedType.SafeArray)> ByVal para() As ConfigTable, _
        <MarshalAs(UnmanagedType.SafeArray)> ByVal spara() As stickypara)
    End Sub

    which worked for me.

    2.3 Do not declare the parameters to DatabaseInitialization() as LPSAFEARRAY*, declare them as LPSAFEARRAY instead. Also, it is not wise to directly access the SAFEARRAY's "pvData" field, use SafeArrayAccessData() to access the array data. After that, use SafeArrayUnaccessData() to unaccess the data. The following is a sample re-write of DatabaseInitialization() :

    void _stdcall DatabaseInitialization2(LPSAFEARRAY ptrParamData, LPSAFEARRAY ptrStickyData)
    {
    	PARAMETERS			*ParaFiles;
    	STICKYPARAMETERS 	*StickyFiles;
    
    	char str4[255];
    
    	sprintf(str4,"(*ptrParamData)->rgsabound->cElements is %d\n\n(*StickyFiles)->rgsabound->cElements is %d ",(ptrParamData->rgsabound)->cElements,(ptrStickyData->rgsabound)->cElements);
    	MessageBoxA(NULL, str4, " NULL ", MB_OK );
    	
    	// Added by Bio. Start.
    	SafeArrayAccessData(ptrParamData, (void**)&ParaFiles);
    	SafeArrayAccessData(ptrStickyData, (void**)&StickyFiles);
    	// Added by Bio. End.
    
    	// Removed by Bio. Start.
    	//ParaFiles=(PARAMETERS *)(ptrParamData->pvData);
    	//StickyFiles=(STICKYPARAMETERS*)(ptrStickyData->pvData);
    	// Removed by Bio. End.
    	
    		for(int i=0;i<(ptrParamData->rgsabound->cElements);i++)
    			{
    				sprintf(str4,"%d\n%d\n%d\n%d\n",
    					ParaFiles[i].datatype,
    					ParaFiles[i].disptype,
    					ParaFiles[i].startbit,
    					ParaFiles[i].stopbit);
    				MessageBoxA(NULL, str4, " NULL ", MB_OK );
    			}
    		for(int j=0;j<ptrStickyData->rgsabound->cElements;j++)
    			{
    				sprintf(str4,"%d\n%d\n%d\n%d\n",
    					StickyFiles[j].pid,
    					StickyFiles[j].groupid,
    					StickyFiles[j].wordno,
    					StickyFiles[j].subframecode);
    				MessageBoxA(NULL, str4, " NULL ", MB_OK );
    			}
    
    	MessageBoxA(NULL, "dll function  executed", " NULL ", MB_OK );
    	
    	// Added by Bio. Start.
    	SafeArrayUnaccessData(ptrParamData);
    	ParaFiles = NULL;
    	SafeArrayUnaccessData(ptrStickyData);
    	StickyFiles = NULL;	
    	// Added by Bio. End.
    }
    

    Hope the above helps.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Friday, April 25, 2014 11:49 AM
  • Hello Bio,

    Thanks for your reply. Although i have figure that out in some other way and its working perfectly now but i think it should works too. Thanks for your valuable time.

    • Edited by Suman29 Friday, December 26, 2014 6:52 AM
    Friday, December 26, 2014 6:51 AM