Stop "Illegal function call" compiler error

Forum - I am trying to print the content(graphical image) in a pictureBox form on a different thread than the one which creates the image. The compiler throws the error below:

error C2352: 'System::Drawing::Graphics::DrawImage' : illegal call of non-static member function

all attempts to get rid of this error have failed. I very much need some help getting rid of this error.

Code is as follows (line 9 which throws error is marked with comment) -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
	delegate void InvokeDelegate();
	public ref class Form1 : public System::Windows::Forms::Form
	{
				<many lines of code>
	protected: public: void InvokeMethod()
			{
				Graphics::DrawImage(pictureBox1->Image,0,0);		// Line 9 error C2352
			}
				<many lines of code>
private: System::Void Run_btn_Click(System::Object^  sender, System::EventArgs^  e) {			 
			 Thread^ thread = gcnew Thread(gcnew ThreadStart(this, &Form1::DoLoop));
			 thread->IsBackground = true;
			 thread->Start();
} //End of Run_btn_Click
				<several Click_<event> handlers>
private: System::Void DoLoop()
		 {
				<this loop draws graph on pictureBox1 in separate thread>
		 } // End of DoLoop

private: System::Void printToolStripMenuItem_Click(System::Object^  sender, System::EventArgs^  e) {
			 PrintDocument^ printDocument1 = gcnew PrintDocument;
			 printDocument1->PrintPage += gcnew PrintPageEventHandler(this, &SimpleWinUSBDemo::Form1::printDocument1_PrintPage);
			 if(printDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK)
				{
					printDocument1->Print();
				}
		 } // End of printToolStripMenuItem_Click

private: System::Void printDocument1_PrintPage(System::Object^  sender, System::Drawing::Printing::PrintPageEventArgs^  e)
		 {
			 Form1 ^ MyForm1 = gcnew Form1();
			 InvokeDelegate ^ pID = gcnew InvokeDelegate(MyForm1, &Form1::InvokeMethod);
		 }; // End of _PrintPage

}; // End of class Form1 


Some help removing the error [with syntax example(s)] would be really appreciated; thanks for the time and assistance.

MickH 04 July 20 2:51 PM PDT
This link may help: https://stackoverflow.com/questions/15644235/non-static-link-on-the-member-error-c2352

Also MSDN is a great place to look for information on their error message codes.
jlb - thanks for the reference; I'll have to study it as I have no experience with some of the terminology mentioned therein. Let's see what I can make of it.

MickH 04 July 20 7:28 PM PDT USA
jlb - again, I appreciate the reference. Unfortunately, because of a combination of no experience with the terminology plus language problems, I simply do not understand what the reference is telling me. What I really need is some sample syntax of what to put in that cross-thread code call to print the image in the pictureBox. Thanks again for your help.

MickH 06 July 20 10:20 AM PDT USA
error C2352: 'System::Drawing::Graphics::DrawImage' : illegal call of non-static member function
Basically it means you try to call a method that doesn't exist.
The Graphics class doesn't have a static function DrawImage.
To use DrawImage you need to have an instance of the Graphics class.

Why do you insist on threads?
In a previous post you mentioned you got it working with the BackgroundWorker class.
Thomas1965 - thanks much for the reply.

To use DrawImage you need to have an instance of the Graphics class.


I've tried the code below to declare a new instance of the Graphics class:

1
2
3
4
5
	public: void InvokeMethod()
			{
				Graphics ^ graph = gcnew Graphics();
				graph->DrawImage(pictureBox1->Image,0,0);
			} // End of InvokeMethod 


The above only causes the compiler to throw a new error on the first line:

error C2512: 'System::Drawing::Graphics::Graphics' : no appropriate default constructor available

I don't have a clue as to why the compiler thinks I am trying to instantiate a method called "Graphics" in a class called "Graphics" (which is already in the code). So basically I don't know how to make a new instance of Graphics in order to call the non-static function DrawImage. A code example would be greatly appreciated.

More fundamentally, if the above code sample is not the correct way to print the image in the pictureBox in multi-threaded code I would surely like to know the correct way, again through example code.

Why do you insist on threads?


Pure curiosity. I just wanted/want to know what it would take to do it in basic multithreading.

Thanks again for your time and help.

MickH 07 July 20
The Graphics class doesn't have a default compiler so you can't create this way.
Think in this terms. The graphics class need to know where you want to draw. You can't draw just in space.

Normally you get an Graphics object from the PaintEventHandler or you let the control you want to paint on it create it. For example if you want to paint on a button you ask the button to give you a graphics object with CreateGraphics()

Another way to get one is from the Image or Bitmap class.

Thomas1965 - again thanks for the reply and advice.

The Graphics class doesn't have a default compiler so you can't create this way.


