I have a DLL that I need to handle in C++. I'm using WxWidgets (standard compilation, but I also tried Unicode on/off) and NetBeans. I also tried dealing with this without WxWidgets (windows.h) and had same problems.
Here is how I access the DLL functions using WxWidgets:
// -------------------- POINTERS TO FUNCTIONS
typedef bool(*TYPE_DLL_SetLicense)(char*, char*);
typedef bool(*TYPE_DLL_PingConnection)(char*);
typedef char*(*TYPE_DLL_ERR_DESCRIPTION)(void);
class DLL_Library
{
public:
// pointers to functions inside dll
TYPE_DLL_SetLicense DLL_SetLicense; //initialize - will wor fine as it returns only true/false (buffer only provide data)
TYPE_DLL_PingConnection DLL_PingConnection; //ping to serwer. Will return trahs, becouse it uses buffer to provide data ang get answear back
TYPE_DLL_ERR_DESCRIPTION DLL_ERR_DESCRIPTION; //error description. No buffer, no trouble. Returns correct string.
wxDynamicLibrary dynLib2;
int initialize(void)
{
//patch to dll
wxString path = wxStandardPaths::Get().GetExecutablePath().BeforeLast('\\') + _("\\DLL_dll\\DLLMOK.dll");
if(!wxFile::Exists(path)) return -1;
//load dll
if(!dynLib2.Load(path)) return -2;
//Assign functions in dll to variable
DLL_SetLicense=(TYPE_DLL_SetLicense) dynLib2.GetSymbol(wxT("DLL_SetLicense"));
DLL_PingConnection=(TYPE_DLL_PingConnection) dynLib2.GetSymbol(wxT("DLL_PingConnection"));
DLL_ERR_DESCRIPTION=(TYPE_DLL_ERR_DESCRIPTION) dynLib2.GetSymbol(wxT("DLL_ERROR_DESCRIPTION"));
return 0;
}
};
And here is the function I run. It should return and XML content, that I try to save to the file.
//DLL_PingConnection
//result ping to be save in file
wxFile file_ping_xml;
plik_ping_xml.Open(wxT("C:\\dll\\ping.xml"),wxFile::write);
char buffor_ping_xml[2000];
//I run the function here
bool is_ping = DLL_PingConnection(buffor_ping_xml);
if(is_ping)
{
tex_box->AppendText(wxT("DLL_PingConnection True\n"));
//we save result to file
bool is_write_ping_ok = file_ping_xml.Write(buffor_ping_xml,2000);
if (is_write_ping_ok){tex_box->AppendText(wxT("Save to file is ok ok\n"));}
else {tex_box->AppendText(wxT("Save to file failed :( \n"));}
}
else
{
tex_box->AppendText(wxT("DLL_PingConnection False\n"));
}
std::cout << "Error description: " << DLL_ERR_DESCRIPTION() << "\n"; //will work fine both in saving to file, and in streaming to screen.
The problem is that inside the file instead of good content I get rubbish like this:
NOTE that this only happens in functions that use buffers like:
char buffer[2000] //buffer will contain for example file xml
function do_sth_with_xml(buffer) //buffer containing xml will (should) be overwriten with xml results of the function - in our case DLL_PingCONNECTION should save in buffer xml with connection data
Documentation say that the DLL operates on Windows-1250. File ping.xml I have set to windows ANSI, but I don't think problem lies here.
EDIT: I have written problem without WxWidgets (I load DLL using windows.h) - same problems. Here is the code: Getting trash data in char* while using it as buffer in function . Please help :(
This
DLL_PingConnection=(TYPE_DLL_PingConnection)
shouldn't it be
DLL_PingConnection=(TYPE_DLL_PingConnection) dynLib2.GetSymbol(wxT("DLL_PingConnection"));
?
seems otherwise you will not get a valid pointer to the function in the DLL.
as a general rule you should check return values, especially from a DLL
you load dynamically since it happens that you sometimes get another version
of the DLL which may have a function with same name but other signature or
where is missing entirely.
You named a function
DLL_PingConnection=(TYPE_DLL_PingConnection) dynLib2.GetSymbol(....
and call it with
OSOZ.OSOZ_PingConnection(buffor_ping_xml);
you typedef a function
typedef bool(*TYPE_DLL_PingConnection)(char*);
you create a variable
char buffor_ping_xml[2000];
in your typedef it is char* and your buffor_ping_xml is char
how can that work ?
try
char *buffor_ping_xml = new char[2000];
/* or */
wchar_t *buffor_ping_xml = new wchar_t[2000];
/* or */
wxChar *buffor_ping_xml = new wxchar[2000];
bool is_ping = DLL_PingConnection(buffor_ping_xml);
wxString mystring = wxString::FromUTF8(buffor_ping_xml);
write mystring to file.
To Do:
look in your wxwidgets\libs folder for your libs
are there libwxmsw29ud_* with a 'u' in the name (after version number here 29)?
If not You can not use unicode
If yes next steps
for all different test char *, wchar_t *, wxChar * give the files different name.
for example file_ping_xml.Open(wxT("C:\dll\ping_w_t_FromUTF8.xml"), ...
for wchar_t * in combination with
wxString mystring = wxString::FromUTF8(buffor_ping_xml);
also in combination with
wxString mystring(buffor_ping_xml);
Then check out the look like, of the files in a browser .
To test you can go to your wxWidgets sample folder . Compile in the folder C:\wxWidgets\samples\docview\docview.cpp . Open with docview.exe a unicode file . How does it look.
Unicode download file
Unicode-related compilation settings
You should define wxUSE_UNICODE to 1 to compile your program in Unicode mode. This currently works for wxMSW, wxGTK, wxMac and wxX11. If you compile your program in ANSI mode you can still define wxUSE_WCHAR_T to get some limited support for wchar_t type.
Here is answear: Getting trash data in char* while using it as buffer in function.
Thanks everyone - expecially for patience.
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 dont understand the mistake I am making.
I tryed alot but I am unable to read my FILE.
Basically I write an structure into a file named 0.txt / 1.txt / 2.txt ... based of account amound.
I realy seached hours to fix my problem but I dont understand how I can fix and why I get the ERROR.
Also I have no problem in complining my code (with dev c++) but when I press on Load Accounts Button I get the ERROR "Segmentation Fault" (using windows 7).
I noticed that the problem is at fread() line in function ladeAccounts().
The name of my Structure is "iAccount".
The variable infoma is as iAccount typed and the "number of accounts existing" typed as int anzahl in newAccount() decides the path.
iAccount looks like this:
struct iAccount
{
string ID;
string password;
int level;
};
This is how I write my STRUCT into the FILE:
void Account::newAccount(int anzahl, string username, string pw, int lvl)
{
iAccount neu;
neu.ID = username;
neu.password = pw;
neu.level = lvl;
ss.str("");
ss<<anzahl;
s = ss.str();
s = "Accounts/"+s+".txt";
f1 = fopen(s.c_str(), "w");
fseek(f1, 0, SEEK_SET);
fwrite(&infoma, sizeof(iAccount), 1, f1);
fclose(f1);
}
This is how I read the File (ERROR APPEARS when I call fread()
void Account::ladeAccount(int nummer)
{
stringstream sa;
iAccount account_geladen;
sa.str("");
sa<<nummer;
s = sa.str();
s = "Accounts/"+s+".txt";
f2 = fopen(s.c_str(), "r");
fseek(f2, 0, SEEK_SET);
fread(&infoma, sizeof(infoma), 1, f2);
fclose(f2);
}
Thank you for your help. I have no clue where my problem is and as I said I am searching for hours.
EDIT:
The file gets opened I tryed it (f2 is true!).
EDIT":
ERRNO = 0 !!!
SEE HERE:
ostringstream Str;
Str << errno;
infoma.ID = Str.str();
Just did this to see the result of errno in my wxtextlabel.
Reason
You are most probably calling fread on a NULL file handle. So you have two problems here:
In your code (you don't check if fread succeeds or returns a NULL value)
Your file can't be opened for some reason (this, you should investigate...)
Explication
fopen (see documentation) can return a NULL handle for different reasons. If you don't check the validity of the handle before calling fread you will have a segmentation fault.
Tips
As you can read in the official documentation I linked above, on most library implementations the errno variable can help you giving the system-specific error code on failure. This could help you debugging your error in opening the file.
Side Issues
Once you solve this bug in our code you will have other issues. As people (notably #Christophe) remarked in other answers, there is a structural problem in your code because you try to serialize/deserialize on your file objects non POD (aka your strings). Since string are complex objects you can't serialize them directly.
The approach of using an array of characters will work correctly, as simple types can be handled the way you coded.
For this reason, you can use the std::string c_str() method to obtain a null terminated array of chars from your string and store it in the file.
The opposite operation is even more straightforward, as you can initialize a std::string simply passing the deserialized array of chars:
std::string str(the_array);
You have a problem because you use fread() to load binary data. But this works only with plain old data (POD) objects.
It uses to give desastrous results with less trivial objects especially if the internals of these manage dynamic memory allocaton and/or pointers like it's the case here with strings.
By the way:
If you read/write binary data, you should really use "rb"/"wb" as mode for fopen(). If you don't you would'nt necessary have a seg.fault, but your data might be incorrect on some systems.
Edit:
Sorry, I didn't read well enough: if it happens right at fread() the reason provided by Alex will certainly help. However I leave this answer because as soon as you've solved your fopen() issue, you might get segmentation errors if you try to work with the object that you've read. If you're not conviced, look at sizeof(iAccount) and compare it to the size your string content.
EDIT
if(f2) is true so I am wrong and file got opened successfull right?
I found out that the file is not opened/the fopen can not handle with the path for example 0.txt .
Also I tryed to enter the path directly without building it (without stringstream and so on). Still I have the problem of the segmentation fault. I checked everything the file exists in the folder Accounts. I have an other file called "Accounts.txt" in the same folder and there I have no problem reading the amound of accounts existing (also using a struct). There I dont even check if the fopen had success but it works anyway I will write the code for the file-open-check later.
The code for the reading/writing into Accounts/Accounts.txt is:
struct init{
int anzahl_1;};
init anzahl;
FILE* f;
static string ss = "Accounts/Accounts.txt";
int account_anzahl1()
{
f = fopen(ss.c_str(), "r");
fread(&anzahl, sizeof(init), 1, f);
fseek(f, 0, SEEK_END);
fclose(f);
return anzahl.anzahl_1;
}
void account_anzahl_plus()
{
anzahl.anzahl_1 = anzahl.anzahl_1 +1;
f = fopen(ss.c_str(), "w");
fwrite(&anzahl, sizeof(init), 1, f);
fclose(f);
}
There I have no problem!
I'm loading a delphi dll in c++. When I use functions with char* as buffers (char* given as parameter to the procedure) I get only trash data.
When I have functions that return char* all is fine.
I'm new to c++ and I spend a lot of time trying to crack this. Please help.
Everything is explained in code below. I have put there 3 functions to show exacly what I mean.
Example function that has problem with buffer is:
DLL_PingConnection(var avXml:PChar):Boolean; - it returns true/false, as parameter it takes buffer and the function is done in buffer there should be valid xml (but there is only trash)
#include <windows.h> //this will load delphi dll
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string.h>
using namespace std;
// ------------------------------------------------ pointers on functions inside Delphi DLL (32 bits)
typedef bool(*TYPE_DLL_SetLicense)(char*, char*); //initialize dll stuff - I load licence from a file into char* - everything works fine
typedef bool(*TYPE_DLL_PingConnection)(char*); //the char* is buffer - I give empty char* as parameter and I should get correct xml with serwer data - I GET ONLY TRASH :(
typedef char*(*TYPE_DLL_ERR_DESCRIPTION)(void); //this function does not use buffer it returns char* - everything works fine
//so as you see problem is with buffers and function like this: DLL_PingConnection(buffer)
int main()
{
// ------------------------------------------------ Loading the library
HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\full_path\\SOMEDLL.dll");
//checking the library
if (hGetProcIDDLL == NULL) {std::cout << "Could NOT load the dynamic library" << std::endl;return EXIT_FAILURE;}
else{std::cout << "dynamic library loaded" << std::endl;}
// ------------------------------------------------ START: resolving functions adresses
TYPE_DLL_SetLicense DLL_SetLicense = (TYPE_DLL_SetLicense)GetProcAddress(hGetProcIDDLL, "DLL_SetLicense");
if (!DLL_SetLicense) {std::cout << "Could NOT locate the function: DLL_SetLicense" << std::endl;return EXIT_FAILURE;}
else{std::cout << "Function DLL_SetLicense located" << std::endl;}
TYPE_DLL_PingConnection DLL_PingConnection = (TYPE_DLL_PingConnection)GetProcAddress(hGetProcIDDLL, "DLL_PingConnection");
if (!DLL_PingConnection) {std::cout << "Could NOT locate the function: DLL_PingConnection" << std::endl;return EXIT_FAILURE;}
else{std::cout << "Function DLL_PingConnection located" << std::endl;}
TYPE_DLL_ERR_DESCRIPTION DLL_ERR_DESCRIPTION = (TYPE_DLL_ERR_DESCRIPTION)GetProcAddress(hGetProcIDDLL, "DLL_ERR_DESCRIPTION");
if (!DLL_ERR_DESCRIPTION) {std::cout << "Could NOT locate the function: DLL_ERR_DESCRIPTION" << std::endl;return EXIT_FAILURE;}
else{std::cout << "Function DLL_ERR_DESCRIPTION located" << std::endl;}
std::cout << "\n\nInitialization over. \n\n" << std::endl;
// ------------------------------------------------ START: calling functions from delphi dll
//DLL_SetLicence - this function take buffer as parameter, but dont return anything into the buffer. All works fine.
//start - we read licence from file
char buffer_licence[1242];
memset(buffer_licence,0,sizeof(buffer_licence));
//I read content of buffer_licence usinf ifstream from the file here (but I don't put the code, to keep sample minimal)
//we set licence with dll function
bool is_licence = DLL_SetLicense(buffer_licence,(char*)"");
//the output
if (is_licence == TRUE)
std::cout << "Licence has been set\n";
else
std::cout << "Licence has been NOT set\n";
//DLL_PingConnection - it takes empty buffer as parameter, it should save xml into buffer but it saves only trash.
//we try to save ping to the file - buffer
char buffor_ping_xml[2000];
memset(buffor_ping_xml,0,sizeof(buffor_ping_xml));
//this should gieve proper xml, but it returns only trash.... please help
bool is_ping = DLL_PingConnection(buffor_ping_xml);
if(is_ping)
{
std::cout << "DLL_PingConnection True\n"; //function returned true, so it worked correct.
std::cout << buffor_ping_xml; //but in the buffer is trash that I show on the screen. I also tried to put buffor_ping_xml info the file (diferent ways) but always result was trash just like on screen.
}
else
{
std::cout << "DLL_PingConnection False: \n";
}
//DLL_ERR_DESCRIPTION - if will automaticly return error description if there is any error to report. No buffer, no problems.
std::cout << buffor_ping_xml; //the data on screet is fine, so is in file and everywhere else.
return EXIT_SUCCESS;
}
PingConnection function will return only this instead of good xml.
EDIT:
Oroginally I used Netbeans + MinGW, but as suggested in comments I have used alternative compilers: Borland builder c++ 6.0, and Embarcadero RAD Studio XE3 (C++ Builder). The problems stayed the same even thou I used all calling convention types Remy Lebeau mentioned.
typedef bool(*TYPE_DLL_PingConnection)(char*); //standard calling convention default for compiler - returns trash
typedef bool(__cdecl *TYPE_DLL_PingConnection)(char*); //returns trash also
typedef bool(__stdcall *TYPE_DLL_PingConnection)(char*); //doesnt write anything to the buffer
typedef bool(__fastcall *TYPE_DLL_PingConnection)(char*); //returns trash
I have encountered small problem under c++ builder. I can't clean buffer under this enviroment:
memset(buffer,0,sizeof(buffer)); // will crash the program under c++ builder
Trying to use 'char *&' will crash the program also.
typedef bool(__cdecl *TYPE_DLL_PingConnection)(char*&);
OR
typedef bool(__stdcall *TYPE_DLL_PingConnection)(char*&);
OR
typedef bool(__fastcall *TYPE_DLL_PingConnection)(char*&);
char * buffer;
bool is_ping = DLL_PingConnection(buffer);
Using char ** will cause type mismatch with buffer.
EDIT2:
As requested by David Heffernan I attach sample of documentation. Important parts are trasnated to english. Rest is just structure of xlm that PIngConnection should return. Not much of help there - entire documentation is like this.
PS: I asked similar question here: Trash characters when using buffers in c++ - code based on WxWidgets (I though WxWidgets creates the problem, but it doesn't. Maybe someone will find WxWidgets code usefull thou).
EDIT 3:
I managed to get some more information about dll.
Delphi version is 7.
For sure calling type is stdcall. ( DLL_PingConnection: function(var avXml: PChar): Boolean; stdcall; )
This is how a function from this dll is called in delphi:
lPointer := nil; //pointer
lOSOZPointer := nil; //pointer
lpXML := nil; //pChar
lpXML:=StringToPChar(lXML);
lPointer := lpXML;
lWynik:=OSOZ_GetServerDataTime(lpXML);
if lWynik then
begin
lOSOZPointer := lpXML;
//akcja na wyniku
end;
if lPointer <> nil then begin
Freemem(lPointer);
end;
if lOSOZPointer <> nil then begin
OSOZ_FreeMem(lOSOZPointer);
end;
DLL_PingConnection(var avXml:PChar):Boolean;
This is not a full declaration. Obviously, it is a function since it has a Boolean return type. But does it also declare a calling convention as well - stdcall (__stdcall in C/C++) or cdecl (__cdecl in C/C++)? If not, then it is using Delphi's default register convention instead (which is __fastcall in Borland/CodeGear/Embarcadero C++ compilers only, but has no equivalent in any other C/C++ compiler). Your existing typedefs are using your C++ compiler's default calling convention, which is usually __cdecl. Calling convention mismatches are the most common problem with using DLLs, as it causes mismanagement of the call stack, which affects how parameters are passed, accessed, and cleaned up.
Also, what version of Delphi was the DLL written in? PChar is PAnsiChar (char* in C++) in Delphi 2007, but is PWideChar (wchar_t* in C++) in Delphi 2009 and later. Chances are, since the data is XML, then PAnsiChar/char* is likely being used.
Also, the PChar parameter is being passed as a var in the Delphi declaration, which is the same as a pointer in C and a reference in C++.
You need these important pieces of information in order to use this DLL function in C/C++ code. Unless the documentation explictly states these details, or the DLL has a C/C++ .h/.hpp file showing the actual declaration, then the best you can do is guess, and there are several variations possible given the incomplete declaration you have shown so far:
(char*& can be replaced with char** if needed):
typedef bool (__cdecl *TYPE_DLL_PingConnection)(char*&);
typedef bool (__stdcall *TYPE_DLL_PingConnection)(char*&);
typedef bool (__fastcall *TYPE_DLL_PingConnection)(char*&);
typedef bool (__cdecl *TYPE_DLL_PingConnection)(wchar_t*&);
typedef bool (__stdcall *TYPE_DLL_PingConnection)(wchar_t*&);
typedef bool (__fastcall *TYPE_DLL_PingConnection)(wchar_t*&);
If the DLL functions are using cdecl or stdcall, then you are OK, as most C/C++ compilers support those calling conventions. However, if the DLL functions are using register instead, and if you are not using a Borland/CodeGear/Embarcadero C++ compiler, then you are SOL. You would have to wrap the DLL inside another Delphi-written DLL that exports wrapper functions that use more portable signatures.
I am trying to make an audio player that plays .wav files. I wrote a function ReadWaveFile(CString szFilename) for reading the wave data of the file into the WAVEHDR structure. In this function
BOOL CWavePlay::ReadWaveFile(CString szFilename)
{
hmmio = mmioOpen((LPTSTR)&szFilename,NULL,MMIO_READ);
ASSERT(hmmio); //error here: hmmio=0x00000000
if(hmmio==0)
return FALSE;
....
}
mmioOpen is always returning 0 whenever I pass a filepath to this function for opening the specified file. And what baffles me is when i pass the filepath explicitly in mmioOpen API the code works; i.e., a valid handle is returned.
can some body explain why is this happening??
What will happen when you say
MessageBox(NULL,(LPTSTR)&szFilename,"Foo",MB_ICONINFORMATION);
When passing strings to system functions you will need to pick up the pointer to the raw string. For example, if you want to use an std::string object to build your path you will need to say
mmioOpen(filename.c_str(),NULL,MMIO_READ);
Your cast assumes from CString* to LPTSTR assumes that a CString is binary compatible with a LPTSRT which is not the case. When you write LPCTSTR on szFilename you will invoke a cast operator defined on CStrings that converts it to apropriate format. Did you tried just
hmmio = mmioOpen((LPCTSTR)szFilename,NULL,MMIO_READ);
The last cast does not do anything real here so it should be enough.
this question may seem a bit too specific, but I figure I'd give it a shot here since I've found some great programming answers here in the past.
I am modifying the open-source program TinyCad for a project I'm working on. I've visited the TinyCad message board and posted, but I didn't get the answer I'm looking for. I'm having trouble wrapping my head about how to integrate a small XML converter class I wrote into the loading function of TinyCad.
A little background about me: I have no experience with MFC or Visual Studio, but that is what I have to use. I am used to C++ and was taught using iostream syntax (cout, cin, new, etc.) so I'm not used to older C code (like printf, sprintf, malloc, alloc, etc.) either. I usually write my programs from start to finish in Qt, but I was told that for this project I should modify an existing program to save time. I don't know if it'll save that much time if I have to learn something totally foreign, but I digress.
I wrote a small class to read in an XML file that is structured differently than the XML file that TinyCad reads in. My class converts it and outputs an intermediate XML file. Well, I don't want to spit out an intermediate file. I modified it to save the output as a string (using the string datatype from the standard C++ iostream library). I want to get this string into a stream so that TinyCad can open the file, do the conversion, and then continue loading.
My class is called like so:
std::string blah;
char* filename = "library.xml";
XMLopen myXML(filename, blah);
So it takes in a filename, opens the file, parses the relevant information out of the file, puts the information into TinyCad's XML structure, and saves the XML code as a string that has been passed by reference.
I had an idea to use istringstream to make a stream, but that did not play nice with CFile. I tried it like so:
istringstream ins; // Declare an input string stream.
ins.str(blah);
// First open the stream to save to
CFile theFile(ins);
Below is the code in TinyCad that opens and loads the selected XML file:
void CLibraryStore::LoadXML( const TCHAR *filename )
{
// First open the stream to save to
CFile theFile;
// Open the file for saving as a CFile for a CArchive
BOOL r = theFile.Open(filename, CFile::modeRead);
if (r)
{
CString name;
// Create the XML stream writer
CStreamFile stream( &theFile, CArchive::load );
CXMLReader xml( &stream );
// Get the library tag
xml.nextTag( name );
if (name != "Library")
{
Message(IDS_ABORTVERSION,MB_ICONEXCLAMATION);
return;
}
xml.intoTag();
CTinyCadApp::SetLockOutSymbolRedraw( true );
while ( xml.nextTag( name ) )
{
// Is this a symbol?
if (name == "SYMBOL")
{
// Load in the details
xml.intoTag();
CTinyCadMultiSymbolDoc temp_doc;
drawingCollection drawing;
CLibraryStoreNameSet s;
// this is where the stream gets sent to be loaded into the data structure
s.LoadXML( &temp_doc, xml );
xml.outofTag();
// ... and store the symbol
Store( &s, temp_doc );
}
}
xml.outofTag();
CTinyCadApp::SetLockOutSymbolRedraw( false );
}
}
Edit 7/28/2010 5:55PM
So I tried to make a stream, but it fails.
CStreamFile takes in a filename and then gets set as a CArchive:
m_pArchive = new CArchive( theFile, nmode );
I tried to make a CStream like so (since CStreamFile is an overloaded CStream):
CString test = blah.c_str();
CStreamMemory streamCS;
streamCS << test;
CXMLReader xml( &streamCS );
But at streamCS << test; it doesn't put the stream in at all. test gets assigned correctly with blah so I know that's working.
Any ideas on how to approach this?