Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion GroupsVB SyntaxEnterprise DevelopmentDatabase AccessControlsCOMWin APICrystal ReportDeploymentGeneralGeneral 2
Related Topics
VB.NET / ASP.NETMS SQL ServerMS AccessOther Database ProductsMore Topics ...

VB Forum / Win API / March 2007



Tip: Looking for answers? Try searching our database.

Passing an array of Strings from VB to C

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Bernard Delmée - 26 Mar 2007 22:53 GMT
I have to be able to pass an array of strings to a C DLL
from a VB program. I don't need to change the strings.
I'd like to use the simplest mechanism possible, yet
this should work from VB6 and VB.Net. I have to admit I
am getting confused as the articles I've been able to
find seem to either address .net only (P/Invoke) or
possibly VB3 (through some vbapi.h header which I suspect
may have worked on win16 only).
I know I can easily pass arrays of scalar (int, double)
values by passsing the first element by ref, but it seems
this is not applicable to strings. Perhaps some creative use
of SafeArrays and Variants could help, but I am at a loss
right now.
Ken Halter - 26 Mar 2007 23:16 GMT
>I have to be able to pass an array of strings to a C DLL
> from a VB program. I don't need to change the strings.
[quoted text clipped - 9 lines]
> of SafeArrays and Variants could help, but I am at a loss
> right now.

This may help...

How to pass arrays and strings between Visual Basic and C functions or
between Visual Basic and C++ functions by using Visual Basic 6.0
http://support.microsoft.com/kb/205277/en-us

Signature

Ken Halter - MS-MVP-VB - Please keep all discussions in the groups..
In Loving Memory - http://www.vbsight.com/Remembrance.htm

Karl E. Peterson - 26 Mar 2007 23:18 GMT
> I have to be able to pass an array of strings to a C DLL
> from a VB program. I don't need to change the strings.
[quoted text clipped - 4 lines]
> possibly VB3 (through some vbapi.h header which I suspect
> may have worked on win16 only).

You might find this useful: http://vb.mvps.org/tips/vb5dll.asp
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Bernard Delmée - 27 Mar 2007 10:43 GMT
Thanks gents for the useful links. I've now got a procedure
within my DLL which accesses correctly the string array elements
when called from VB6. The same test project migrated to
VB2005 Express crashes, though. The proc does not seem
to receive the expected SAFEARRAY** and trying to access
the structure returns random data (e.g absurdly huge number
of elements). Any suggestion welcome ;-(

--------> VB decl <--------
Private Declare Sub tst_str2 Lib "test_sl.dll" (str() As String)

--------> C impl <--------
extern "C" void __stdcall tst_str2( SAFEARRAY **psaArray )
{
    char cbuf[80];

    // lock before using
    long l = SafeArrayLock(*psaArray);

    // how many elements
    long nelt = (*psaArray)->rgsabound[0].cElements;
    wsprintf(cbuf, "There are %ld elements in this array", nelt);
    MessageBox(0,cbuf,"# elements",0);

    // access 3rd string in array for test purpose
    // BSTR *pArrayElements = (BSTR *) (*psaArray)->pvData;
    char **pArrayElements = (char **) (*psaArray)->pvData;
    MessageBox(0, pArrayElements[2],"elt[2]",0);

    // releasing the array
    l = SafeArrayUnlock(*psaArray);
}
aao - 27 Mar 2007 13:22 GMT
SAFEARRAY **psaArray . double * indicates that caller expects you do create the array. You start you function with  SafeArrayLock trying to lock nonexisting memory,. Your function should strat with SafeArrayCreate

> Thanks gents for the useful links. I've now got a procedure
> within my DLL which accesses correctly the string array elements
[quoted text clipped - 28 lines]
>     l = SafeArrayUnlock(*psaArray);
> }
Bernard Delmée - 27 Mar 2007 14:32 GMT
> SAFEARRAY **psaArray . double * indicates that caller expects you do
> create the array. You start you function with  SafeArrayLock trying to
> lock nonexisting memory,. Your function should strat with SafeArrayCreate

That's not the intention. The array is/should be allocated from VB,
just read (not modified in any way) by the DLL. What I posted is
working from VB6 as I said, only not from VB.Net.
I am looking for an example of accessing the strings from C,
with the conversion from Unicode to Ansi performed (if possible
tranparently) and in a way that works when called both from VB6
and VB.Net.

Thanks.
aao - 27 Mar 2007 14:54 GMT
If that is the case you signature should have been " tst_str2( SAFEARRAY
*psaArray )".
You can make your current signature work with all versions of VB though:
start you function  with something like:

if(*psaArray) //I hope VB is descent enough to initialize the pointer
{
   *psaArray = SafeArrayCreate(...);
   ...

}

>> SAFEARRAY **psaArray . double * indicates that caller expects you do
>> create the array. You start you function with  SafeArrayLock trying to
[quoted text clipped - 9 lines]
>
> Thanks.
Bernard Delmée - 27 Mar 2007 15:27 GMT
> If that is the case you signature should have been " tst_str2( SAFEARRAY
> *psaArray )".

I don't know - I don't think I can control that signature as
it has to match whatever VB6 passes on the stack when calling.
If I try with one level of indirection less, it stops working.
VB6 insists that the array must be passed ByRef, hence the
double indirection makes sense, doesn't it? Just because I can
potentially change/reallocate the array doesn't mean I have
to. If I am only interested in reading the pre-existing
content as managed by VB6, I should be able to - and I am
as I said.

