NO

Author Topic: BSTR data type for dynamic strings?  (Read 10418 times)

PBFan

  • Guest
BSTR data type for dynamic strings?
« on: January 04, 2011, 07:40:53 PM »
@all,

I'm not a C programmer (I develop in PowerBasic) but I'm curious to call my PB Win32 DLLs with a native C environment. So I've written a little dialog which until now concatinates two fixed strings (taken from two dialog fields) to a result string. With the help of the Pelles C samples that was even possible to program in C for me...  ;)

Now I'll extend this program to get the second string not from the dialog field but from an external PB DLL. The problem is, that the PB DLL delivers a so-called "dynamic string" (in C it is the BSTR data type, which is supported by the Win32 OLE subsystem).

How can I realize that with Pelles C or what have I to do. that that BSTR is recognized as data type. Or are there other alternatives?

bye,
Volker


Offline TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 2115
Re: BSTR data type for dynamic strings?
« Reply #1 on: January 05, 2011, 10:31:30 AM »
http://msdn.microsoft.com/en-us/library/ms221069(VS.85).aspx
This small example use BSTR
Code: [Select]
#include <wchar.h>
#include <objbase.h>
#include <assert.h>

#pragma lib "ole32.lib"
#pragma lib "oleaut32.lib"

/////////////////////////////////////////////////////
int main(void)
{
BSTR bstr;

bstr = SysAllocString(L"Hello World"); // Allocate BSTR
if (bstr == 0)
{
wprintf(L"Unable to allocate BSTR\n");
return 1;
}

wprintf(L"String: %ls\n", bstr);
wprintf(L"Byte Length=%u\n", ((DWORD*)bstr)[-1]);
wprintf(L"Length=%d\n", wcslen(bstr));
SysFreeString(bstr);

return 0;
}
May the source be with you

PBFan

  • Guest
Re: BSTR data type for dynamic strings?
« Reply #2 on: January 05, 2011, 12:50:18 PM »
@timovjl,

many thanks! I'll try to understand (  ;) ) and implement that and then give a report...

bye,
Volker

PBFan

  • Guest
Re: BSTR data type for dynamic strings?
« Reply #3 on: January 05, 2011, 04:07:27 PM »
Ok, now I'm a step further...

This is my "OK Button Code" (Callback routine):

Code: [Select]

case IDOK:

                    /* GetDlgItemText(hwndDlg, TB1, szItemName1, 80); */
            /* GetDlgItemText(hwndDlg, TB2, szItemName2, 80); */

                    bstr1 = SysAllocString(L"Hello World, ");           // Allocate BSTR
                    bstr2 = SysAllocString(L"let's call PB DLLs...");     // Allocate BSTR        

            /* strcat(szItemName1, szItemName2); */
                    /* SetDlgItemText(hwndDlg, Label1, szItemName1); */

                    wcscat(bstr1, bstr2);
            SetDlgItemTextW(hwndDlg, Label1, bstr1);
                    SysFreeString(bstr1);
            SysFreeString(bstr2);

                    return TRUE;


I've substituted the original char variables szItemName1 amd szItemName2 with BSTR variables bstr1 and bstr2 and even the "SetDlgItemTextW(hwndDlg, Label1, bstr1)" command delivers the expected concatinated string, but then the program hangs and is only to abort via the WIN32 task manager...  :'(

I've found, that the " wcscat(bstr1, bstr2)" command is the reason (nevertheless it delivers the right concatinated string!).

Anyone any ideas?

bye,
Volker
 
 

Offline TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 2115
Re: BSTR data type for dynamic strings?
« Reply #4 on: January 05, 2011, 05:12:45 PM »
You can't use this:
wcscat(bstr1, bstr2);

Use another bigger buffer instead bstr1 that have limited length.


May the source be with you

PBFan

  • Guest
Re: BSTR data type for dynamic strings?
« Reply #5 on: January 05, 2011, 05:27:35 PM »
...these are the problems for "non-C" programmers!

In my (home) language I easily can do coding this:

string3 = string1 + string2


But how to do it (using a bigger buffer or new variable) in C ???

I currently only know the strcat (with char) and wcscat (with bstr) commands which concatinate string1 and string2 to a new string1...

You see, in C i know only a little more than nothing...  ;)

