robatdural

Hi

I am upgrading some VB 6 programs to VB 2005. These programs pass data to a C++ DLL and I am having trouble passing an array of strings.

The DLL looks basically like:

int _stdcall PassInStrings(short idx, LPSAFEARRAY FAR *strArrayPtr)
{
BSTR *tmpStrArray;
tmpStrArray = (BSTR*)(*tagArrayPtr)->pvData;

for (i=0; i<idx; i++)
{
MyDebugPrint("PassInStrings", LPSTR(tmpStrArray[i+1]));
}
//other stuff
}

Calling this function from VB6 works nicely

Public Declare Function PassInStrings Lib "MyDLL.dll" (ByVal idx As Integer, ByRef lpStrArray() As String) as Integer

public sub sendStrings()

Dim iret as integer
Dim iCtr as Integer
dim myStr(4) as string

iCtr = 4
myStr(1)="Test String 1"
myStr(2)="Test String 2"
myStr(3)="Test String 3"
myStr(4)="Test String 4"

iret = PassInStrings(iCtr, myStr)

End Sub

Calling the function from VB2005 does not work - An AccessViolationException is trapped.

Public Declare Function PassInStrings Lib "MyDLL.dll" (ByVal idx As short, ByRef lpStrArray() As String) as Integer

public sub sendStrings()

Dim iret as integer
Dim iCtr as Short
dim myStr(4) as string

iCtr = 4
myStr(1)="Test String 1"
myStr(2)="Test String 2"
myStr(3)="Test String 3"
myStr(4)="Test String 4"

iret = PassInStrings(iCtr, myStr)

End Sub

As I prefer not to modify the DLL (it does a lot more than list a few strings) can anyone tell me what I must change in the VB 2005 code.

Thanks in advance

Rob



Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

MSFT Abel Valadez

Hi Rob,

You probably need to add some attributes to the array parameter to marshal it correctly. However, before you do that do verify that the first prameter is indeed 2 bytes long on the C++ side:

Public Declare Function PassInStrings Lib "MyDLL.dll" (ByVal idx As short, <MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_BSTR)> ByRef lpStrArray() As String) as Integer

If your C++ dll doesn't nodify the SafeArray, you could also change the parameter to be ByVal instead of ByRef and optionally add the <In()> attribute.

You will need to import the System.Runtime.InteropServices namespace for the code to compile.

Hope this helps,






Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

robatdural

Hi Abel

Thanks for the response.

I tried Marshalling the array parameter as you have suggested (the first param is a short - to match VB6 integer) it overcomes the AccessViolationException but the debug print only shows the first character of the string ( ).

eg Passing in 4 strings ("Test String 1", etc) I get:

T
T
T
T

Adding the <In()> attribute did not change this.

Changing to ByVal does not work - AccessViolationException again!

Any other suggestions

Thanks again

Rob





Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

MSFT Abel Valadez

Hi Rob,

I'm glad that helped. Givent he results that you are seeing, it seems that the only issue left to resolve is making sure the width of the strings that you are sending to the DLL match.

Can you try the following:

Public Declare Ansi Function PassInStrings Lib "MyDLL.dll" Alias "PassInStrings" (ByVal idx As short, <MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_BSTR)> ByRef lpStrArray() As String) as Integer

or changing VT_BSTR to VT_LPSTR.

Do note that there are different semantics involved for allocating and releasing the strings in memory so you really need to make sure that you are sending exactly what the C++ dll is expecting.

Hope that helps,






Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

Sudhansu Tiwari

Hi Rob , Abel & All .Net Masters ,

I will be very happy if anyone of you can solve my following issue which is very much similar to one raised by Rob.

In my case I am getting the following output :


2
l
d
n

In these 4 strings the last character is correct but the character count is NOT. Any suggestions

Rob ,

Could you tell me how did you solve your issue , mine is exactly same .

Thanks in advance ,

Sudhansu





Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

MSFT Abel Valadez

Hi Sudhansu,

