locked
Calling C++ from C -- LNK2019 error RRS feed

  • Question

  • I have some code original written in C.  I'm implementing in on mutiple platforms so I'll coding the user interface in various languages while keeping the bulk of the logic in C. For Windows, I'm using VC++ for the GUI form.

    After jumping through all sorts of hurdles I finally got down to this link error.

    error LNK2019: unresolved external symbol _WriteOutput referenced in function _write_stats

    In the C file, which is "No Common Language Runtime Support" I have the following code snippets:

    . . .
    
    void WriteOutput( char * );
    
    . . .
    
    void write_stats( void )
    {
      char str[1000] = "\n";
    
    . . .
    
      WriteOutput( str );
    }
    
    
    

    In the C++ file, which is compiled with "/clr", I have:

    void WriteOuput( char *str )
     {
        String^ newstr = Marshal::PtrToStringAnsi(static_cast<IntPtr>(str));
    
        GAS2::Form1::WriteLabel( newstr );
    }
    

    I tried defining a namespace and class around the function, it didn't work. The C module didn't even recognize the keword namespace.

    I tried using extern "C".

    I tried changing the file extension of the C module to cpp. I got some syntax errors (didn't know VC++ was extension-sensitive). After I I got it to compile, I got the same error LNK2091 except that , in the error message, "_WriteOutput" became "void __cdecl WriteOutput(char *)".

    How could I make the C module see the function WriteOutput()? Should I change the extension of the C module to "cpp"?

    Thank you.

    Tuesday, October 11, 2011 5:26 AM

Answers

  • Have you tried the following declaration in CPP file?

     

    extern "C" void WriteOutput(char * msg);

    void WriteOutput(char * msg)

    {

        String ^ newstr = gcnew String(msg);

        GAS2::Form1::WriteLabel( newstr );

    }

     

    Put it outside namespaces.

     

    The above approach works in Visual Studio 2010 with default project settings, but with ‘/clr’ instead of ‘/crl:pure’, and with a C file without ‘/clr’ and precompiled headers.

     

    • Marked as answer by Rob Pan Monday, October 24, 2011 8:02 AM
    Tuesday, October 11, 2011 6:15 AM
  • public: static void WriteLabel( String ^ str )
    {
           System::Windows::Forms::Label^ lblOut;
           lblOut->Text = "Testing"; // str; <--- Exception!
    }

    You are declaring a local variable lblOut, which is not initialized. When you dereference it with ->, an exception is thrown, this is normal since lblOut is used without being initialized.

    Declaring a local variable is certainly not what you want to do, because the local variable override the class member of the same name. So you should remove lblOut declaration in WriteLabel.

     

    • Marked as answer by Rob Pan Monday, October 24, 2011 8:02 AM
    Wednesday, October 12, 2011 8:57 PM
  • I think I see what the problem is. I removed the declaration and changed WriteLabel() to non-static. The function WriteOutput(), which is where WriteLabel() is called, needs to call an instantiation of Form1, but I couldn't form the object.

    When I declared

    GAS2::Form1 myForm;

    in WriteOuput(), it accesses a different instance of Form1 and nothing is displayed.

    Do you know where I could find the correct instance?

    Thank you.

    • Marked as answer by Rob Pan Monday, October 24, 2011 8:02 AM
    Thursday, October 13, 2011 5:26 AM
  • Hi,

     

    According to the error message,  you cannot use gcnew to create the form1. So I suggest you can use this statement to avoid your issue:

     

    Form1^ GASForm = gcnew Form1();

     

    Best Regards,

    Rob

     


    Rob Pan [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Rob Pan Monday, October 24, 2011 8:02 AM
    Tuesday, October 18, 2011 8:45 AM

All replies

  • Have you tried the following declaration in CPP file?

     

    extern "C" void WriteOutput(char * msg);

    void WriteOutput(char * msg)

    {

        String ^ newstr = gcnew String(msg);

        GAS2::Form1::WriteLabel( newstr );

    }

     

    Put it outside namespaces.

     

    The above approach works in Visual Studio 2010 with default project settings, but with ‘/clr’ instead of ‘/crl:pure’, and with a C file without ‘/clr’ and precompiled headers.

     

    • Marked as answer by Rob Pan Monday, October 24, 2011 8:02 AM
    Tuesday, October 11, 2011 6:15 AM
  • Thank you. It linked with the changes you suggested.

    The next problem is that I don't think it the C function gamain() (in a .c file) is called, or at least the call to WriteOutput() does not work. Is the call from the Form correct? 

    private: System::Void btnRun_Click(System::Object^  sender, System::EventArgs^  e) {
    
        lblOut->Text = "Processing";
       <strong> __declspec(dllexport)void __cdecl gamain();
    </strong>//  lblOut->Text = "Completed";
    
    }
    
    

    "Processing" is displayed when the button is pressed, but not texts passed by the call to WriteOutput().

    Thanks.

    Tuesday, October 11, 2011 2:04 PM
  • In order to call the function, also write “gamain();”. It is not clear what is the purpose of __declspec(dllexport) in this example.

    Tuesday, October 11, 2011 2:17 PM
  • Be aware of the difference between the declaration of a function:

    extern "C" _declspec(dllexport) void __cdecl myfunc();
    

    and its definition:

    void __cdecl myfunc()
    {
        ...
    }
    
    

    See http://msdn.microsoft.com/fr-fr/library/sc8yf29y.aspx


    Tuesday, October 11, 2011 9:47 PM
  • Thanks all. I finally got it linked and call gamain, but got an exception in WriteLabel:

    An unhandled exception of type 'System.NullReferenceException' occurred in GAS2.exe

    Additional information: Object reference not set to an instance of an object.

    public: static void WriteLabel( String ^ str ) {
        System::Windows::Forms::Label^   lblOut;
        <strong>lblOut->Text = "Testing";   // str;        <--- Exception!
    </strong>}
    private: System::Void btnRun_Click(System::Object^  sender, System::EventArgs^  e) {
        lblOut->Text = "Processing";
        gastring();
        // lblOut->Text = "Completed";
    }
    private: System::Void btnExit_Click(System::Object^  sender, System::EventArgs^  e) {
        Application::Exit();
    }
    
    

    First I don't understand why I had to decalre lblOut in the function but not in the following functions. Then why do I get an excption in "lblOut->Text = "Testing" while it was fine in the following function.

    A full listing of Form1.h:

    #pragma once
    #include "stdafx.h"
    
    extern "C" __declspec(dllexport)void __cdecl gastring();
    
    namespace GAS2 {
    
    	using namespace System;
    	using namespace System::ComponentModel;
    	using namespace System::Collections;
    	using namespace System::Windows::Forms;
    	using namespace System::Data;
    	using namespace System::Drawing;
    
    	/// <summary>
    	/// Summary for Form1
    	/// </summary>
    	public ref class Form1 : public System::Windows::Forms::Form
    	{
    	public:
    		Form1(void)
    		{
    			InitializeComponent();
    			//
    			//TODO: Add the constructor code here
    			//
    		}
    
    	protected:
    		/// <summary>
    		/// Clean up any resources being used.
    		/// </summary>
    		~Form1()
    		{
    			if (components)
    			{
    				delete components;
    			}
    		}
    
    	private: System::Windows::Forms::Button^  btnRun;
    	protected: 
    	private: System::Windows::Forms::Button^  btnExit;
    	private: System::Windows::Forms::Label^   lblOut;
    
    
    	private:
    		/// <summary>
    		/// Required designer variable.
    		/// </summary>
    		System::ComponentModel::Container ^components;
    
    #pragma region Windows Form Designer generated code
    		/// <summary>
    		/// Required method for Designer support - do not modify
    		/// the contents of this method with the code editor.
    		/// </summary>
    		void InitializeComponent(void)
    		{
    			this->btnRun = (gcnew System::Windows::Forms::Button());
    			this->btnExit = (gcnew System::Windows::Forms::Button());
    			this->lblOut = (gcnew System::Windows::Forms::Label());
    			this->SuspendLayout();
    			// 
    			// btnRun
    			// 
    			this->btnRun->Location = System::Drawing::Point(482, 380);
    			this->btnRun->Name = L"btnRun";
    			this->btnRun->Size = System::Drawing::Size(75, 23);
    			this->btnRun->TabIndex = 0;
    			this->btnRun->Text = L"&Run";
    			this->btnRun->UseVisualStyleBackColor = true;
    			this->btnRun->Click += gcnew System::EventHandler(this, &Form1::btnRun_Click);
    			// 
    			// btnExit
    			// 
    			this->btnExit->Location = System::Drawing::Point(594, 380);
    			this->btnExit->Name = L"btnExit";
    			this->btnExit->Size = System::Drawing::Size(75, 23);
    			this->btnExit->TabIndex = 1;
    			this->btnExit->Text = L"E&xit";
    			this->btnExit->UseVisualStyleBackColor = true;
    			this->btnExit->Click += gcnew System::EventHandler(this, &Form1::btnExit_Click);
    			// 
    			// lblOut
    			// 
    			this->lblOut->BackColor = System::Drawing::SystemColors::ControlLightLight;
    			this->lblOut->BorderStyle = System::Windows::Forms::BorderStyle::Fixed3D;
    			this->lblOut->Location = System::Drawing::Point(27, 9);
    			this->lblOut->Name = L"lblOut";
    			this->lblOut->Size = System::Drawing::Size(512, 256);
    			this->lblOut->TabIndex = 2;
    			this->lblOut->Text = L"Output";
    			// 
    			// Form1
    			// 
    			this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
    			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
    			this->ClientSize = System::Drawing::Size(728, 415);
    			this->Controls->Add(this->lblOut);
    			this->Controls->Add(this->btnExit);
    			this->Controls->Add(this->btnRun);
    			this->Name = L"Form1";
    			this->Text = L"Genetic Algorithm String";
    			this->ResumeLayout(false);
    
    		}
    #pragma endregion
    	public: static void WriteLabel( String ^ str ) {
    			System::Windows::Forms::Label^   lblOut;
    			lblOut->Text = "Testing";    // str;
    		}
    
    	private: System::Void btnRun_Click(System::Object^  sender, System::EventArgs^  e) {
    				 lblOut->Text = "Processing";
    				 gastring();
    				 // lblOut->Text = "Completed";
    			 }
    	private: System::Void btnExit_Click(System::Object^  sender, System::EventArgs^  e) {
    				 Application::Exit();
    			 }
    	};
    }
    

    Thanks.

    Wednesday, October 12, 2011 5:51 AM
  • public: static void WriteLabel( String ^ str )
    {
           System::Windows::Forms::Label^ lblOut;
           lblOut->Text = "Testing"; // str; <--- Exception!
    }

    You are declaring a local variable lblOut, which is not initialized. When you dereference it with ->, an exception is thrown, this is normal since lblOut is used without being initialized.

    Declaring a local variable is certainly not what you want to do, because the local variable override the class member of the same name. So you should remove lblOut declaration in WriteLabel.

     

    • Marked as answer by Rob Pan Monday, October 24, 2011 8:02 AM
    Wednesday, October 12, 2011 8:57 PM
  • I think I see what the problem is. I removed the declaration and changed WriteLabel() to non-static. The function WriteOutput(), which is where WriteLabel() is called, needs to call an instantiation of Form1, but I couldn't form the object.

    When I declared

    GAS2::Form1 myForm;

    in WriteOuput(), it accesses a different instance of Form1 and nothing is displayed.

    Do you know where I could find the correct instance?

    Thank you.

    • Marked as answer by Rob Pan Monday, October 24, 2011 8:02 AM
    Thursday, October 13, 2011 5:26 AM
  • Looks like I have to instantiate or define inside  a function.

     

    Form1 GASForm = gcnew Form1();

    error C3145: 'GASForm' : global or static variable may not have managed type 'GAS2::Form1' may not declare a global or static variable, or a member of a native type that refers to objects in the gc heap

     

     

     

     

    Thursday, October 13, 2011 2:01 PM
  • Hi,

     

    According to the error message,  you cannot use gcnew to create the form1. So I suggest you can use this statement to avoid your issue:

     

    Form1^ GASForm = gcnew Form1();

     

    Best Regards,

    Rob

     


    Rob Pan [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Rob Pan Monday, October 24, 2011 8:02 AM
    Tuesday, October 18, 2011 8:45 AM