bye,
Volker

PBFan

  • Guest
Re: BSTR data type for dynamic strings?
« Reply #6 on: January 05, 2011, 11:41:03 PM »
...hmm, try to say it easier:

This code with "fixed char data type" works (and runs in a Pelles C program - see little dialog gif in #1):
Code: [Select]
/****************************************************************************
 *                                                                         
 * Function: MainDlgProc                                                   
 * 
 * Purpose : Process messages for the Main dialog.                         
 *                                                                         
 ****************************************************************************/
char szItemName1 [80];
char szItemName2 [80];

static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
            /*
             * TODO: Add code to initialize the dialog.
             */
            return TRUE;

        case WM_SIZE:
            /*
             * TODO: Add code to process resizing, when needed.
             */
            return TRUE;

        case WM_COMMAND:
            switch (GET_WM_COMMAND_ID(wParam, lParam))
            {
                /*
                 * TODO: Add more control ID's, when needed.
                 */
                case IDOK:
                    GetDlgItemText(hwndDlg, TB1, szItemName1, 80);
GetDlgItemText(hwndDlg, TB2, szItemName2, 80);
strcat(szItemName1, szItemName2);
                    SetDlgItemText(hwndDlg, Label1, szItemName1);
                    return TRUE;

   case IDCANCEL:
                    EndDialog(hwndDlg, TRUE);
                    return TRUE;
            }
            break;

        case WM_CLOSE:
            EndDialog(hwndDlg, 0);
            return TRUE;

        /*
         * TODO: Add more messages, when needed.
         */
    }

    return FALSE;
}


Is it possible to substitute the two "fixed char variables" by two "dynamic BSTR" variables and has someone an idea to code this in C ???

bye,
Volker

CommonTater

  • Guest
Re: BSTR data type for dynamic strings?
« Reply #7 on: January 06, 2011, 04:42:09 AM »
Personally... I would not use BSTR for variables inside a program.  I would use TCHAR strings, employing BSTR only to export or import strings from OLE or DB functions.  The method you have in your code sample should be just fine inside your programs... but when you need to communicate with libs or dlls using BSTRs, covert only as needed.

Code: [Select]
#define UNICODE
#include <windows.h>

//string3 = string1 + string2
PTCHAR string3 = malloc(((lstrlen(string1) + lstrlen(string2)) * sizeof(TCHAR)) + sizeof(TCHAR));
 lstrcpy(string3,string1);
 lstrcat(string3,string2);

// for ouside call
BSTR expstring = SysAllocString(string3);

// do functions calls here

SysFreeString(expstring);







« Last Edit: January 06, 2011, 04:50:49 AM by CommonTater »

PBFan

  • Guest
Re: BSTR data type for dynamic strings?
« Reply #8 on: January 06, 2011, 03:57:22 PM »
... thanks for your hints.

Now I've found a way to prepare this little dialog for a later use with a "dynamic string" (BSTR) input from a foreign (PowerBasic) DLL. Using wchar_t and the wcs... functions of Pelles C it runs also including the optional BSTR variable. I show the main.c here (the main part of the program is the callback function part for the OK Button "IDOK"). Perhaps there can be deleted some defines, includes or also code - I don't know, because C is so far away of my knowledge...

Code: [Select]
/****************************************************************************
 *                                                                                                   
 * File    : main.c                                                                         
 *                   
 * Purpose : based on Pelles C generic dialog based Win32       
 * application: test program for calling PB/Win32 DLLs                     
 *                                                                         
 * copyright 2011 Dipl.-Ing. Volker Butzlaff
 *
 * 01/06/2011
 *
 *Pelles C v6.00.4  (calling PowerBasic Win32 v9.05 DLLs)
 *
 * ****************************************************************************/

#define WIN32_LEAN_AND_MEAN
#define WIN32_DEFAULT_LIBS

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <tchar.h>
#include "main.h"