You will need to decorate your function signature with the <marshalas> attribute as I specified above. You will then need to try out different VarEnum values until you actually math the width/type of string that you are using in the C++ function.

The ones you need to try out are:

VarEnum.BSTR

VarEnum.LPSTR

VarEnum.LPWSTR

The one you actually need to use should be deterministic, but without an actual C++ signature to match it to, I can't tell you which one to use.

Hope this helps, - Abel.






Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

Sudhansu Tiwari

Hi Abel ,

Million thanks for your response dear friend !!

Actually when it is VarEnum.VT_LPSTR I get some output , with rest of the options I get the following exception :

Specified array was not of the expected type.


My declaration looks like follows after including your sugggestions :

Declare Function vb_check_applications Lib "tcvbodss.dll" (ByVal ii_login_handle As Short, ByRef apl_count As Short, <MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_BSTR)> ByRef apl_ids() As String, ByRef apl_list_handle As Short) As Short

In my VC++ dll the parameter type for apl_ids() is declared as LPSAFEARRAY FAR * .

Following is how I am calling the above dll function and trying to print the output :

Public Sub getEntitledApplicationsList()

Dim i As Integer = 0
Dim handle_apl_list_handle As Short
Dim no_of_entl_apl As Short
Dim entl_apl_list() As String
ReDim entl_apl_list(6)

retcode = vb_check_applications(login_handle, no_of_entl_apl, entl_apl_list, handle_apl_list_handle)

Console.WriteLine("Number of attached applications : " & no_of_entl_apl & " ")

For i = 0 To no_of_entl_apl - 1
Console.Write("Application : " & i+1 & " ")
Console.WriteLine(entl_apl_list(i))
Next i

Console.WriteLine(retcode)

End Sub

Following is the output :

Number of attached applications : 5
Application : 1
Application : 2 2
Application : 3 l
Application : 4 d
Application : 5 n

STRANGE
part is last characters except for Application 1 is getting printed correctly BUT character count is not correct.

Kindly let me know if you need further details regarding this , THANKS A MILLION IN ADVANCE

Sicerely ,

Sudhansu






Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

MSFT Abel Valadez

Hi Sudhansu,

I would need to look at your C++ code to figure out how exactly is it that you are filling up the array.

Thanks, Abel.






Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

Sudhansu Tiwari

Hi Abel ,

Thanks again for the reply and your interest in this issue .

Actually the dll is written in VC++ 6.0 and when I write the
same set of application in VB 6.0 everything works fine ,
so I don't think the problem is in c++ side but can't say
anything confidently as I am very new to this field , so need
your kind suggestions Smile

Below is my C++ code :

vb_check_applications(int p_login_handle,int *p_apl_count,LPSAFEARRAY FAR * p_apl_ids,int *p_apl_list_handle)
{
int l_retcode;
int l_apl_count;
int l_apl_list_handle;
int l_count;

char ** l_apl_ids;

l_apl_ids = (char **)malloc(sizeof(char *) * 65);

if (l_apl_ids == NULL)
{
trace_error("Memory Allocation failed", __LINE__,s_file);
return E_MALLOC_FAIL;
}

l_retcode = c_check_applications(p_login_handle, &l_apl_count,l_apl_ids,&l_apl_list_handle);

*p_apl_count = l_apl_count;
*p_apl_list_handle = l_apl_list_handle;
if (l_retcode != SUCCESS)
{
free(l_apl_ids);
return l_retcode;
}

if (*p_apl_count > 0)
{
for (l_count = 0; l_count < *p_apl_count; l_count ++)
{
vb_set_array_elem_val(p_apl_ids,*(l_apl_ids+l_count),l_count,-1); /* -1 is dummy value */
}
}

cmn_free (*(l_apl_ids));
cmn_free(l_apl_ids);

return l_retcode;
}


