Stumped on debugging LNK2019 and LNK2001 errors

I have two linking errors i cant figure out:
Error 1 error LNK2019: unresolved external symbol "void __cdecl AppendText(struct HWND__ * const &,wchar_t)" (?AppendText@@YAXABQAUHWND__@@_W@Z) referenced in function "public: void __thiscall ReadSitesCSV::ReadFile(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class WindSite *,struct HWND__ * const &)" (?ReadFile@ReadSitesCSV@@QAEXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@PAVWindSite@@ABQAUHWND__@@@Z) S:\Engineer\JWahl\MyProjects\Invenergy_Lightning_V03\Invenergy_Lightning_V03\ReadSitesCSV.obj Invenergy_Lightning_V03
Error 2 error LNK2001: unresolved external symbol "void __cdecl AppendText(struct HWND__ * const &,wchar_t)" (?AppendText@@YAXABQAUHWND__@@_W@Z) S:\Engineer\JWahl\MyProjects\Invenergy_Lightning_V03\Invenergy_Lightning_V03\ReadStrikesCSV.obj Invenergy_Lightning_V03
The relevant code is as follows:
AppendText.cpp
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
#pragma once
#include <windows.h>
#include <vector>
#include <tchar.h>
#include "WinMain.h"
#include <string>
#include "AppendText.h"
void AppendText( const HWND &hwnd, TCHAR *newText )
{
    // get edit control from dialog
    HWND hwndOutput = GetDlgItem( hwnd, 203); // #define IDC_EDIT_OUTPUT 203, need to get into the winMain.h file
    DWORD StartPos, EndPos;
    SendMessage( hwndOutput, EM_GETSEL, reinterpret_cast<WPARAM>(&StartPos), reinterpret_cast<WPARAM>(&EndPos) );
    int outLength = GetWindowTextLength( hwndOutput );
    SendMessage( hwndOutput, EM_SETSEL, outLength, outLength );
    SendMessage( hwndOutput, EM_REPLACESEL, TRUE, reinterpret_cast<LPARAM>(newText) );
    SendMessage( hwndOutput, EM_SETSEL, StartPos, EndPos );
}
TCHAR StringtoTCHAR(string in)
{	
	TCHAR *param=new TCHAR[in.size()+1];
	param[in.size()]=0;
	std::copy(in.begin(),in.end(),param);
	return *param;
}

ReadstrikesCSV.cpp
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#pragma once
#include <windows.h>
#include "ReadStrikesCSV.h"
#include "WindSite.h"
#include <string>
#include <iostream>
#include <sstream>
#include "AppendText.h"
#include <tchar.h>
using std::ios;
using std::string;
using std::endl;
#include <fstream>
using std::ifstream;
using std::ofstream;
#include <sstream>
#include <stdlib.h>
void ReadStrikesCSV::ReadFile(string file, WindSite sites[], const HWND &hwnd)
{
	string line;
	LatLong point;
	ifstream inFile(file, ios::in);
	bool fileexists = true;
	string TimeofStrike;
	// get edit control from dialog
	if (!inFile)
	{
		TCHAR temp = StringtoTCHAR("Strikes file does not exist");
		AppendText(hwnd, temp);
		fileexists = false;
	}
	std::string	tempstring[17];
	inFile.ignore('\n'); //ignore the first line as it is headers
	  while(std::getline(inFile,line))
    {
		std::stringstream  lineStream(line);
		std::string cell;
		int j = 0;
        while(std::getline(lineStream,cell,','))
        {
			tempstring[j] = cell;
			j++;
		}
		TimeofStrike = tempstring[0];
		point.SetLat((atof(tempstring[1].c_str())));
		point.SetLong((atof(tempstring[2].c_str())));
		CheckPointSites(sites, point, hwnd, TimeofStrike);
	}
	  if (!inFile && fileexists) //if file exists but there is no more data to be read.
	  {

		TCHAR temp = StringtoTCHAR("\r\nFinished reading strikes file.\r\n");
		AppendText(hwnd, temp);
	  }
}
void ReadStrikesCSV::CheckPointSites(WindSite sites[], LatLong point, const HWND &OUTPUT, string TimeofStrike) // check point against N,E,S,W before checking against turbines.  Pass array of WindSites to check
{
	//check point is inside box
	for (int j=0; j < 27; j++) //We have 27 sites, need to make that a variable, probably fixed when sites turned into vector.
	{
	if ((point.GetLat() >= ((sites[j].GetNorth()) - 0.01)) && (point.GetLat() <= ((sites[j].GetSouth()) + 0.01)) && (point.GetLong() >= (sites[j].GetWest() - 0.01) && (point.GetLong() <= (sites[j].GetEast() + 0.01))))
	{
		CheckPointWTGs(sites[j].GetFirstTurbine(), point, sites[j].getName(), OUTPUT, TimeofStrike);
	}
	}
}
void ReadStrikesCSV::CheckPointWTGs(Turbine *WTG, LatLong StrikePoint, string site, const HWND &OUTPUT , string TimeofStrike)//// check point if it is inside box for site, pass it the pointer for the first turbine of the site from WindSite to check.
{
	// read a point from strikes file.
	LatLong WTGpoint;
	bool run = true; //variable for the while loop,  set to true so that it runs at least once.
	ofstream outLog("Log.txt", ios::app);
	while (run == true)
	{
	WTGpoint = WTG->GetLatLong();
	if (WTGpoint.InRange(StrikePoint, 0.01))
	{
		string output;
		output.append(WTG->GetName()); //placed WTG name
		output.append(" at ");
		output.append(site); //placed site name
		output.append(" might have had a lightning strike at ");
		output.append(TimeofStrike); //placed time of lightning strike
		TCHAR temp = StringtoTCHAR(output);
		AppendText(OUTPUT, temp);
		outLog << site << " " << WTG->GetName() << " might have a lightning strike" << endl;	
	}	
		if (WTG->GetnextPtr() != NULL)//if more WTGs to check
			WTG = WTG->GetnextPtr(); //get next WTG
		else
			run = false;
	}
}

