Returning a string value from dll

319 views
Skip to first unread message

Hayden Fisher

unread,
Apr 1, 2009, 2:40:50 AM4/1/09
to mapi...@googlegroups.com

Hi,

 

I have created my own dll in c++ to use in MapInfo. The exportable function takes a string parameter and returns a string value. Example below.

 

BSTR __declspec(dllexport) __stdcall SayHello(BSTR BSTR_str)

{

            AFX_MANAGE_STATE(AfxGetStaticModuleState());

 

            CString CString_str = _T("");

            //This converts the BSTR to CString

            if (BSTR_str != NULL)

            {

                        CString s;

                        LPSTR p = s.GetBuffer(::SysStringLen(BSTR_str) + 1);

                        BOOL UsedDefaultChar;

 

                        ::WideCharToMultiByte(CP_ACP, 0, BSTR_str, -1, p, ::SysStringLen(BSTR_str)+1, NULL, &UsedDefaultChar);

           

                        if (UsedDefaultChar)

                        {

                                    CString_str = (LPCTSTR)BSTR_str;

                        }

                        else

                        {

                                    CString_str = (LPCWSTR)BSTR_str;

                        }

            }

            CString_str = “Hello there ” + CString_str;

           

            return CString_str.AllocSysString(); //Which return a BSTR type

}

 

Now in the mbx program, I’m trying to retrieve that string value, in VB you could convert it but I’m not sure how in MapBasic, simply placing it into a string variable will give me the first letter.

Should I be doing it this way or can I return some other type that I can retrieve easier?

 

Any ideas or suggestions would be greatly appreciated!

 

Regards,

Hayden Fisher

 

Uffe Kousgaard

unread,
Apr 1, 2009, 3:15:22 AM4/1/09
to mapi...@googlegroups.com
You should store your string (not unicode) in a zero-terminated buffer and return a pointer to the buffer to MB.

In MB the declaration should look like:
Declare Function SomeFunction Lib "some.dll" as string

The VB and MB way of doing it is not compatible, afaik.

Regards
Uffe Kousgaard

Hayden Fisher

unread,
Apr 1, 2009, 3:40:17 AM4/1/09
to mapi...@googlegroups.com

Hi Uffe,

 

Just a couple of questions, still brushing up on my c++, is LPTSTR a zero-terminated buffer and is CString unicode?

 

To return that pointer how does this look and I know this won’t quite work but am I close at all?

 

LPTSTR __declspec(dllexport) __stdcall SayHello()

{

            LPTSTR myBuffer;

            CString myReturnValue = “some text”;

 

myBuffer = myReturnValue.GetBuffer(0);

            return &myBuffer;

}

 

Regards,

Hayden Fisher

Development and Support

BizeAsset Pty Ltd

sup...@bizeasset.com.au

Mob: (04) 3838 4747

Ph: (07) 3369 2222

Toll Free: 1800 836 850


Uffe Kousgaard

unread,
Apr 1, 2009, 4:06:32 AM4/1/09
to mapi...@googlegroups.com
Hi,

My experience is based on delphi-dll's. I'm pretty blank on c++.

When you say you only get the first character in mb, I think it means you are passing a unicode string. I think that's also what BSTR basically is.

I have attached a delphi example.

Regards
Uffe
DLLtest.zip

Hayden Fisher

unread,
Apr 1, 2009, 6:31:55 AM4/1/09
to mapi...@googlegroups.com

Hi,

 

Ok I’ve done some changes to the dll and it is passing back the pointer no problem. However what is happening is that once the function returns, the string that was created in the function, its destructor is called and what I’m feeding into my string variable back in MB is rubbish.

 

So looking at your example (and I don’t know delphi) you declare your string as a global variable ensuring that it doesn’t disappear when the function ends???

And this is where I get confused, because you have the pointer sent back, it is able to free up that memory as soon as the MB program has finished with (as in your example) the ‘a’ or ‘b’ variables, say if you leave the sub routine.

 

Uffe

 

Any comments welcome

 

Regards,

Hayden

Uffe Kousgaard

unread,
Apr 1, 2009, 6:49:42 AM4/1/09
to mapi...@googlegroups.com
Hi,

The d variable in my program stores the value between the call to settext and gettext. When gettext is called this value is stored inside the buffer, a zero (null) is added and the address of the buffer is returned. The StrPLCopy function also ensures no more than 240 characters are copied to the buffer, even if the string is longer (the famous "buffer-overrun" problem).

The global variables don't disappear until the DLL is unloaded. Unloading happens when the mbx finishes.

Since there is only 1 buffer, this approach would be a problem in a multi-threaded scenario - luckily mapbasic isn't multi-threaded.

