Passing an array of Strings from VB to C
|
|
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
|
|
|