I have an interesting problem that seems to be unresolved by my research on the internet.
I'm trying to load libraries dynamically in my c++ project with the functions from dlfcn.h. The problem is that when I try to reload the plugins at running time (because I made a change on any of them), the main program crashes (Segmentation fault (core dumped)) when dlclose() is called.
Here is my example that reproduces the error:
main.cpp:
#include <iostream>
#include <dlfcn.h>
#include <time.h>
#include "IPlugin.h"
int main( )
{
void * lib_handle;
char * error;
while( true )
{
std::cout << "Updating the .so" << std::endl;
lib_handle = dlopen( "./test1.so", RTLD_LAZY );
if ( ! lib_handle )
{
std::cerr << dlerror( ) << std::endl;
return 1;
}
create_t fn_create = ( create_t ) dlsym( lib_handle, "create" );
if ( ( error = dlerror( ) ) != NULL )
{
std::cerr << error << std::endl;
return 1;
}
IPlugin * ik = fn_create( );
ik->exec( );
destroy_t fn_destroy = ( destroy_t ) dlsym( lib_handle, "destroy" );
fn_destroy( ik );
std::cout << "Waiting 5 seconds before unloading..." << std::endl;
sleep( 5 );
dlclose( lib_handle );
}
return 0;
}
IPlugin.h:
class IPlugin
{
public:
IPlugin( ) { }
virtual ~IPlugin( ) { }
virtual void exec( ) = 0;
};
typedef IPlugin * ( * create_t )( );
typedef void ( * destroy_t )( IPlugin * );
Test1.h:
#include <iostream>
#include "IPlugin.h"
class Test1 : public IPlugin
{
public:
Test1( );
virtual ~Test1( );
void exec( );
};
Test1.cpp:
#include "Test1.h"
Test1::Test1( ) { }
Test1::~Test1( ) { }
void Test1::exec( )
{
std::cout << "void Test1::exec( )" << std::endl;
}
extern "C"
IPlugin * create( )
{
return new Test1( );
}
extern "C"
void destroy( IPlugin * plugin )
{
if( plugin != NULL )
{
delete plugin;
}
}
To compile:
g++ main.cpp -o main -ldl
g++ -shared -fPIC Test1.cpp -o plugin/test1.so
The problem occurs when for example I change something on the Test1::exec method (changing the string to be printed or commenting the line) and while the main program sleeps I copy the new test1.so to main running directory (cp). If I use the move command (mv), no error occurs. What makes the difference between using cp or mv? Is there any way to solve this problem or to do that using cp?
I'm using Fedora 14 with g++ (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4).
Thanks in advance.
The difference between cp and mv that is pertinent to this question is as follows:
cp opens the destination file and writes the new contents into it. It therefore replaces the old contents with the new contents.
mv doesn't touch the contents of the original file. Instead, it makes the directory entry point to the new file.
This turns out to be important. While the application is running, the OS keeps open handles to the executable and the shared objects. When it needs to consult one of the these files, it uses the relevant handle to access the file's contents.
If you've used cp, the contents has now been corrupted, so anything can happen (a segfault is a pretty likely outcome).
If you've used mv, the open file handle still refers to the original file, which continues to exist on disk even though there's no longer a directory entry for it.
If you've used mv to replace the shared object, you should be able to dlclose the old one and dlopen the new one. However, this is not something that I've done or would recommend.
Try this:
extern "C"
void destroy( IPlugin * plugin )
{
if( plugin != NULL && dynamic_cast<Test1*>(plugin))
{
delete static_cast<Test1*>(plugin);
}
}
Related
The problem is:
I have a Qt application that dynamically loads a library and executes one function from this library (see code example)
...
QLibrary library( "d:/Libs/MyLib.dll" );
if ( !library.load( ) )
qDebug( ) << library.errorString( );
if ( library.load( ) )
qDebug( ) << "library loaded";
typedef void ( *StartPrototype ) ( );
StartPrototype Start = ( StartPrototype ) library.resolve( "Start" );
if ( !Start )
{
qDebug() << "Start( ) failed!";
}
Start();
...
And the defenition of Start( ) function froam loaded dll looks like:
extern "C" __declspec( dllexport ) void Start( )
{
ofstream myfile( "example.txt" );
if ( myfile.is_open( ) )
{
myfile << "This is a line.\n";
myfile.close( );
}
}
My Qt application is located at d:/Projects/MyApp.exe and when I run it, example.txt appears in d:/Projects/example.txt (as the current working directory for MyApp.exe)
But I want to save example.txt in the same directory where my dll is located (d:/Libs/example.txt).
Can I do that without specifying the path directly? I mean is there any way to specify the working directory for the loaded lib and all functions in it? How to set dll path as as a current working directory for dll functions (for example creating file). Thanks.
I'm trying to load up a Comma Separated file called POSDATA.GAMEDATA. I've looked up several places on the internet and it turns out I need to do some tweaking and / or a different class.
I tried using ifstream. However, it cannot open the file. Xcode 4.3.2 cannot seem to find my POSDATA.GAMEDATA file. I also tried to make the file using ofstream but when I use open() in both cases, the file is not opened.
My code is something like this:
using namespace std;
void FileLoader::loadFile( string p_WhichFile ) {
// Local Variables
string thisLine;
// Open POSDATA.GAMEDATA
ifstream dataStream;
dataStream.open( p_WhichFile.c_str( ) );
// Check if file is opened
if ( !dataStream ) {
cerr << "[ ERROR ] Cannot load file:" << p_WhichFile.c_str( ) << endl;
exit( 1 );
}
// Get lines of strings
while ( getline( dataStream, thisLine ) ) {
fileContents.push_back( thisLine ); // fileContents is a vector< string > object
}
dataStream.close( );
cout << "[ NOTICE ] Finished reading file" << p_WhichFile << endl;
}
I've seen CCFileUtils but I can't seem to get how to use it.
EDIT: I've tried supplying the absolute path ( /Users/LanceGray/Documents/LanceDev/COCOS2DX/cocos2dx/TestGame/Data/POSDATA.GAMEDATA ) and it worked. However, I cannot do this since the game is supposed to be used in iOS devices and Android, so the path is not always the same on each device. Any help will be grealy appreciated.
I got working by using CCFileUtils( )::sharedFileUtils( ) -> fullPathFromRelativePath( "POSDATA.GAMEDATA" );
A more detailed explanation:
Add the files that you need in the Project by going to the Project Tree on the left and Right-click -> Add Files. What I did was I added a new folder called Data on the same level as the Resources and Classes folders and placed my POSDATA.GAMEDATA file there. In Xcode, I added a new group and added that file in that group.
Then I used ifstream to open the file.
When opening the file, use CCFileUtils( )::sharedFileUtils( ) -> fullPathFromRelativePath( ) to get the absolute path of the file. Supply the file name on the fullPathFromRelativePath( ) as argument.
Try to run it and it should work fine.
A small example:
// FileReader.h
#include "cocos2d.h"
using namespace std;
using namespace cocos2d;
class FileReader {
private:
vector< string > mFileContents;
public:
FileReader( string pFileName, char pMode = 'r' );
};
// FileReader.cpp
#include "FileReader.h"
#include <fstream>
#include "cocos2d.h"
using namespace cocos2d;
using namespace std;
FileReader::FileReader( string pFileName, char pMode ) {
// Create input file stream
ifstream inputStream;
string thisLine;
// Open file
inputStream.open( CCFileUtils( )::sharedFileUtils( ) -> fullPathFromRelativePath( pFileName ).c_str( ) );
// Check if it is open
if ( !inputStream.is_open( ) ) {
cerr << "[ ERROR ] Cannot open file: " << pFileName.c_str( ) << endl;
exit( 1 );
}
while ( getline( inputStream, thisLine ) ) {
// Put all lines in vector
mFileContents.push_back( thisLine );
}
inputStream.close( );
cout << "[ NOTICE ] Finished opening file: " << pFileName.c_str( ) << endl;
}
This class will load a file with the name pFileName and place it on its member variable mFileContents. ( Note that it should have a public get function like vector< string > getFileContents( ) to access the mFileContents because it is private )
EDIT: The above sample will work on iOS, however, it won't on Android devices. So to fix this, instead of using ifstream, use CCFileUtils::sharedUtils( ) -> getFileData( ) instead. In conjunction with CCFileUtils::sharedUtils( ) -> fullPathFromRelativePath( ), we will be able to achieve our goal of reading a plain text file that works on both iOS and Android.
The FileReader class would then be like this:
// FileReader.cpp
#include "FileReader.h"
#include <fstream>
#include "cocos2d.h"
using namespace cocos2d;
using namespace std;
FileReader::FileReader( string pFileName, char pMode ) {
// Initialize variables needed
unsigned long fileSize = 0;
unsigned char * fileContents = NULL;
string thisLine, result, fullPath, contents;
// Get absolute path of file
fullPath = CCFileUtils::sharedFileUtils( ) -> fullPathFromRelativePath( pFileName.c_str( ) );
// Get data of file
fileContents = CCFileUtils::sharedFileUtils( ) -> getFileData( fullPath.c_str( ) , "r", &fileSize );
contents.append( ( char * ) fileContents );
// Create a string stream so that we can use getline( ) on it
istringstream fileStringStream( contents );
// Get file contents line by line
while ( getline( fileStringStream, thisLine ) ) {
// Put all lines in vector
mFileContents.push_back( thisLine );
}
// After this, mFileContents will have an extra entry and will have the value '\x04'.
// We should remove this by popping it out the vector.
mFileContents.pop_back( );
// Delete buffer created by fileContents. This part is required.
if ( fileContents ) {
delete[ ] fileContents;
fileContents = NULL;
}
// For testing purposes
cout << "[ NOTICE ] Finished opening file: " << pFileName.c_str( ) << endl;
}
// For versions less than v2.0.1
// The version I am using is 0.12.0
unsigned long fileSize = 0;
char* pBuffer = CCFileUltils::getFileData("relative_path","r",&fileSize);
CCLOG("Data is %s",pBuffer);
You can reference from Cocos's wiki
Read/write file in cocos2d
I'm just starting out with C++ and am learning how to use QT creator as the IDE so I suspect this might be really easy but I might be missing something. I installed apollo MQ and installed the apache CMS(C++ messaging service) so I can interact with the server in C++. The problem is, I have no clue what I'm doing :-) (yet), and am not sure why I'm getting the above error when using the examples.
I am pretty sure the examples are good because they are included in the source's example folder so it leads me to suspect that the configuration of the project is not correct(as its the only ting I really have an influence on).
Here's the .pro file:
TEMPLATE = app
CONFIG += console
CONFIG -= qt
SOURCES += main.cpp
INCLUDEPATH += /usr/local/include/activemq-cpp-3.4.2/
INCLUDEPATH += /opt/local/lib/
Here is the folder contents where I'm pointing to(the files I want are all within the subdirectories there):
drwxr-xr-x 13 root admin 442 May 11 19:09 activemq
drwxr-xr-x 48 root admin 1632 May 11 19:09 cms
drwxr-xr-x 9 root admin 306 May 11 19:09 decaf
Here's the error I get:
20:54:22: Running build steps for project stackexchangeexample...
20:54:22: Configuration unchanged, skipping qmake step.
20:54:22: Starting: "/usr/bin/make" -w
make: Entering directory `/Users/lostsoul/Dropbox/qt_cuda/stackexchangeexample-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug'
/Users/lostsoul/QtSDK/Desktop/Qt/4.8.1/gcc/bin/qmake -spec ../../../QtSDK/Desktop/Qt/4.8.1/gcc/mkspecs/macx-g++ CONFIG+=declarative_debug -o Makefile ../stackexchangeexample/stackexchangeexample.pro
make: Leaving directory `/Users/lostsoul/Dropbox/qt_cuda/stackexchangeexample-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug'
make: Entering directory `/Users/lostsoul/Dropbox/qt_cuda/stackexchangeexample-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug'
g++ -headerpad_max_install_names -arch x86_64 -Xarch_x86_64 -mmacosx-version-min=10.5 -o stackexchangeexample.app/Contents/MacOS/stackexchangeexample main.o
Undefined symbols for architecture x86_64:
"activemq::library::ActiveMQCPP::initializeLibrary()", referenced from:
_main in main.o
"activemq::library::ActiveMQCPP::shutdownLibrary()", referenced from:
_main in main.o
"activemq::core::ActiveMQConnectionFactory::ActiveMQConnectionFactory(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)", referenced from:
SimpleProducer::run() in main.o
"cms::CMSException::CMSException(cms::CMSException const&)", referenced from:
SimpleProducer::run() in main.o
"typeinfo for cms::CMSException", referenced from:
GCC_except_table15 in main.o
GCC_except_table16 in main.o
SimpleProducer::run() in main.o
"cms::CMSException::~CMSException()", referenced from:
SimpleProducer::run() in main.o
"decaf::lang::Thread::getId()", referenced from:
SimpleProducer::run() in main.o
"decaf::lang::Long::toString(long long)", referenced from:
SimpleProducer::run() in main.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
make: *** [stackexchangeexample.app/Contents/MacOS/stackexchangeexample] Error 1
make: Leaving directory `/Users/lostsoul/Dropbox/qt_cuda/stackexchangeexample-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug'
20:54:22: The process "/usr/bin/make" exited with code 2.
Error while building project stackexchangeexample (target: Desktop)
When executing build step 'Make'
and if it helps, here's the code I run(it may not be useful to anyone for helping because you need to install a bunch of stuff to get it work, but just in case it helps):
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <decaf/lang/Thread.h>
#include <decaf/lang/Runnable.h>
#include <decaf/util/concurrent/CountDownLatch.h>
#include <decaf/lang/Long.h>
#include <decaf/util/Date.h>
#include <activemq/core/ActiveMQConnectionFactory.h>
#include <activemq/util/Config.h>
#include <activemq/library/ActiveMQCPP.h>
#include <cms/Connection.h>
#include <cms/Session.h>
#include <cms/TextMessage.h>
#include <cms/BytesMessage.h>
#include <cms/MapMessage.h>
#include <cms/ExceptionListener.h>
#include <cms/MessageListener.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <memory>
using namespace activemq;
using namespace activemq::core;
using namespace decaf;
using namespace decaf::lang;
using namespace decaf::util;
using namespace decaf::util::concurrent;
using namespace cms;
using namespace std;
////////////////////////////////////////////////////////////////////////////////
class SimpleProducer : public Runnable {
private:
Connection* connection;
Session* session;
Destination* destination;
MessageProducer* producer;
bool useTopic;
bool clientAck;
unsigned int numMessages;
std::string brokerURI;
std::string destURI;
private:
SimpleProducer( const SimpleProducer& );
SimpleProducer& operator= ( const SimpleProducer& );
public:
SimpleProducer( const std::string& brokerURI, unsigned int numMessages,
const std::string& destURI, bool useTopic = false, bool clientAck = false ) :
connection(NULL),
session(NULL),
destination(NULL),
producer(NULL),
useTopic(useTopic),
clientAck(clientAck),
numMessages(numMessages),
brokerURI(brokerURI),
destURI(destURI) {
}
virtual ~SimpleProducer(){
cleanup();
}
void close() {
this->cleanup();
}
virtual void run() {
try {
// Create a ConnectionFactory
auto_ptr<ActiveMQConnectionFactory> connectionFactory(
new ActiveMQConnectionFactory( brokerURI ) );
// Create a Connection
try{
connection = connectionFactory->createConnection();
connection->start();
} catch( CMSException& e ) {
e.printStackTrace();
throw e;
}
// Create a Session
if( clientAck ) {
session = connection->createSession( Session::CLIENT_ACKNOWLEDGE );
} else {
session = connection->createSession( Session::AUTO_ACKNOWLEDGE );
}
// Create the destination (Topic or Queue)
if( useTopic ) {
destination = session->createTopic( destURI );
} else {
destination = session->createQueue( destURI );
}
// Create a MessageProducer from the Session to the Topic or Queue
producer = session->createProducer( destination );
producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );
// Create the Thread Id String
string threadIdStr = Long::toString( Thread::getId() );
// Create a messages
string text = (string)"Hello world! from thread " + threadIdStr;
for( unsigned int ix=0; ix<numMessages; ++ix ){
TextMessage* message = session->createTextMessage( text );
message->setIntProperty( "Integer", ix );
// Tell the producer to send the message
printf( "Sent message #%d from thread %s\n", ix+1, threadIdStr.c_str() );
producer->send( message );
delete message;
}
}catch ( CMSException& e ) {
e.printStackTrace();
}
}
private:
void cleanup(){
// Destroy resources.
try{
if( destination != NULL ) delete destination;
}catch ( CMSException& e ) { e.printStackTrace(); }
destination = NULL;
try{
if( producer != NULL ) delete producer;
}catch ( CMSException& e ) { e.printStackTrace(); }
producer = NULL;
// Close open resources.
try{
if( session != NULL ) session->close();
if( connection != NULL ) connection->close();
}catch ( CMSException& e ) { e.printStackTrace(); }
try{
if( session != NULL ) delete session;
}catch ( CMSException& e ) { e.printStackTrace(); }
session = NULL;
try{
if( connection != NULL ) delete connection;
}catch ( CMSException& e ) { e.printStackTrace(); }
connection = NULL;
}
};
////////////////////////////////////////////////////////////////////////////////
int main(int argc AMQCPP_UNUSED, char* argv[] AMQCPP_UNUSED) {
activemq::library::ActiveMQCPP::initializeLibrary();
std::cout << "=====================================================\n";
std::cout << "Starting the example:" << std::endl;
std::cout << "-----------------------------------------------------\n";
// Set the URI to point to the IPAddress of your broker.
// add any optional params to the url to enable things like
// tightMarshalling or tcp logging etc. See the CMS web site for
// a full list of configuration options.
//
// http://activemq.apache.org/cms/
//
// Wire Format Options:
// =====================
// Use either stomp or openwire, the default ports are different for each
//
// Examples:
// tcp://127.0.0.1:61616 default to openwire
// tcp://127.0.0.1:61616?wireFormat=openwire same as above
// tcp://127.0.0.1:61613?wireFormat=stomp use stomp instead
//
std::string brokerURI =
"failover://(tcp://127.0.0.1:61616"
// "?wireFormat=openwire"
// "&connection.useAsyncSend=true"
// "&transport.commandTracingEnabled=true"
// "&transport.tcpTracingEnabled=true"
// "&wireFormat.tightEncodingEnabled=true"
")";
//============================================================
// Total number of messages for this producer to send.
//============================================================
unsigned int numMessages = 2000;
//============================================================
// This is the Destination Name and URI options. Use this to
// customize where the Producer produces, to have the producer
// use a topic or queue set the 'useTopics' flag.
//============================================================
std::string destURI = "TEST.FOO";
//============================================================
// set to true to use topics instead of queues
// Note in the code above that this causes createTopic or
// createQueue to be used in the producer.
//============================================================
bool useTopics = false;
// Create the producer and run it.
SimpleProducer producer( brokerURI, numMessages, destURI, useTopics );
// Publish the given number of Messages
producer.run();
// Before exiting we ensure that all CMS resources are closed.
producer.close();
std::cout << "-----------------------------------------------------\n";
std::cout << "Finished with the example." << std::endl;
std::cout << "=====================================================\n";
activemq::library::ActiveMQCPP::shutdownLibrary();
}
I just created a empty c++ project, modified the .pro file to include a path to the libraries and then copy/pasted the code into main.cpp. Any idea what causes this? and how I can prevent it?
It looks like you've forgotten the library paths; you define INCLUDEPATH with new values but don't add a corresponding LIBPATH or LIBRARYPATH or whatever the equivalent variable for libraries is named. Won't you also need the corresponding -l library parameters for the specific libraries?
I am new to embedding python in C++ application. Please forgive me if this has been asked before, even though I did my homework by searching through the web.
So, here is my problem.
If I ran the embedded python code in the application's main thread, everything runs fine. But if I ran the embedded python code in the child thread ( created by main thread ), some of the python import will not work.
I made a small program here to demonstrate:
#include <iostream>
#include <boost/python.hpp>
#include <boost/thread.hpp>
using namespace boost::python;
int threadFunc ( ){
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
std::cout << "Py_GetProgramName: " << Py_GetProgramName() << std::endl;
std::cout << "Py_GetPath: " << Py_GetPath() << std::endl;
std::cout << "Py_GetExecPrefix: " << Py_GetExecPrefix() << std::endl;
object main_module = boost::python::import ( "__main__");
object main_namespace = main_module.attr("__dict__");
object objc_import = boost::python::import ( "objc" );
exec("a = 10\n", main_namespace);
exec("print a\n", main_namespace);
return 0;
}
int main( ){
Py_InitializeEx( 0 ); //
PyEval_InitThreads();
// Uncomment this will make the program work.
//object objc_import = boost::python::import ( "objc" );
PyEval_ReleaseLock();
boost::thread * theChildThread = new boost::thread (threadFunc);
while (1){
boost::this_thread::sleep( boost::posix_time::seconds(1) );
}
delete theChildThread;
Py_Finalize();
return 0;
}
The code fails when I tries to import objc in the threadFunc
object objc_import = boost::python::import ( "objc" );
By experiment different things, I find that if I do this in the main thread before starting child thread, the whole program would work ( see comments in the main function ):
//Uncomment this will make the program work.
//object objc_import = boost::python::import ( "objc" );
I am wondering what happened there. Is there anyway to without importing objc in main thread, but still make the child thread work?
Thanks,
I have an odd problem. When I try to compile the code below, it works without a failure as expected:
#include <iostream>
#include <Windows.h>
int main(){
FILE *f = fopen("trystl.geo","w");
fprintf(f,"Merge \"trystl.stl\";");
fprintf(f,"\n");
fprintf(f,"Surface Loop(2) = {1};");
fprintf(f,"\n");
fprintf(f,"Volume(3) = {2};");
fclose(f);
return 0;
}
But when I try to connect this program to a button with FLTK user interface, it gives me an assertion runtime error. The segment of my code:
void UserInterface::cb_m_BtnSTLToGEOConverter_i(Fl_Button*, void*){
//OnSTLToGEOConvert();
FILE *f = fopen("trystl.geo","w");
fprintf(f,"Merge \"trystl.stl\";");
fprintf(f,"\n");
fprintf(f,"Surface Loop(2) = {1};");
fprintf(f,"\n");
fprintf(f,"Volume(3) = {2};");
fclose(f);
}
void UserInterface::cb_m_BtnSTLToGEOConverter(Fl_Button* o, void* v){
((UserInterface*)(o->parent()->parent()->parent()->parent()->parent()->parent()->parent()->user_data()))->cb_m_BtnSTLToGEOConverter_i(o,v);
}
When the user presses the button, I want the program to create a file called trystl.geo and perform the operations shown. But when compile and open the program and click the button, it says:
Debug Assertion Failed!
Program: *.......\src\fprintf.c Line 55:
Expression: (str! NULL)
abort retry or ignore...
I'm using Visual Studio 2010.
The error is simple: Line 55 in fprintf.c in VC++ is _VALIDATE_RETURN( (str != NULL), EINVAL, -1); and str is the FILE* parameter (I've seen better named variables though).
For the curious (I was) _VALIDATE_RETURN is defined as follows:
#define _VALIDATE_RETURN( expr, errorcode, retexpr ) \
{ \
int _Expr_val=!!(expr); \
_ASSERT_EXPR( ( _Expr_val ), _CRT_WIDE(#expr) ); \
if ( !( _Expr_val ) ) \
{ \
errno = errorcode; \
_INVALID_PARAMETER(_CRT_WIDE(#expr) ); \
return ( retexpr ); \
} \
}
So better check if your fopen() call succeeds before trying to write to a nonexistant filedescriptor.
OK I found the solution. The only problem is that if you don't type in the whole path in the program, the file doesn't get opened. I replaced
FILE *f = fopen("trystl.geo","w");
with
FILE *f = fopen("c:/Users/anypath/trystl.geo","w");
it works!
Thank you for all your help!