I don't undertand the difference between creating the instance of Graphics in the DoLoop, which works fine, and in
the InvokeMethod call, which doesn't. I'll have to think about this at length.

The graphics class need to know where you want to draw. You can't draw just in space.


I think I understand this point and am also pondering what to do here.

Normally you get an Graphics object from the PaintEventHandler or you let the control you want to paint on it create it.


I'll also have to think about this at great length; I don't see as of now how the PaintEvent fits into what I'm doing here.

For example if you want to paint on a button you ask the button to give you a graphics object with CreateGraphics().


CreateGraphics() is how I'm drawing the graph in the pictureBox in the DoLoop, which works:

1
2
3
Graphics^ g = pictureBox1->CreateGraphics();
<several lines of code>
g->DrawLine(blackPen, Point(J,(400*(1-NUMF/FSV))), Point(J+1,(400*(1-NUMF/FSV))));


Another way to get one is from the Image or Bitmap class.


Here's the original form of the PrintPage call. This compiles but throws an "Unhandled exception" error at runtime. The error "Details" box says "image cannot be null" which I believe is saying the "Image" variable has not retrieved the graph from the pictureBox and is empty:

1
2
3
4
private: System::Void printDocument1_PrintPage(System::Object^  sender, System::Drawing::Printing::PrintPageEventArgs^  e)
		 {
			 e->Graphics->DrawImage(pictureBox1->Image,0,0);
		 } // End of _PrintPage 


This is my attempt to make a cross-thread call to allow "image" on the main thread to contain the graph; it throws the non-static versus static error and doesn't work:

1
2
3
4
5
	public: void InvokeMethod()
			{
				Graphics ^ graph = gcnew Graphics();
				graph->DrawImage(pictureBox1->Image,0,0);
			}


All of this will require a great deal of thought. A little example code would be tremendously helpful.

MickH 8 July 20 6:19 PM PDT USA
MickH, it sounds like you are trying to use GDI Plus. Well, there is a bit of setup that you need to do. First, add these two items to your header definitions:

1
2
#include <gdiplus.h>
#pragma comment (lib,"Gdiplus.lib") 


Next, add this to your WinMain() variable declarations:

 
ULONG_PTR   gdiplusToken;


Add these two lines of code to your WinMain() function, just after your variable declarations:

1
2
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);


Add this line of code at the bottom of WinMain(), just ahead of your return statement:

 
Gdiplus::GdiplusShutdown(gdiplusToken);


This turns on GDI Plus at the start of you program, and shuts it off at the end. "Token" is just another form of a handle.

In your functions or classes, declare your objects, a pointer to your image, and a graphics object. "image1" and "graphic1" are just sample names. You can use whatever names you choose:

1
2
static Gdiplus::Image *image1=nullptr;
Gdiplus::Graphics graphic1(hdc);


Load your image from HDD or other drive:

 
image1 = Gdiplus::Image::FromFile(L"<file path>/<file name>");


Finally, draw your image:

 
graphic1.DrawImage(image1, xImgOrg, yImgOrg, cxSize, cySize);


The image origin is always the upper-left corner. You may need to use brackets "{}" in your "case" statements, if the function gives you an error.

I hope this was of help.
Last edited on
anachronon - thanks much for the assistance. I was/am not consciously trying to use the GDI+ API. I just assumed that I needed to make some data available cross-thread, which I don't know how to do and am looking for some syntax examples of how to do that. But I will try your suggestions ASAP and report on the result. May take a few days.

MickH 16 July 20 6:27 PM PDT USA
Anachronon - sorry for the delayed response; I've been off on other problems.

Now that I've been able to study a bit your reply of 16 July 20 I think there's a fundamental misconception here. The image I want is already drawn in the pictureBox; I just need to print it, and the "illegal function call" error from the compiler is only in the code sections concerned with printing. The printing technique I am trying involves a new instance of Graphics which seems to require the image to be redrawn on that new instance but the image itself already exists in the pictureBox. There is no image file on another HDD or in other storage.

The code for this app has two threads: the principle thread (called Form1.h) sets up the GUI and contains all of the control click event handlers. A Run button click handler launches a second thread (called DoLoop) which draws a graphic image in a pictureBox control using System::Drawing::Graphics::DrawLine method. So the graphic image is drawn into the pictureBox on the second thread but the command to print it is on the first thread.

I am assuming that the print routines on thread 1 can't access the image which is drawn on thread 2 (cross-thread data problem). I have tried several approaches for cross-thread transfer involving Delegates and Invokes, etc., with total lack of success; none of my attempts will even compile. The example I posted is an attempt to adapt code to the two-thread case that works fine in a single-thread implementation. What I really need it some example code on how to access that image from thread 1 and print it.

Thanks for any help you might be able to offer on this problem.

Mick H 21 July 20 10:58 AM PDT USA
Topic archived. No new replies allowed.