and ReadSitesCSV.cpp
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#pragma once
#include <Windows.h>
#include <iostream>
#include "ReadSitesCSV.h"
#include "WindSite.h"
#include "AppendText.h"
using std::ios;
using std::cin;
using std::endl;
#include <fstream>
using std::ifstream;
#include <string>
using std::string;
#include <sstream>
#include <stdlib.h>
#include <tchar.h>
void ReadSitesCSV::ReadFile(string tempfilename, WindSite site[],const HWND &output)
{
	string turbinename, line;
	ifstream inFile(tempfilename, ios::in);
	bool fileexists = true;
	if (!inFile)
	{
		TCHAR temp = StringtoTCHAR("Strikes file does not exist");
		AppendText(output, temp);
		fileexists = false;
	}
	// ignore the first TWO! lines as they are column headers. 
	int templine;
	for (templine = 0; templine <= 34; templine++)
	{
	inFile.ignore('\n');
	}
	int sitenum = 0;	//keep track of the number of sites.  
	std::string	temp[18]; 
    while(std::getline(inFile,line))
    {
		std::stringstream  lineStream(line);
		std::string cell;
		int j = 0;
        while(std::getline(lineStream,cell,','))
        {
			temp[j] = cell;
			j++;
		}
		if (sitenum < 25)
			if ((site[sitenum].getName() != "defualt") && (site[sitenum].getName() != temp[0])  )
			{
				sitenum++;
			}
			if (site[sitenum].getName() == "defualt") //if site has yet to be named name it and add first point
			{
				LatLong point; // create a new point to be filled
				site[sitenum].SetName(temp[0]);
				point.SetLat(atof(temp[12].c_str())); //atof is from char to double.  temp.c_str() is from string to char.
				point.SetLong(atof(temp[7].c_str()));
				site[sitenum].SetNorth(point.GetLat()); //set all boundry points equal to first point
				site[sitenum].SetSouth(point.GetLat());
				site[sitenum].SetWest(point.GetLong());
				site[sitenum].SetEast(point.GetLong());
			}
			if (site[sitenum].getName() == temp[0]) //if line belongs to site create and add the turbine to the site.
			{
			Turbine *tempturbine; //create pointer
			tempturbine = new Turbine; //creates memmory for object then returns pointer of memory setting tempturbine to memory address
			LatLong point; // create a new point to be filled
			tempturbine->SetName(temp[1]);
			point.SetLat(atof(temp[12].c_str())); //atof is from char to double.  temp.c_str() is from string to char.
			point.SetLong(atof(temp[7].c_str()));	
			if (point.GetLat() < site[sitenum].GetNorth()) //If current points Lat is less than Northern most point for box of site make new point lat point for North
				site[sitenum].SetNorth(point.GetLat());
			if (point.GetLat() > site[sitenum].GetSouth()) //If current points Lat is more than Southern most point for box of site make new point lat point for South
				site[sitenum].SetSouth(point.GetLat());
			if (point.GetLong() < site[sitenum].GetWest()) //If current points Long is less than Western most point for box of site make new point Long point for West
				site[sitenum].SetWest(point.GetLong());
			if (point.GetLong() > site[sitenum].GetEast()) //If current points Long is more than Eastern most point for box of site make new point long point for East
				site[sitenum].SetEast(point.GetLong());
			tempturbine->SetLatLong(point);
			site[sitenum].AddTurbine(tempturbine);
			}
	}
		

	if (!inFile && fileexists) //If the file exists but there is no more data.
	{
		TCHAR temp = StringtoTCHAR("\r\nDone Reading WTG Sites.\r\n");//for error checking, denotes finished reading in a site.  
		AppendText(output, temp);
	}
}

