I am looking to convert a [32]C.wchar_t to a go string.
The array is defined as follows in the dll I am talking to:
typedef struct myStruct {
WCHAR someString[32];
}
I am defining the struct in go as follows:
type myStruct struct {
someString [32]C.wchar_t
}
I have a method in the dll:
DLLINTERFACE HRESULT __stdcall GetMyStruct (myStruct* ptrMyStruct);
This method will populate the someString field of myStruct.
I am calling the method like so (this is working correctly, I think, I have not been able to see the contents of someString):
func getMyStruct() (*myStruct, uintptr) {
var getMyStruct = dll.MustFindProc("GetMyStruct")
var args = new(myStruct)
ret, _, _ := getMyStruct .Call(uintptr(unsafe.Pointer(args)))
fmt.Printf("Return: %d\n", (int)(ret))
return args, ret
}
I need to convert someString to a go string. I have tried using "github.com/GeertJohan/cgo.wchar", but it does not have a method for converting []C.whar_t to go string.
Currently I'm not sure if my go struct is correct. I'm also not sure if I am initializing myStruct correctly before sending it to the dll.
Any help will be greatly appreciated.
On Windows, wchar_t is normally UTF-16 (little-endian). They don't normally start with a BOM (which is present so a decoder can detect if they are stored in big or little endian form).
There is a utf16 package but this only translates individual runes. However, there is an additional unicode text encoding package that can help.
You would probably do something like this:
dec:=unicode.UTF16(unicode.LittleEndian,unicode.UseBOM).NewDecoder()
out,err:= dec.Bytes(([]byte)(unsafe.Pointer(args.someString)))
if err!=nil {
//handle error
}
// Strings are null terminated, only want content up to null byte
i:=bytes.IndexByte(out,0)
if i==-1 {
i = len(out)
}
s:=string(out[:i])
However, I'd be tempted to declare someString as [64]byte which is the amount of bytes that a 32 character (16 bites = 2 bytes per character) would need. This would avoid the unsafe typecasting but otherwise should work as above.
I'm doing this off the top of my head so the code above is meant as an example & may not necessarily work - use at your peril :-)
It seems GeertJohan's library hasn't been updated for the more recent cgo changes, but a fork has, try github.com/vitaminwater/cgo.wchar (godoc) instead.
If the C function writes to a C type, pass a variable of the C type.
A (dodgy) example:
package main
/*
#include <wchar.h>
#include <string.h>
typedef struct myStruct {
wchar_t someString[32];
} myStruct;
wchar_t sample[6] = {0x0048, 0x0069, 0x0020, 0x4e16, 0x754c, 0};
void writeSample(myStruct *m) {
memcpy(m->someString, &sample, sizeof(wchar_t) * 6);
}
*/
import "C"
import (
"fmt"
"log"
"unsafe"
"github.com/vitaminwater/cgo.wchar"
)
func main() {
m := C.myStruct{}
C.writeSample(&m)
s, err := wchar.WcharStringPtrToGoString(unsafe.Pointer(&m.someString))
if err != nil {
log.Fatal(err)
}
fmt.Println(s)
}
This outputs:
Hi 世界
Related
I am using InstallShield 2013 Premium. I created a C++ dll in Visual Studio 2010 to provide some functionality I could not achieve with InstallScript alone. My C++ function needs to return a small string (a username) to the InstallScript after doing considerable work to get this value.
Throughout the C++ am I using CStringW to represent my strings. Ideally, I would like to return it as Unicode, but I'm content with ANSI if that's my only option. I have tried numerous approaches with CStringW, std::wstring, std::string, LPCTSTR, LPSTR, char *... I tried direct returns, and attempts to return by reference. Nothing works!
Sometimes the dll function hangs, sometimes it throws an exception, at best it returns garbage values with non-printing characters. The official documentation on this does not seem accurate (it doesn't work for me!). Extensive Googling, and searching the Flexera boards produce "solutions" from others struggling with the same ridiculous problem, and yet non of those work for me either...
I didn't try this until the end, as I took for granted that you could pass strings between dlls and InstallScript easily enough. In retrospect, I should have started with the interface between the two and then developed the dll functionality after that.
Thanks for the help guys! I finally figured this out for myself though. There are multiple facets to the solution, however, which I have not found documented or suggested elsewhere.
Major points
1) return a WCHAR * from C++
2) use WSTRING as the corresponding return type in the InstallScript prototype
3) return it into a regular STRING variable in InstallScript, and treat it like any other
4) retain the value the WCHAR * points to in the C++ dll in a static variable, otherwise it apparently gets deleted and the pointer becomes invalid
If you've gotten far enough to find yourself in the same boat, I probably don't need to serve up every detail, but here's a chunk of example code to help you along:
Visual Studio Def File
LIBRARY MyIsDllHelper
EXPORTS
getSomeStringW #1
C++ Header
#ifdef MYISDLLHELPER_EXPORTS
#define MYISDLLHELPER_API __declspec(dllexport)
#else
#define MYISDLLHELPER_API __declspec(dllimport)
#endif
#include <stdexcept>
#include <atlstr.h>
namespace MyIsDllHelper
{
class MyIsDllHelper
{
public:
static MYISDLLHELPER_API WCHAR * getSomeStringW();
};
}
C++ Source
#include "stdafx.h"
#include "MyIsDllHelper.h"
static CStringW someStringRetained;
CStringW getTheString()
{
CStringW s;
// do whatever...
return s;
}
WCHAR * MyIsDllHelper::MyIsDllHelper::getSomeStringW()
{
someStringRetained = getTheString();
return someStringRetained.GetBuffer( someStringRetained.GetLength() ) + L'\0';
}
InstallScript
#define HELPER_DLL_FILE_NAME "MyIsDllHelper.dll"
prototype WSTRING MyIsDllHelper.getSomeStringW();
function DoSomething( hMSI )
STRING svSomeString;
STRING svDllPath;
begin
// Find the .dll file path. (A custom function)
GetSupportFilePath( HELPER_DLL_FILE_NAME, TRUE, svDllPath );
// Load the .dll file into memory.
if( UseDLL( svDllPath ) != 0 ) then
MessageBox ("Could not load dll: " + svDllPath, SEVERE );
abort;
endif;
// Get the string from the dll
try
svSomeString = MyIsDllHelper.getSomeStringW();
catch
MessageBox( "Could not execute dll function: MyIsDllHelper.getSomeStringW", SEVERE );
abort;
endcatch;
// Remove the .dll file from memory.
if( UnUseDLL( svDllPath ) < 0 ) then
MessageBox ("Could not unload dll: " + svDllPath, SEVERE );
abort;
endif;
// Use the string
MessageBox( "svSomeString: [" + svSomeString + "]", INFORMATION );
end;
You're best off when you can make your interface use C approaches rather than C++ ones. Match the interface of functions like GetEnvironmentVariable in which your function accepts a pointer to a buffer (and for correctness a size of that buffer), and then writes into that buffer. The majority of your implementation shouldn't have to change, as long as you can finish with something like a StringCchCopy from your CString into the buffer.
Since you specifically mention CStringW and other Unicode string types, I'd suggest choosing LPWSTR (rather than LPTSTR) for the interface type.
Then all that's left is declaring this for consumption by InstallScript. This means the prototype should use WSTRING and BYREF. If the function interface is the same as GetEnvironmentVariableW, the prototype should look something like this:
prototype MyFunc(WSTRING, BYREF WSTRING, NUMBER);
You can use strings, but I guess the problem is with the encoding.
Have a look here: https://adventuresinscm.wordpress.com/2014/01/12/unicode-files-and-installshield/
I'm trying to call a C++ DLL function that is defined like this:
int read_record (filep *fptr, int key, char *contents, int *status)
This is a widely used DLL, so I'm pretty certain the problem I'm having is how I'm calling the function.
The DLL docs have this example of how to call it
TFILETYPE *fptr; /* file pointer
char contents[80];
int status = 0;
int single = key;
if (read_record(fptr, key, card, &status)) break;
printf("%s\n", card);
Here's what I think should work, and almost does:
type
TCharArray = Array[1..100] of AnsiChar; // Function returns an array less than 100 char
var
read_record : function( var fptr: TFILETYPE;
Key: Integer;
var Contents: TCharArray; // function fills this in
var Status: Integer): Integer cdecl stdcall;
Procedure Test;
var
Contents: TCharArray;
Status: Integer;
Key: Integer;
begin
#read_record:= GetProcAddress(DLLHandle, 'read_record');
for Key := 1 to 10 do
begin
Contents[1] := #0; // shouldn't be necessary
if (read_record( fptr^, Key, Contents, Status) <> 0) OR (Status <> 0) then
ShowMessage('Error')
else
ShowMessage(Contents); // This shows the expected proper string on all 10 calls
...Other calls at this point to other functions in the DLL result in
an Exception writing to x01a.
end;
Multiple calls from Delphi XE work fine. But after that, when I call different function in the DLL that has always worked in the past, I get an exception writing to x0000001a, which I suspect means I've trashed memory or the stack.
The *fptr pointer datatype I'm using in calls to other functions in the dll, so I don't think that's the problem.
This is the first time I've tried to call a function that returns a string, so I suspect I'm not understanding something with call by reference of string arrays.
Any suggestions on how I should call this function differently to avoid what appears to be trashing of memory?
You have a couple of problems here.
First, the function declaration in Delphi is wrong. First, it's declared as both cdecl and stdcallat the same time. It needs to be one or the other; it can't be both simultaneously.
Also, you have an extra level of dereferencing on the fptr variable.
The declaration indicates it's a pointer:
filep *fptr
You've said in your Delphi declaration that it's a pointer:
var fptr: TFileType
But you're passing a pointer to a pointer:
fptr^
Change your call to the DLL function to
if (read_record( fptr, Key, Contents, Status) <> 0) OR (Status <> 0)
(I think your test of the results should actually be <> 0) AND (Status <> 0), but without the docs I'm not sure.)
If you do in fact need to initialize Contents before passing it to the DLL, you should use FillChar(Contents, SizeOf(Contents), #0) (or ZeroMemory) to do so, BTW.
As an additional suggestion, you can simplify your code somewhat (I've chosen stdcall as the calling convention):
var
read_record : function( var fptr: TFILETYPE;
Key: Integer;
Contents: PAnsiChar; // function fills this in
var Status: Integer): Integer stdcall;
var
Contents: AnsiString;
...
begin
SetLength(Contents, 100);
if (read_record(fptr, Key, PAnsiChar(Contents), Status)...
....
end;
I'm trying to return structure so I can use it in Python. I am beginner programmer so please explain me what am I doing wrong. I've succeeded to return simple ctypes earlier (bool, unsigned int) but struct is too complicated for me. This is what I have:
DLLAPI.h
#define DLLAPI extern "C" __declspec(dllexport)
...
DLLAPI myStruct* DLLApiGetStruct();
DLLAPI.cpp
EDIT1: instead of TString, struct members type is wchar_t* now, but error I get is the same
...
typedef struct myStruct{
wchar_t* id;
wchar_t* content;
wchar_t* message;
} myStruct;
DLLAPI myStruct* DLLApiGetStruct(){
myStruct* test = new myStruct();
test->id = _T("some id");
test->content = _T("some content");
test->message = _T("some message");
return test;
}
here is my Python code:
...
class TestStruct(Structure):
_fields_ = [
("id", c_wchar_p),
("content", c_wchar_p),
("message", c_wchar_p)
]
class SomeClass(object):
....
def test(self):
myDLL = cdll.LoadLibrary('myDLL.dll')
myDLL.DLLApiGetStruct.restype = TestStruct
result = myDLL.DLLApiGetStruct()
print "result type: ", type(result)
print "-"*30
print "result: ",result
print "-"*30
print result.id # line 152
this is what I get:
result type: <class 'Foo.TestStruct'>
------------------------------
result: <Foo.TestStruct object at 0x027E1210>
------------------------------
Traceback (most recent call last):
....
....
....
line 152, in test
print result.id
ValueError: invalid string pointer 0x00000002
TString I've used is std::wstring
Should type in myStruct be pointers or something instead TString?
Please help me, I've spend 5 days trying to make this work.
As the others explained, the problem with version 1 of the question is the use of std::string which is not a valid type for interop.
Looking at version 2 of the question, your C++ and Python declarations do not match. The C++ code returns a pointer to the struct, but the Python code expects the struct to be returned by value.
You could change either C++ or Python to match the other.
C++
DLLAPI myStruct DLLApiGetStruct()
{
myStruct result;
result.id = L"some id";
result.content = L"some content";
result.message = L"some message";
return result;
}
Python
myDLL.DLLApiGetStruct.restype = POINTER(TestStruct)
Obviously you must apply only one of these changes!
Note that in the C++ code I chose to use explicit wide strings with the L prefix rather than the _T() macro. The former matches wchar_t* and the latter is what you use with TCHAR. I would not recommend TCHAR these days, unless you need to support Win98.
http://docs.python.org/3.1/library/ctypes.html
c_wchar_p contains wchar_t *, not std::wstring
The problem is you are returning a structure containing std::string's but you are telling Python that the types are pointers to wchar_t. This has the same effect as doing the following in C++.
struct Foo
{
std::string id;
std::string content;
std::string message;
};
struct Bar
{
wchar_t* id;
wchar_t* content;
wchar_t* message;
};
Foo f;
Bar* = reinterpret_cast<Bar*>(&f);
I am new to _bstr_t's and still trying to get the hang of it. I was trying to check whether a particular string x is contained anywhere within the bstring. Something I would normally do like;
String x = "hello";
String example = "You! hello there";
...
if (example.find(x) != string::npos) {
...
Just for the record the intended platform is windows.
There is no need to use _bstr_t. Use the BSTR type.
Next, read Eric's Complete Guide to BSTR Semantics.
Lastly, you can use the BSTR in native code the way you would a normal character array in most cases.
BSTR bstr = SysAllocString(L"FooBarBazQux");
if (wcsstr(bstr, L"Bar") != NULL) {
// Found it! Do something.
} else {
// Not there.
}
SysFreeString(bstr);
MSDN for wcsstr.
Your example appears to be trying to use string::find from STL. But you specify your variables of type "String" (capitalized). If you instead did:
using namespace std;
string x = "hello";
string example = "You! hello there";
...
your example would compile. Do you actually have a BSTR or _bstr_t that you need to work with that you haven't shown? Even if so, it's pretty easy to make an std::string from a _bstr_t, and after that you can use STL as you normally would.
(This is not so much a problem as an exercise in pedantry, so here goes.)
I've made a nice little program that is native to my linux OS, but I'm thinking it's useful enough to exist on my Windows machine too. Thus, I'd like to access Windows' environment variables, and MSDN cites an example like this:
const DWORD buff_size = 50;
LPTSTR buff = new TCHAR[buff_size];
const DWORD var_size = GetEnvironmentVariable("HOME",buff,buff_size);
if (var_size==0) { /* fine, some failure or no HOME */ }
else if (var_size>buff_size) {
// OK, so 50 isn't big enough.
if (buff) delete [] buff;
buff = new TCHAR[var_size];
const DWORD new_size = GetEnvironmentVariable("HOME",buff,var_size);
if (new_size==0 || new_size>var_size) { /* *Sigh* */ }
else { /* great, we're done */ }
}
else { /* in one go! */ }
This is not nearly as nice (to me) as using getenv and just checking for a null pointer. I'd also prefer not to dynamically allocate memory since I'm just trying to make the program run on Windows as well as on my linux OS, which means that this MS code has to play nicely with nix code. More specifically:
template <class T> // let the compiler sort out between char* and TCHAR*
inline bool get_home(T& val) { // return true if OK, false otherwise
#if defined (__linux) || (__unix)
val = getenv("HOME");
if (val) return true;
else return false;
#elif defined (WINDOWS) || defined (_WIN32) || defined (WIN32)
// something like the MS Code above
#else
// probably I'll just return false here.
#endif
}
So, I'd have to allocate on the heap universally or do a #ifdef in the calling functions to free the memory. Not very pretty.
Of course, I could have just allocated 'buff' on the stack in the first place, but then I'd have to create a new TCHAR[] if 'buff_size' was not large enough on my first call to GetEnvironmentVariable. Better, but what if I was a pedant and didn't want to go around creating superfluous arrays? Any ideas on something more aesthetically pleasing?
I'm not that knowledgeable, so would anyone begrudge me deliberately forcing GetEnvironmentVariable to fail in order to get a string size? Does anyone see a problem with:
const DWORD buff_size = GetEnvironmentVariable("HOME",0,0);
TCHAR buff[buff_size];
const DWORD ret = GetEnvironmentVariable("HOME",buff,buff_size);
// ...
Any other ideas or any suggestions? (Or corrections to glaring mistakes?)
UPDATE:
Lots of useful information below. I think the best bet for what I'm trying to do is to use a static char[] like:
inline const char* get_home(void) { // inline not required, but what the hell.
#if defined (__linux) || (__unix)
return getenv("HOME");
#elif defined (WINDOWS) || defined (WIN32) || defined (_WIN32)
static char buff[MAX_PATH];
const DWORD ret = GetEnvironmentVariableA("USERPROFILE",buff,MAX_PATH);
if (ret==0 || ret>MAX_PATH)
return 0;
else
return buff;
#else
return 0;
#endif
}
Perhaps it's not the most elegant way of doing it, but it's probably the easiest way to sync up what I want to do between *nix and Windows. (I'll also worry about Unicode support later.)
Thank you to everybody who has helped.
DWORD bufferSize = 65535; //Limit according to http://msdn.microsoft.com/en-us/library/ms683188.aspx
std::wstring buff;
buff.resize(bufferSize);
bufferSize = GetEnvironmentVariableW(L"Name", &buff[0], bufferSize);
if (!bufferSize)
//error
buff.resize(bufferSize);
Of course, if you want ASCII, replace wstring with string and GetEnvironmentVariableW with GetEnvironmentVariableA.
EDIT: You could also create getenv yourself. This works because
The same memory location may be used in subsequent calls to getenv, overwriting the previous content.
const char * WinGetEnv(const char * name)
{
const DWORD buffSize = 65535;
static char buffer[buffSize];
if (GetEnvironmentVariableA(name, buffer, buffSize))
{
return buffer;
}
else
{
return 0;
}
}
Of course, it would probably be a good idea to use the wide character versions of all of this if you want to maintain unicode support.
This wasn't the original question, but it might worth to add the MFC way to this thread for reference:
CString strComSpec;
if (strComSpec.GetEnvironmentVariable(_T("COMSPEC")))
{
//Do your stuff here
}
VC++ implements getenv in stdlib.h, see, for example, here.
The suggestion you made at the end of your post is the right way to do this - call once to get required buffer size and then again to actually get the data. Many of the Win32 APIs work this way, it's confusing at first but common.
One thing you could do is to pass in a best-guess buffer and its size on the first call, and only call again if that fails.
Don't bother. %HOME% is a path on Windows, and should be usable by all reasonable programs. Therefore, it will fit in a WCHAR[MAX_PATH]. You don't need to deal with the edge case where it's longer than that - if it's longer, most file functions will reject it anyway so you might as well fail early.
However, do not assume you can use a TCHAR[MAX_PATH] or a char[MAX_PATH]. You do not have control over the contents of %HOME%; it will contain the users name. If that's "André" (i.e. not ASCII) you must store %HOME% in a WCHAR[MAX_PATH].