I cannot claim I fully understand what's going on as I was
expecting to have to deal with VARIANTS and BSTRs.
But what I posted is working with VB6, so either I am taking
shortcuts or the mechanism is not supported under .Net anymore...

I am trying to refactor some old code as a DLL so a VB client
can use it, and (all too obviously) had barely played with .Net
before. Just trying to find the path of least resistance, here ;-)

From the VB6 side, the signature and method invocation are:

*) Declare Sub tst_str2 Lib "test_sl.dll" (ByRef str() As String)

*) Dim a(5) As String
   ' ... populate a() here ...
   Call tst_str2(a)

Does that look right (the explicit "call" looks a lil weird)
aao - 27 Mar 2007 16:36 GMT
Well you are kind of in gray area of VB. C functions are supported , but not
very well defined. The trick of ** in COM is that it might be empty on the
way in. In addition to that there is whole issue of marshaling data.
Required in COM, not well defined in simple c-DLL(I suspect that it what has
changed in new VB).  You are trying to use COM types in non-COM interface.
You can try to make it work, but if you are refactoring old code you might
be better off with creating COM object, or .NET wrapper class.

P.S. At least try the following

   if(!*psaArray)

{    //write error log or something}

>> If that is the case you signature should have been " tst_str2( SAFEARRAY
>> *psaArray )".
[quoted text clipped - 27 lines]
>
> Does that look right (the explicit "call" looks a lil weird)
Bernard Delmée - 27 Mar 2007 18:28 GMT
> You can try to make it work, but if you are refactoring old code you might
> be better off with creating COM object, or .NET wrapper class.

I am trying to revive old code that lives under a pre-.Net
Borland IDE. Ideally I'd want to keep the VCL GUI (for reference)
and let colleagues build a new one in VB.Net while calling
the same well-tested "computational engine" exposed as a DLL.

By .Net wrapper classes, are you suggesting porting the code
to a recent VSC++, where some extended syntax might allow me
to easily expose some procedures or classes in a form the CLR
could use?

> P.S. At least try the following
>
>     if(!*psaArray)
>
> {    //write error log or something}

Good suggestion, i have implemented it and whatever VB.Net
passes is not NULL (nor point to NULL), yet apparently not a
SAFEARRAY**. Oh Well.

Thanks,

Bernard.
aao - 27 Mar 2007 19:55 GMT
yes .Net wrappers are relatevly easy if you figure out clr setting with code
like this

public __gc class WrapperClassForDotNet : public IDisposable

{

public:

     WrapperClassForDotNet(){m_old = new MyOldCPPClassIWantToReuse();}

     void Dispose()

     {

           GC::SuppressFinalize(this);

           Dispose(true);

     }

     __property int get_SampleValAsProperty()

     {

           if(!m_old)

                 return 0;

           return static_cast<int>(m_old->SampleVal());

     }

protected:

     virtual void Dispose(bool disposing)

     {

           if(m_old)

           {

                 delete m_old;

                 m_old = 0;

           }

     }

private:

     MyOldCPPClassIWantToReuse* m_old;

};
Karl E. Peterson - 27 Mar 2007 17:35 GMT
>> SAFEARRAY **psaArray . double * indicates that caller expects you do
>> create the array. You start you function with  SafeArrayLock trying to
[quoted text clipped - 3 lines]
> just read (not modified in any way) by the DLL. What I posted is
> working from VB6 as I said, only not from VB.Net.

I can't say for sure, but I suspect the issue you're running into is that VFred
doesn't use BSTR's or Variants.  To it, all variables are objects of some sort.
Hard to imagine being able to pass an array of anything from it to a DLL, though I
suppose it's possible.  At any rate, I'm almost certain such a solution will need to
take each environment into consideration differently.
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Bernard Delmée - 27 Mar 2007 18:09 GMT
> Hard to imagine being able to pass an array of anything from it to a DLL, though I
> suppose it's possible.  At any rate, I'm almost certain such a solution will need to
> take each environment into consideration differently.

AFAICT it still works fine for arrays of scalars (Doubles, Longs),
alas not for Strings. I must be looking in the wrong places as it
seems like such a basic and useful mechanism to preserve (or emulate).
Perhaps I'll just concatenate the strings, tab-separated, and rebuild
the list in the DLL. Ugly but it'll have to do.

Cheers,

Bernard.
Karl E. Peterson - 27 Mar 2007 18:20 GMT
>> Hard to imagine being able to pass an array of anything from it to a DLL, though
>> I suppose it's possible.  At any rate, I'm almost certain such a solution will
>> need to take each environment into consideration differently.
>
> AFAICT it still works fine for arrays of scalars (Doubles, Longs),
> alas not for Strings.

That actually makes sense.  Even though they want to present those numerics as
objects, the entire shamework would be far less efficient than it already is were
they to do anything other than keep the actual array data contiguous.

> I must be looking in the wrong places as it
> seems like such a basic and useful mechanism to preserve (or emulate).

Yeah, lots of ClassicVB users have come to same conclusion -- look elsewhere.

> Perhaps I'll just concatenate the strings, tab-separated, and rebuild
> the list in the DLL. Ugly but it'll have to do.

Good luck...
Signature

.NET: It's About Trust!
http://vfred.mvps.org

 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.