• C

Conversion from LPCTSTR to BSTR

I have written a Dll in C, which calls a function in an ActiveX Dll(written in VB). The C interface to this ActiveX function requires some parameters to be passed in as BSTR *.

The function in the C Dll which calls this ActiveX function gets the data as LPCTSTR parameters. Here's part of the code to explain what I mean.

mycdll.c

_clsTestVbDll      *pClass = NULL;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
        return True;
}

__declspec(dllexport) long
DataCheck( LPCTSTR docAction, LPCTSTR elmAction)
{
// initialization

//call to activex function in vb
pClass->lpVtbl->MyVBFunctionStr(pClass, docAction, &sOutput);
}


mycdll.h

// amongst other things
virtual /* [id] */ HRESULT STDMETHODCALLTYPE MyVBFunctionStr(
            /* [out][in] */ BSTR __RPC_FAR *sStr,
            /* [retval][out] */ short __RPC_FAR *__MIDL_0015) = 0;


When the application calls the DataCheck () in the C Dll, it crashes as it can't convert the data from LPCTSTR to BSTR *. Any ideas on how I can get this working?

Thanx.

kkokalAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

captainkirkCommented:
try:
BSTR bString = CString::AllocSysString(docAction);

pClass->lpVtbl->MyVBFunctionStr(pClass, bString, &sOutput);

and let me know if that works...

0
kkokalAuthor Commented:
Since I am not using C++ but C I tried this:

BSTR bString = SysAllocString(docAction);

ppClass->lpVtbl->MyVBFunctionStr(pClass, &bString, &sOutput);

Now it does NOT crash but it gives me gibberish. The value of docAction coming into the C Dll function is 'ML001' and what I get in VB is ??|.

Any ideas?

Thanks.

cheers



0
abancroftCommented:
Since a BSTR is a pointer, I suspect that your VB function doesn't need a "BSTR *" parameter - it probably only needs "BSTR".
0
Has Powershell sent you back into the Stone Age?

If managing Active Directory using Windows Powershell® is making you feel like you stepped back in time, you are not alone.  For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why.

jkrCommented:
Use a conversion function like the following:

char*    pszANSI = (LPCTSTR) strMyCString;
OLECHAR* awcBSTR[ MAX_SIZE]; // OLECHAR* == LPOLESTR == BSTR*, see wintypes.h

MultiByteToWideChar ( CP_ACP,
0,
pszANSI,
-1,
awcBSTR
lstrlen (pszANSI) + 1
);

Feel free to ask if you need more information!
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
kkokalAuthor Commented:
jkr,

Thx for the tip. However, I still get junk in the VB function. Any other ideas would be very helpful.

Cheers
0
abancroftCommented:
Try this:
1. Changing the VB function declaration to:
virtual /* [id] */ HRESULT STDMETHODCALLTYPE MyVBFunctionStr(
            /* [out][in] */ BSTR __RPC_FAR sStr,
            /* [retval][out] */ short __RPC_FAR *__MIDL_0015) = 0;
2. Use jkr's code to convert to a OLECHAR * (which is a BSTR).
3. Change your function call to:
pClass->lpVtbl->MyVBFunctionStr(pClass, awcBSTR, &sOutput);
0
abancroftCommented:
What is the VB function declaration?

What do you need to do with the string inside the VB function?
0
kkokalAuthor Commented:
The VB function in my test program looks like this

Public Function MyVBFunctionStr(ByVal sStr As String) As Integer
    MsgBox sStr, vbYesNo, "MyVBFunctionStr"
End Function

Right now I am just popping up a message box but in the real program I have to send it to a UNIX server to be validated. I have another function like this

Public Function MyVBFunctionNum(iNum As Integer) As Integer
    MsgBox iNum, vbOKCancel, "MyVBFunctionNum"
End Function

This works just great. I can see the integers in the msgbox but strings are getting to be a problem. Here's the code in the C Dll - I am still trying out your and jkr's suggestions. Right now as it stands, MyVBFunctionStr() doesn't popup the message box.

To know exactly what I am trying to do take a look at this knowledge base article. I am using Method 3. In the starting part of the article they define a function with an integer parameter.

http://support.microsoft.com/support/kb/articles/Q194/8/73.ASP

Thx for your help.

Cheers



0
abancroftCommented:
Does your VB function need to modify the string? If not, the COM method should be declared as:
virtual /* [id] */ HRESULT STDMETHODCALLTYPE MyVBFunctionStr(
            /* [in] */ BSTR __RPC_FAR sStr,
            /* [retval][out] */ short __RPC_FAR *__MIDL_0015) = 0;
i.e. the string is only an input parameter.

Then, your VB function should be declared as:
Public Function MyVBFunctionStr(ByRef sStr As String) As Integer
i.e. the string is passed as a reference.
0
kkokalAuthor Commented:
By default, VB treats parameters as 'ByRef'. However, just to be absolutely sure, I have changed the declaration of the VB function to be this

Public Function MyVBFunctionStr(ByRef sStr As String) As Integer

Since this is declared as byref, when i generate the .idl file and then the .h file from the .idl, I get this function declaration

HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MyVBFunctionStr )(
            _clsTestVbDll __RPC_FAR * This,
            /* [out][in] */ BSTR __RPC_FAR *sStr,
            /* [retval][out] */ short __RPC_FAR *__MIDL_0015);