Tim Smith

unread,
Apr 1, 2009, 8:21:34 AM4/1/09
to MapInfo-L
Hi,

Can you not pass an allocated string by reference (not by val) as an
attribute to the DLL function? Then in the DLL you can do what you
wish with the string (you will have to know the max pre-allocated
string length you specified in MapBasic, perhaps passed as another
function parameter to the DLL), then you have no issues with variable
scope in the DLL.
I’m sure I’ve seen this work before.

Cheers

Tim

Gentreau

unread,
Apr 1, 2009, 12:33:10 PM4/1/09
to mapi...@googlegroups.com

That's what I do, I create a string in MapBasic padded to it's full extent
with spaces.
I pass the string ByVal to the DLL
In the DLL I then write what I want into the string
I then return an integer to indicate success or failure.

Hth
Gentreau.



-----Original Message-----
From: mapi...@googlegroups.com [mailto:mapi...@googlegroups.com] On
Behalf Of Tim Smith
Sent: Wednesday, April 01, 2009 12:22 PM
To: MapInfo-L
Subject: [MI-L] Re: Returning a string value from dll


Hayden Fisher

unread,
Apr 1, 2009, 10:11:27 PM4/1/09
to mapi...@googlegroups.com
I was thinking along the lines of having two functions one gets and sets
the string allocating the space and returns the pointer. The second is
called when string is no longer required and basically deletes the
string object freeing up the memory again.

Regards,
Hayden

Hayden Fisher

unread,
Apr 3, 2009, 1:51:57 AM4/3/09
to mapi...@googlegroups.com
Hi, I've found this note

If MapBasic behaves exactly like VB, you can return a BSTR containing
ANSI string and VB (or MapBasic) will automatically release the pointer
using SysFreeString.

Does this happen?

Eric Blasenheim

unread,
Apr 3, 2009, 5:15:12 PM4/3/09
to mapi...@googlegroups.com

MapBasic calling of DLL functions does not support BSTR which is a COM type which is Unicode.  The data on the DLL end must be single byte (char in C/C++)!

 

Eric Blasenheim

Chief Product Architect

Pitney Bowes Business Insight [MapInfo]

 

 

 

Mail List:grbounce-YvY1eQUAAAAJBprYSySRydkk7vpghP_9=mail_list=mapin...@googlegroups.com

From:

Hayden Fisher <sup...@bizeasset.com.au> on 04/03/2009 03:51 PM ZE10

To:

<mapi...@googlegroups.com>

cc:

 

Bo Victor Thomsen

unread,
Apr 6, 2009, 4:24:20 AM4/6/09
to mapi...@googlegroups.com
Try the following code. It's compiled with lcc-win32, a "pure" c-compiler

======== C- code mbdll.c ================
#include <windows.h>

// Don't mangle names if it's a C++ compiler

#ifdef __cplusplus
extern "C" {
#endif

// copies string in a to b .- max <l> characters
// Use _stdcall calling for dll's intended for MapBasic;
// remember that with _stdcall, the external function
// name will be changed from "copystr" to "_copystr@" + the
// number of bytes in the argument list.. (3*4 bytes = 12), i.e.
// external name becomes "_copystr@12"

long __declspec(dllexport) __stdcall copystr
(char *a, char *b, int l) {
strncpy (b,a,l);
return strlen(b);
}


// Don't mangle names if it's a C++ compiler

#ifdef __cplusplus
}
#endif

=============== MapBasic code: mbdll.mb ===================


' alias in function declaration is the exported
' name for the function in the dll
Declare function copystr lib "mbdll" alias
"_copystr@12" (byval a as string, b as string, byval l as integer)
as integer

Declare sub main

Sub Main
dim nLen as integer, nFinal as integer, sBuf as string

' Set the lenght to 255 characters
nLen = 255

' Initialize the string variable to 255 spaces
sBuf = space$(255)

' Call the function, nFinal will contain the number of
' characters copied
nFinal = copystr("Mickey Mouse",sBuf,255)

print str$(nfinal) + "==>" + sBuf + "<=="
end sub

Regards
Bo Thomsen
GeoConsult I/S
Denmark


Eric Blasenheim skrev:
>
> MapBasic calling of DLL functions does not support BSTR which is a COM
> type which is Unicode. The data on the DLL end must be single byte
> (char in C/C++)!
>
>
>
> Eric Blasenheim
>
> Chief Product Architect
>
> Pitney Bowes Business Insight [MapInfo]
>
>
>
>
>
>
>
> *Mail
> List:*/grbounce-YvY1eQUAAAAJBprYSySRydkk7vpghP_9=mail_list=mapin...@googlegroups.com/