#include <wchar.h>
#include <objbase.h>
#include <assert.h>

#pragma lib "ole32.lib"
#pragma lib "oleaut32.lib"

#define NELEMS(a)  (sizeof(a) / sizeof((a)[0]))

/** Prototypes **************************************************************/

static INT_PTR CALLBACK MainDlgProc(HWND, UINT, WPARAM, LPARAM);

/** Global variables ********************************************************/

static HANDLE ghInstance;

/****************************************************************************
 *                                                                         
 * Function: WinMain                                                       
 *                                                                         
 * Purpose : Initialize the application.  Register a window class,         
 *                 create and display the main window and enter the               
 *                 message loop.                                               
 *
 ****************************************************************************/

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
    INITCOMMONCONTROLSEX icc;
    WNDCLASSEX wcx;

    ghInstance = hInstance;

    /* TODO: set the ICC_???_CLASSES that you need. */
    icc.dwSize = sizeof(icc);
    icc.dwICC = ICC_WIN95_CLASSES /*|ICC_COOL_CLASSES|ICC_DATE_CLASSES|ICC_PAGESCROLLER_CLASS|ICC_USEREX_CLASSES|... */;
    InitCommonControlsEx(&icc);

    /* Get system dialog information */
    wcx.cbSize = sizeof(wcx);
    if (!GetClassInfoEx(NULL, MAKEINTRESOURCE(32770), &wcx))
        return 0;

    /* Add our own stuff */
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_ICO_MAIN));
    wcx.lpszClassName = _T("pbcallClass");
    if (!RegisterClassEx(&wcx))
        return 0;

    /* The user interface is a modal dialog box */
    return DialogBox(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)MainDlgProc);
}

/****************************************************************************
 *                                                                         
 * Function: MainDlgProc                                                   
 * 
 * Purpose : Process messages for the Main dialog.                         
 *                                                                         
 ****************************************************************************/
wchar_t string1 [80];
wchar_t string2 [1024000];
wchar_t string3 [5120000];

static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
            /*
             * TODO: Add code to initialize the dialog.
             */
            return TRUE;

        case WM_SIZE:
            /*
             * TODO: Add code to process resizing, when needed.
             */
            return TRUE;

        case WM_COMMAND:
            switch (GET_WM_COMMAND_ID(wParam, lParam))
            {
                /*
                 * TODO: Add more control ID's, when needed.
                 */
                case IDOK:
                    GetDlgItemTextW(hwndDlg, TB1, string1, 80);
GetDlgItemTextW(hwndDlg, TB2, string2, 1024000);

                    /* alloc optional BSTR variable for later to implement outside DLL call */
                    BSTR expString = SysAllocString(string2);

wcscpy(string3,string1);
                    wcscat(string3,expString);
                   
                    SetDlgItemTextW(hwndDlg, Label1, string3);

                    /* free BSTR variable */
                    SysFreeString(expString);
 
                    return TRUE;

   case IDCANCEL:
                    EndDialog(hwndDlg, TRUE);
                    return TRUE;
            }
            break;

        case WM_CLOSE:
            EndDialog(hwndDlg, 0);
            return TRUE;

        /*
         * TODO: Add more messages, when needed.
         */
    }

    return FALSE;
}

The next step is to integrate the DLL call - every hint or improvement to the existing code is wellcome!

bye,
Volker

CommonTater

  • Guest
Re: BSTR data type for dynamic strings?
« Reply #9 on: January 06, 2011, 05:58:55 PM »
... thanks for your hints.

The next step is to integrate the DLL call - every hint or improvement to the existing code is wellcome!
bye,
Volker

Did you really intend to allocate 5 megabytes to a string?

Why not use the malloc method I showed?  The result is just big enough for the two joined strings. 

Offline TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 2115
Re: BSTR data type for dynamic strings?
« Reply #10 on: January 06, 2011, 06:19:54 PM »
http://www.jose.it-berater.org/smfforum/index.php?topic=1155.0
http://www.powerbasic.com/support/help/pbcc/comparative_data_types_c_c++.htm

This example may work:
Code: [Select]
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <oleauto.h>