void vb_set_array_elem_val(LPSAFEARRAY FAR *p_invar,char *l_str, int l_count, int int_val)
{
BSTR l_elembstr = NULL;
short l_elemint;
long rgIndex2[] = {0};

size_t size;

/* For String Array */

if ( l_str != (char *)NULL )
{
rgIndex2[0] = l_count;
size = strlen(l_str);
VBSetHlstr(&l_elembstr,l_str,size);


SafeArrayPutElement(*p_invar, rgIndex2, l_elembstr);
}
else /* For Integer Array */
{
l_elemint = int_val;
rgIndex2[0] = l_count;
SafeArrayPutElement(*p_invar, rgIndex2, &l_elemint);
}

return;
}

I guess this piece of code helps you in your analysis , let me know if further info is reqd , I have made it short as actual code is too vast .

Thanks a lot again Smile

Sincerely ,

Sudhansu






Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

MSFT Abel Valadez

Hi Sudhansu,

There's something that I don't understand so I need more information to clarify it. Are you creating the SafeArray on the C++ side and returning it to the VB side, or are you just filling in information into an already existing array

Also, could you post the code for the VBSetH1str fcuntion, and let me know how you specify the count of elements

Thanks, Abel.






Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

Sudhansu Tiwari

Hi Abel ,

1 ) Are you creating the SafeArray on the C++ side and returning it to the VB side, or are you just filling in information into an already existing array

Ans : No , I am just filling information into an already existing array . I pass the reference of Array declared in my VB application.

2) could you post the code for the VBSetH1str fcuntion and let me know how you specify the count of elements.

Ans : #define VBSetHlstr(a,b,c) SysFreeString(*a); *a = SysAllocStringByteLen(b,c)

Count is coming correctly without any issues.

Hope this information helps your analysis of this code .. please do let me know if something is missing.


Thanks a million again friend Smile




Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

MSFT Abel Valadez

Hi Sudhansu,

Unfortunately you won't be able to get correctly read the strings from VB.Net without making changes to your C++ code. The problem here is that you are assigning ANSI strings into a BSTR, which is UNICODE. The simplest approach would be to just convert the string into a Unicode string before setting it in the BSTR, but I would strongly encourage you to consider changing the way in which you pass the arrays.

To convert the string to Unicode change the following code:

/* For String Array */

if ( l_str != (char *)NULL )
{
rgIndex2[0] = l_count;
CComBSTR bstrTemp = lstr;

SafeArrayPutElement(*p_invar, rgIndex2, bstrTemp.Detach());
}

You will probably need to include "atlbase.h".

If you do consider changing the interaction, please let me know and I can give you some tips/post some code to get you started.

- Abel.






Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

Sudhansu Tiwari

Hi Abel,

First of all I am sorry for late reply as it was weekend for us , hope you too had a great weekend Smile

Hats off to you my dear Genius friend Smile

I appreciate your precision work . . . .

I can't express my gratitude in words but I am really thankful and highly impressed with your great intelligence & knowledge .

But I would like to know the other methods which you suggested in last post, got two Qs for you again Wink

Take your own time to reply . ..

1) Would you like to suggest a C equivalent of the suggestion you gave last time

2) You also suggested me to change the way I am passing arrays, may I have some details on same

Thanks again dear friend Smile

Sincerely,

Sudhansu






Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

MSFT Abel Valadez

Here's the answer to your first question:

Take a look at: http://support.microsoft.com/kb/138813 You will find a short snippet of code that will accomplish the ANSI to UNICODE conversion. The only slight change that you need to make is to actually allocate the memory using SysAllocStringLen(NULL, sCharacters-1) instead of CoTaskMemAlloc and free the memory in case of error with SysFreeString instead of CoTaskMemFree.

I will answer #2 later.

Hope this helps, Abel.






Re: Visual Basic General Passing an Array of Strings to a C++ DLL using SAFEARRAY - HELP!!!

Sudhansu Tiwari

Hi Abel ,

I tried all possible methods to get this struct array thing right , tried Pinvoke Sample too but nothing worked for me.
I will be grateful to get a suggestion from you in this regard , Thanks a lot in advance !

Sincerely ,

Sudhansu