Bill Thoen

unread,
Apr 6, 2009, 6:21:31 AM4/6/09
to mapi...@googlegroups.com
You should add that lcc win32 is free C compiler (for non-commercial use
-- if you make money with it you need to buy a license) and add the URL
http://www.cs.virginia.edu/~lcc-win32/ so others can see what a neat
package it is! It even does Windows! And it works well with MapInfo for
those times when you want to take MapBasic way out of its box.

Uffe Kousgaard

unread,
Apr 6, 2009, 6:34:21 AM4/6/09
to mapi...@googlegroups.com
I think we can also safely assume the absolute majority of the list
readers are doing some kind of commercial work, according to the
lcc-win32 definition. Should we guess at 99% or so?

Otherwise I think there are plenty of other free c compilers around, if
the 40 euro is too much for anyone.

Regards
Uffe Kousgaard

Hayden Fisher

unread,
Apr 7, 2009, 1:40:05 AM4/7/09
to mapi...@googlegroups.com
Hi Guys,

Thanks for all your help. I ended up creating a buffer string in
MapBasic, then creating exportable functions in my dll as below, the
first returns the designated length of the string and the second
populates it. I could have merged it into one function but decided not
to.

This dll reads, writes and deletes XML nodes. Did it for MapInfo
versions earlier than 8.5. Just send it through the XML file path and
node structure you want to query. However only sends back a single node
value one at a time.

int __declspec(dllexport) __stdcall GetXMLLength(BSTR xmlFilePath, BSTR
xmlNode)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

CString str_xmlFilePath = BSTR2CString(xmlFilePath);
CString str_xmlNode = BSTR2CString(xmlNode);

CString m_xml_value;
m_xml_value = _T("");
std::string _label;

ParamIO inXml;
inXml.readFile(str_xmlFilePath);
inXml.read(str_xmlNode, _label, std::string(""));
m_xml_value = _label.c_str();

int size = m_xml_value.GetLength();
return size;
}
int __declspec(dllexport) __stdcall ReadXML(char *MtBuffer, int length,
BSTR xmlFilePath, BSTR xmlNode)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

CString str_xmlFilePath = BSTR2CString(xmlFilePath);
CString str_xmlNode = BSTR2CString(xmlNode);

CString m_xml_value;
m_xml_value = _T("");
std::string _label;

ParamIO inXml;
inXml.readFile(str_xmlFilePath);
inXml.read(str_xmlNode, _label, std::string(""));
m_xml_value = _label.c_str();
m_xml_value = m_xml_value.Left(length);

_tcscpy(MtBuffer, m_xml_value);

return 0;
}
int __declspec(dllexport) __stdcall WriteXML(BSTR xmlFilePath, BSTR
xmlNode, BSTR xmlValue)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

CString str_xmlFilePath = BSTR2CString(xmlFilePath);
CString str_xmlNode = BSTR2CString(xmlNode);
CString str_xmlValue = BSTR2CString(xmlValue);
LPTSTR charValueText;

ParamIO outXml;
outXml.readFile(str_xmlFilePath); // Read the file from disk

charValueText = str_xmlValue.GetBuffer(0);
outXml.write(str_xmlNode, charValueText);
str_xmlValue.ReleaseBuffer();

outXml.writeFile(str_xmlFilePath);

return 0;
}
int __declspec(dllexport) __stdcall DeleteXML(BSTR xmlFilePath, BSTR
xmlNode)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());

CString str_xmlFilePath = BSTR2CString(xmlFilePath);
CString str_xmlNode = BSTR2CString(xmlNode);
LPTSTR charNodeText;

ParamIO outXml;
outXml.readFile(str_xmlFilePath); // Read the file from disk

charNodeText = str_xmlNode.GetBuffer(0);
outXml.erase(charNodeText);
str_xmlNode.ReleaseBuffer();

outXml.writeFile(str_xmlFilePath);

return 0;
}


Regards,
Hayden Fisher
Development and Support
BizeAsset Pty Ltd
sup...@bizeasset.com.au
Mob: (04) 3838 4747
Ph: (07) 3369 2222
Toll Free: 1800 836 850

-----Original Message-----
From: mapi...@googlegroups.com [mailto:mapi...@googlegroups.com] On

Gentreau

unread,
Apr 8, 2009, 9:29:54 AM4/8/09
to mapi...@googlegroups.com

I use Open Watcom, free for commercial use.

http://www.openwatcom.org/index.php/Main_Page



-----Original Message-----
From: mapi...@googlegroups.com [mailto:mapi...@googlegroups.com] On
Reply all
Reply to author
Forward
0 new messages