// Sub MyPtr(ByVal myPointer As String Pointer, ByVal length As Long) Export
void _stdcall MyPtr(BSTR, int);
typedef void (WINAPI *MYPTR)(BSTR *myPointer, int length);

#pragma lib "oleaut32.lib"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HANDLE hLib;
MYPTR MyPtr;

hLib = LoadLibrary("PBDll.dll");
if (hLib) {
MyPtr = (MYPTR)GetProcAddress(hLib, "MYPTR");
//BSTR bstrTmp = SysAllocString(L"Test"); // WCHAR UNICODE
BSTR bstrTmp = SysAllocStringByteLen("Test", 4); // ANSI
//BSTR bstrTmp = SysAllocStringByteLen(NULL, 10);
if (MyPtr)
MyPtr(&bstrTmp, 1);
SysFreeString(bstrTmp);
FreeLibrary(hLib);
}
return 0;
}
I like to have this DLL to test with it.
Code: [Select]
#Compile Dll
#Include "WIN32API.INC"

'-------------------------------------------------------------------------------
' Main DLL entry point called by Windows...
'
Function LibMain (ByVal hInstance   As Long, _
                  ByVal fwdReason   As Long, _
                  ByVal lpvReserved As Long) As Long

        Function = 1
End Function

'-------------------------------------------------------------------------------
Sub MyPtr(ByVal myPointer As String Pointer, ByVal length As Long) Export

    ' Display the target value of @myPointer
    MsgBox @myPointer

    ' Display the length of the target value
    MsgBox Str$(length)
End Sub
« Last Edit: January 07, 2011, 01:18:41 PM by timovjl »
May the source be with you

PBFan

  • Guest
Re: BSTR data type for dynamic strings?
« Reply #11 on: January 06, 2011, 07:33:17 PM »
@CommonTater, timovjl,

many thanks for your input.

I've now a first working solution. The PB DLL function will be called () without parameter and delivers a dynamic string. For this scenario it seems, that it don't need even BSTR variables:

Code: [Select]
/
****************************************************************************
 *                                                                                                   
 * File    : main.c                                                                         
 *                   
 * Purpose : based on Pelles C generic dialog based Win32       
 * application: test program for calling PB/Win32 DLLs                     
 *                                                                         
 * copyright 2011 Dipl.-Ing. Volker Butzlaff
 *
 * 01/06/2011
 *
 *Pelles C v6.00.4  (calling PowerBasic Win32 v9.05 DLLs)
 *
 * ****************************************************************************/

#define WIN32_LEAN_AND_MEAN
#define WIN32_DEFAULT_LIBS

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <tchar.h>
#include "main.h"

#include <wchar.h>
#include <objbase.h>
#include <assert.h>
#include <oleauto.h>

/** Prototypes **************************************************************/

static INT_PTR CALLBACK MainDlgProc(HWND, UINT, WPARAM, LPARAM);
typedef char* __stdcall LPFNCALLDLL( );

/** Global variables ********************************************************/

static HANDLE ghInstance;

/****************************************************************************
 *                                                                         
 * Function: WinMain                                                       
 *                                                                         
 * Purpose : Initialize the application.  Register a window class,         
 *                 create and display the main window and enter the               
 *                 message loop.                                               
 *
 ****************************************************************************/

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
    INITCOMMONCONTROLSEX icc;
    WNDCLASSEX wcx;

    ghInstance = hInstance;

    /* TODO: set the ICC_???_CLASSES that you need. */
    icc.dwSize = sizeof(icc);
    icc.dwICC = ICC_WIN95_CLASSES /*|ICC_COOL_CLASSES|ICC_DATE_CLASSES|ICC_PAGESCROLLER_CLASS|ICC_USEREX_CLASSES|... */;
    InitCommonControlsEx(&icc);

    /* Get system dialog information */
    wcx.cbSize = sizeof(wcx);
    if (!GetClassInfoEx(NULL, MAKEINTRESOURCE(32770), &wcx))
        return 0;

    /* Add our own stuff */
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_ICO_MAIN));
    wcx.lpszClassName = _T("pbcallClass");
    if (!RegisterClassEx(&wcx))
        return 0;

    /* The user interface is a modal dialog box */
    return DialogBox(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)MainDlgProc);
}