I know the code is a mess, i had a working MFC program that i am trying to make into a WinAPI program, so any other help would be gladly excepted.
Thanks for all your help.
You didn't post appendtext.h, but I suspect your declaration and your definitionof AppendText do not agree.

In appendtext.cpp line 8, your implementation is:
 
void AppendText( const HWND &hwnd, TCHAR *newText )


In ReadSitesCSV.cpp lines 24-25, you have the following:
1
2
    TCHAR temp = StringtoTCHAR("Strikes file does not exist");
    AppendText(output, temp);

Here, you're declaring a single TCHAR (temp) as assigning the result of StringtoTCHAR to it. In the second line, you're passing that single TCHAR to AppendText.

What I suspect you intended was for StringtoTCHAR to return TCHAR * which you allocated and then pass that array to AppendText.

The second linker error is complaining about the same thing in regard to ReadStringCSV.cpp lines 28-29.

Don't forget to deallocate the TCHAR * you allocated in StringtoTCHAR or you're going to have a memory leak.
Look at how you've defined the second argument to AppendText():

TCHAR *newText

Look at how you've defined the thing you're trying to pass as that second argument in, for example, ReadSitesCSV::ReadFile() :

TCHAR temp

See the difference?

EDIT: Ninja'd by AA.
Last edited on
Thanks for your replies, below is AppendText. h ran out of room on the first post to post it:

1
2
3
4
5
6
7
8
9
10
#pragma once

#include <windows.h>
#include "WinMain.h"
#include <vector>
#include <tchar.h>
#include <string>

void AppendText (const HWND &, TCHAR &);
TCHAR StringtoTCHAR(string);


I have added the & to see if that helps.

I understand about the passing of variables but I am sketchy on fixing it. How would the best way to doing what i want, i really just want to have an editbox declared in a different file (WinMain.cpp) display some text from this files functions.

Thanks again for your help/
I got it to compile!!!!

I change every TCHAR temp = StringtoTCHAR
to
TCHAR *temp = StringtoTCHAR

and

TCHAR StringtoTCHAR(string in)

to

TCHAR * StringtoTCHAR(string in)

with the return *param;

to

return param.

I also kept void AppendText( const HWND &hwnd, TCHAR *newText ) unchanged.

SO i was passing pointers and values all mixed up.

Thanks again.
Note: Opinion and arguably unhelpful suggestions below. Feel free to disregard:


I commend your effort on actually attempting to use TCHARs.... though you're probably making things much more difficult for yourself than they need to be... resulting in memory leaks and code that probably wouldn't even properly support non-ascii characters.

I've ranted about how retarded TCHARs are numerous times on this forum. If you're interested, here's a thread where I explain in detail why you should avoid using them:

http://www.cplusplus.com/forum/windows/105027/#msg566862
http://www.cplusplus.com/forum/windows/105027/#msg566904

Since you are working with std::string and are doing a per-byte straight copy into a TCHAR array... you will not be supporting Unicode. Therefore... if TCHARs are chars, you are wasting time... and if TCHARs are wchar_t, extended characters will be broken.


So since you're not supporting Unicode.. the simpler approach here would be to abandon TCHARs and just use the ANSI version of WinAPI functions instead of the TCHAR versions. This way you can get rid of the StringToTCHAR function (and the memory leaks it is causing by having that rogue new in there) and just use normal strings everywhere.


Or... if you want to support Unicode... then replace StringToTCHAR with StringToWString and have it return a wstring which widens the string data to UTF-16 (see MultiByteToWideChar). You can then pass your wstrings to any 'W' version of WinAPI functions (by using c_str()). This will let you get rid of the new and the memory leaks... and will actually properly support Unicode.
Topic archived. No new replies allowed.