However, I have changed that to be this

HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MyVBFunctionStr )(
            _clsTestVbDll __RPC_FAR * This,
            /* [out][in] */ BSTR __RPC_FAR sStr,
            /* [retval][out] */ short __RPC_FAR *__MIDL_0015);

// ie removed the * (pointer ref) before the sStr.


Also, here's the C code in the dll - I forgot to attach it last time.

long DataCheck(LPCTSTR destCode)
{
      short            sInput = 5;
      short            sOutput;

      const char*    pszANSI = destCode;
      OLECHAR* awcBSTR[12]; // OLECHAR* == LPOLESTR == BSTR*, see wintypes.h

      MultiByteToWideChar ( CP_ACP, 0, pszANSI, -1, (LPWSTR)awcBSTR, lstrlen (pszANSI) + 1 );
      
      //MessageBox(NULL, (LPCTSTR)pszANSI, "Error", MB_OK | MB_ICONINFORMATION );
      pClass->lpVtbl->MyVBFunctionNum(pClass, &sInput, &sOutput);
      pClass->lpVtbl->MyVBFunctionStr(pClass, (BSTR)awcBSTR, &sOutput);
      return 1;
}

I have added some casts to avoid any warnings.

Thx. And I am increasing the points to 200.
0
kkokalAuthor Commented:
I forgot to mention that the above C code still gives me junk specifically ?????????????? in the Msgbox call in MyVbFunctionStr()

Thx.
0
abancroftCommented:
Now I'm confused:

Your earlier posts indicate the VB declare as using "ByVal sStr As String", yet the COM method is declared as "/* [out][in] */ BSTR __RPC_FAR *sStr"

Yet what you've just posted contradicts this - "Since this is declared as byref....I get this function declaration.../* [out][in] */ BSTR __RPC_FAR *sStr"

This implies that if the parameter is declared "ByVal", you should get a "BSTR sStr". Yes?

Anyhow, I didn't realise the VB compiler was creating the IDL for you - stick with whatever it has generated.
0
kkokalAuthor Commented:
Sorry, my apologies.

You're right. When we started this discussion, the 'ByVal' keyword was not there in the VB declaration and hence, it generated

"/* [out][in] */ BSTR __RPC_FAR *sStr"

Then I read somewhere that I should try passing strings 'ByVal' and hence the idl then generated

"/* [out][in] */ BSTR __RPC_FAR sStr"

If I stick to what VB generates for me with a 'ByRef' argument, then I have the first declaration above. However, that doesn't seem to solve my problem. Right now I am willing to try anything.

So as the situation stands, the VB function takes arguments 'ByRef' explicitly.

Can you think of anything else that I can try?

Thx again.

Cheers

0
abancroftCommented:
If you are going to stick with ByRef, your C function call needs to be:
pClass->lpVtbl->MyVBFunctionStr(pClass, (BSTR *)(&awcBSTR), &sOutput);
0
jkrCommented:
Well, I've *no* experience in VB, but you might want to try 'SysAllocString()'... (not that it should make a difference)
0
kkokalAuthor Commented:
jkr,

thx. I've tried that - but it doesn't work.

0
abancroftCommented:
How are you calling SysAllocString()?

Here's what you need to do:

// Create a wide character version & convert from ANSI to WCHAR.
LPWSTR pWStr = new WCHAR[strlen(destCode)+1];
MultiByteToWideChar ( CP_ACP, 0, destCode, -1, pWStr, lstrlen (destCode) + 1 );
// Create the BSTR. Note: Although BSTR & OLECHAR* are typed the same, they cannot be treated the same.
BSTR bStr = SysAllocString(pWStr);
// Clean up the wide string
delete []pWStr;
// Call VB function
pClass->lpVtbl->MyVBFunctionStr(pClass, (BSTR *)(&bStr), &sOutput);
// Free the BSTR
SysFreeString(bStr);

Convoluted isn't it?

Blame the MS programmer who typedef'ed BSTR as OLECHAR*. A BSTR is not the same as an array of OLECHAR - it has extra bytes BEFORE the character pointer.

0
kkokalAuthor Commented:
THAT WORKED !!!! YES !!!!

Thank you. I had tried to use SysAllocString in the simplest of ways. I had no idea this is so convoluted. But thanks to you and jkr for solving this for me.

Now how do I assign the points? Ideally, I'd like to assign points to both you and jkr.

Thx again.

Cheers

0
abancroftCommented:
Easiest way is to:
1. Reduce the points for this answer to 100.
2. Award one of us the answer (you can select a comment as an answer).
3. Create another dummy question for 100 pts. Tell us about it & the other person will post an answer which you accept.
0
kkokalAuthor Commented:
Distribution of points - 100 to jkr and 100 to aboncroft.

Thx!!

Cheers

0
jkrCommented:
kkokal, the distribution of points obviously didn't work (I received all of them). You might want to post a Q in the customer service area (http://oldlook.experts-exchange.com/Customer_Service/Experts_Exchange/) and ask to fix this...
0
kkokalAuthor Commented:
jkr,

Thx for letting me know. I'll get in touch with the Customer service folks.

Cheers
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.