/****************************************************************************
 *                                                                         
 * Function: MainDlgProc                                                   
 * 
 * Purpose : Process messages for the Main dialog.                         
 *                                                                         
 ****************************************************************************/
char string1 [80];
char string2 [80];
char string3 [255];

HMODULE hLib;
LPFNCALLDLL* lpCallDll;
char *chReturn;

static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
            /*
             * TODO: Add code to initialize the dialog.
             */
            return TRUE;

        case WM_SIZE:
            /*
             * TODO: Add code to process resizing, when needed.
             */
            return TRUE;

        case WM_COMMAND:
            switch (GET_WM_COMMAND_ID(wParam, lParam))
            {
                /*
                 * TODO: Add more control ID's, when needed.
                 */
                case IDOK:
                    GetDlgItemText(hwndDlg, TB1, string1, 80);
/* GetDlgItemTextW(hwndDlg, TB2, string2, 1024000); */

                    /* alloc optional BSTR variable for later to implement outside DLL call */
                    /* BSTR expString = SysAllocString(string2); */

       /* get handle to DLL */
                    hLib = LoadLibrary("pbdll.dll");
     
                    /* get pointer to DLL function */
                    lpCallDll = (LPFNCALLDLL*)GetProcAddress( hLib, "pbstring" );

                    /* get dynamic string from external DLL function */
                    chReturn = lpCallDll( );       

      /* concatinate the strings of the dialog fields and/or with the string from the external DLL */
strcpy(string3,string1);
                    strcat(string3, chReturn);
                   
                    SetDlgItemText(hwndDlg, Label1, string3);

                    /* free BSTR variable */
                    /* SysFreeString(expString); */

       /* release the DLL */
                    FreeLibrary(hLib);
                   
return TRUE;

   case IDCANCEL:
                    EndDialog(hwndDlg, TRUE);
                    return TRUE;
            }
            break;

        case WM_CLOSE:
            EndDialog(hwndDlg, 0);
            return TRUE;

        /*
         * TODO: Add more messages, when needed.
         */
    }

    return FALSE;
}

For the next step I have to look at the samples again, because If I call the PB DLL function with a dynamic string as parameter, I think I need an explicit BSTR.

bye,
Volker

PBFan

  • Guest
Re: BSTR data type for dynamic strings?
« Reply #12 on: January 07, 2011, 12:26:25 PM »
... now I'm seeing the target line...

There remains only one problem with the C prototype definition for the DLL call.
Now it's coded so:

Code: [Select]
typedef char* __stdcall LPFNCALLDLL( BSTR pbParameter );
The pbParameter is a dynamic string, which I deliver to the PB DLL and dependent from its content there will be delivered the result string from PB to C.

Unfortunately this only runs properly if I define my pbParameter String in my PB DLL "ByVal". For some other reasons this pbParameter is instead defined as "ByRef" (Standard in my PB Language). With "ByRef" I get a GPF in my C program. I've found a hint (surely from another C dialect) that I've to change only the typedef definition in this way:

Code: [Select]
typedef char* __stdcall LPFNCALLDLL( BSTR &pbParameter );
But that "&" won't be accepted by the Pelles C Compiler. Is there another way to code that correct?

Thanks,
Volker


Offline TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 2115
Re: BSTR data type for dynamic strings?
« Reply #13 on: January 07, 2011, 12:50:02 PM »
Code: [Select]
typedef char* __stdcall LPFNCALLDLL( BSTR *pbParameter );
May the source be with you

Offline Stefan Pendl

  • Global Moderator
  • Member
  • *****
  • Posts: 582
    • Homepage
Re: BSTR data type for dynamic strings?
« Reply #14 on: January 07, 2011, 01:19:44 PM »
The ampersand (&) is used to provide an argument byref, when you call a function.

The asterisk (*) is used to define an argument as byref, when you declare a function.
---
Stefan

Proud member of the UltraDefrag Development Team