Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Question
Monday, November 6, 2017 9:24 PM
What I'm doing is trying to get WTSEnumerateSessions called and I keep getting "[it] was not declared in this scope" in my IDE:
#include <windows.h>
#include <wtsapi32.h>
#include <userenv.h>
typedef DWORD(WINAPI *WTS_SESSION_INFO)(void);
typedef WTS_SESSION_INFO(WINAPI *WTSENUMERATESESSIONSW)(void);
HINSTANCE loaddll = LoadLibrary(TEXT("wtsapi32.dll"));
DWORD GetID() {
int FindSessions = (INT) GetProcAddress(loaddll, "WTSEnumerateSessionsW");
if (FindSessions) {
WTS_SESSION_INFO pSessionInfo = 0;
DWORD dwCount = 0;
WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount);
// I get "'WTSEnumerateSessionsW' was not declared in this scope"
// I also get "'WTS_CURRENT_SERVER_HANDLE' was not declared in this scope...I've resorted to putting a 0 there, as I found on some site
}
}
Follwing MSDN, I can't find a straightforward way to get these WTS functions working. I had a heck of a time with WTSGetActiveConsoleSessionId working. Is there a C-based book or other resource that walks you through the Remote Desktop API stuff?
All replies (11)
Sunday, November 12, 2017 11:54 PM âś…Answered
typedef DWORD(WINAPI *WTS_SESSION_INFO)(void); typedef WTS_SESSION_INFO(WINAPI *WTSENUMERATESESSIONSW)(void);
I doubt that these two typedefs could be used to produced anything meaningful. The function pointer which appears to be for WTSEnumerateSessions fails to include a proper parameter list. And WTS_SESSION_INFO is a struct containing data. Therefore I cannot see the purpose of the function pointer that bears that name.
The typedefs contained in the sample code I provided are properly constructed to declare function pointers for the WTSEnumerateSessions and WTSFreeMemory functions. It should not be necessary to alter the code I've provided if your dev environment is using a genuine Windows SDK to retrieve the required headers.
If you cannot build the sample code I provided using Code::Blocks I'll guess it's because there is something wrong with the header files that Code::Blocks is using.
Monday, November 6, 2017 9:48 PM
And then I'm also unsure of how to handle the data that is returned. I would loop through pSessionInfo somehow?
Wednesday, November 8, 2017 6:17 PM
WTS_CURRENT_SERVER_HANDLE was already defined in <wtsapi32.h> of Platform SDK August 2001. Perhaps you have a rogue copy of wtsapi32.h in your include path. Use the /showIncludes compiler option to check which wtsapi32.h file is being used. And delete all #define _INC_WTSAPI directives from your code.
WTSEnumerateSessionsW likewise exists in Windows 2000, so I recommend you delete the LoadLibrary and GetProcAddress calls and just add wtsapi32.lib to the linker inputs.
Wednesday, November 8, 2017 7:23 PM
While I agree with ranta about linking with the wtsapi32.lib import library, the following demonstrates how to use the functions with LoadLibrary/GetProcAddress -
#include <stdio.h>
#include <tchar.h>
#include <string>
#include <Windows.h>
#include <WtsApi32.h>
typedef BOOL(WINAPI *pfWTSENUMERATESESSIONS)(HANDLE hServer, DWORD Reserved, DWORD Version, PWTS_SESSION_INFO *ppSessionInfo, DWORD *pCount);
typedef void(WINAPI *pfWTSFREEMEMORY)(PVOID pMemory);
#ifdef _UNICODE
#define WTSENUMERATESESSIONS "WTSEnumerateSessionsW"
#define tstring std::wstring
#else
#define tstring std::string
#define WTSENUMERATESESSIONS "WTSEnumerateSessionsA"
#endif
int _tmain()
{
HMODULE hMod = LoadLibrary(_T("wtsapi32.dll"));
if (hMod)
{
pfWTSENUMERATESESSIONS wtsEnum = (pfWTSENUMERATESESSIONS)GetProcAddress(hMod, WTSENUMERATESESSIONS);
pfWTSFREEMEMORY wtsFree = (pfWTSFREEMEMORY)GetProcAddress(hMod, "WTSFreeMemory");
if (wtsEnum && wtsFree)
{
PWTS_SESSION_INFO pwsi = nullptr;
DWORD dwCount = 0;
if (wtsEnum(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pwsi, &dwCount))
{
for (DWORD i = 0; i < dwCount; i++)
{
tstring str;
switch (pwsi[i].State)
{
case WTSActive:
str = _T("Active");
break;
case WTSConnected:
str = _T("Connected");
break;
case WTSConnectQuery:
str = _T("ConnectQuery");
break;
case WTSShadow:
str = _T("Shadow");
break;
case WTSDisconnected:
str = _T("Disconnected");
break;
case WTSIdle:
str = _T("Idle");
break;
case WTSListen:
str = _T("Listen");
break;
case WTSReset:
str = _T("Reset");
break;
case WTSDown:
str = _T("Down");
break;
case WTSInit:
str = _T("Init");
break;
default:
str = _T("Unknown");
}
_tprintf_s(_T("Winstation Name : %s, Session Id : %d, Session State : %s\n"), pwsi[i].pWinStationName, pwsi[i].SessionId, str.c_str());
}
wtsFree(pwsi);
}
}
FreeLibrary(hMod);
}
return 0;
}
Saturday, November 11, 2017 8:04 PM
I'm still struggling with this: I have a brand new project set up with only this code. I'm getting an error that WTS_SESSION_INFO has not been declared, though I see it set up in wtsapi32.h and I even pasted all of those declarations straight into my code in case that wasn't working properly for some strange reason. Then PTWS_SESSION_INFO wasn't declared "in this scope." All the connectstate_class(es) weren't declared "in this scope" either. "pwsi" wasn't declared "in this scope" either, even though it clearly was a couple lines ago.
I don't know what is goofed up but it's causing a big hindrance. I'm fairly new to win32 and C programming, as well as with my IDE Code::Blocks, so maybe I'm missing something stupid. Is there some output or anything I can export to help with troubleshooting?
Saturday, November 11, 2017 8:07 PM
I just noticed that you are using Code::Blocks. My sample code was created with Visual Studio 2015. FYI, Visual Studio 2017 Community can be downloaded from Microsoft at no cost for individual use.
Also, the sample code needs to be compiled with a C++ compiler. I'll post a revised sample that can be compiled with a C compiler in a little while.
Update - Revised sample C code (Builds successfully as either Unicode or MBCS on VS2015)
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <WtsApi32.h>
typedef BOOL(WINAPI *pfWTSENUMERATESESSIONS)(HANDLE hServer, DWORD Reserved, DWORD Version, PWTS_SESSION_INFO *ppSessionInfo, DWORD *pCount);
typedef void(WINAPI *pfWTSFREEMEMORY)(PVOID pMemory);
#ifdef _UNICODE
#define WTSENUMERATESESSIONS "WTSEnumerateSessionsW"
#else
#define WTSENUMERATESESSIONS "WTSEnumerateSessionsA"
#endif
int _tmain()
{
HMODULE hMod = LoadLibrary(_T("wtsapi32.dll"));
if (hMod)
{
pfWTSENUMERATESESSIONS wtsEnum = (pfWTSENUMERATESESSIONS)GetProcAddress(hMod, WTSENUMERATESESSIONS);
pfWTSFREEMEMORY wtsFree = (pfWTSFREEMEMORY)GetProcAddress(hMod, "WTSFreeMemory");
if (wtsEnum && wtsFree)
{
PWTS_SESSION_INFO pwsi = NULL;
DWORD dwCount = 0;
if (wtsEnum(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pwsi, &dwCount))
{
for (DWORD i = 0; i < dwCount; i++)
{
TCHAR str[32];
switch (pwsi[i].State)
{
case WTSActive:
_tcscpy_s(str, _countof(str), _T("Active"));
break;
case WTSConnected:
_tcscpy_s(str, _countof(str), _T("Connected"));
break;
case WTSConnectQuery:
_tcscpy_s(str, _countof(str), _T("ConnectQuery"));
break;
case WTSShadow:
_tcscpy_s(str, _countof(str), _T("Shadow"));
break;
case WTSDisconnected:
_tcscpy_s(str, _countof(str), _T("Disconnected"));
break;
case WTSIdle:
_tcscpy_s(str, _countof(str), _T("Idle"));
break;
case WTSListen:
_tcscpy_s(str, _countof(str), _T("Listen"));
break;
case WTSReset:
_tcscpy_s(str, _countof(str), _T("Reset"));
break;
case WTSDown:
_tcscpy_s(str, _countof(str), _T("Down"));
break;
case WTSInit:
_tcscpy_s(str, _countof(str), _T("Init"));
break;
default:
_tcscpy_s(str, _countof(str), _T("Unknown"));
}
_tprintf_s(_T("Winstation Name : %s, Session Id : %d, Session State : %s\n"), pwsi[i].pWinStationName, pwsi[i].SessionId, str);
}
wtsFree(pwsi);
}
}
FreeLibrary(hMod);
}
return 0;
}
Saturday, November 11, 2017 8:10 PM
I have tried both scenarios. It seemed to me like it needed to be .cpp because I wasn't familiar with the #define<string> line and it barked at me while I had it as .c
Saturday, November 11, 2017 8:29 PM
I posted revised code for a C compiler above. Since I use Visual Studio I can't explain why the compiler invoked by Code::Blocks may not accept the sample code.
Saturday, November 11, 2017 8:32 PM
Output from running the sample code on Win 8.1 -
Winstation Name : Services, Session Id : 0, Session State : Disconnected
Winstation Name : Console, Session Id : 1, Session State : Active
Winstation Name : RDP-Tcp, Session Id : 65537, Session State : Listen
Sunday, November 12, 2017 11:27 PM
Perhaps you could help with this error I've gotten as I've tried to alter this code for use in Code::Blocks. I have a project coded using that IDE and I need wtsapi code added to it. I've had a bit of luck by adding these definitions. They were needed in a bit of code I used to successfully print the session ID.
typedef DWORD(WINAPI *WTS_SESSION_INFO)(void);
typedef WTS_SESSION_INFO(WINAPI *WTSENUMERATESESSIONSW)(void);
But I still have some issues getting it to print the output like it did in VisualStudio. I am having trouble retrieving values out of wpsi. The print statements weren't quite working from the example code, so I'm trying to write out some individual lines to test the retrieval. I can at least loop through what wtsEnum retrieves and see that it loops three times, which, based on the output above, is something I'd expect to have happen.
printf("%s", pwsi[i].pWinStationName);
shows me: error: request for member 'pWinStationName' in '*(pwsi + ((sizetype)i))', which is of non-class type 'DWORD() {aka long unsigned int()}'
and, in similar fashion,
DWORD abcd = pwsi[i].SessionId;
shows me: error: request for member 'SessionId' in '*(pwsi + ((sizetype)i))', which is of non-class type 'DWORD() {aka long unsigned int()}'
Wednesday, November 15, 2017 10:15 PM
It turns out that, for whatever reason, Code::Blocks' wtsapi32.h is missing a lot of lines compared to Visual Studio's WtsApi32.h. I can both run it in Visual Studio fine and run it in Code::Blocks fine, if I reference that WtsApi32.h, along with tweaking that header file a little bit so it doesn't go down the rabbit trail of needing every included header that Visual Studio would call.
I have the code looking like this now:
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <WtsApi32.h> // Visual Studio version
typedef BOOL(WINAPI *pfWTSENUMERATESESSIONS)(HANDLE hServer, DWORD Reserved, DWORD Version, PWTS_SESSION_INFO *ppSessionInfo, DWORD *pCount);
typedef void(WINAPI *pfWTSFREEMEMORY)(PVOID pMemory);
#ifdef _UNICODE
#define WTSENUMERATESESSIONS "WTSEnumerateSessionsW"
#else
#define WTSENUMERATESESSIONS "WTSEnumerateSessionsA"
#endif
#define WTS_CURRENT_SERVER_HANDLE 0
int _tmain()
{
HMODULE hMod = LoadLibrary(_T("wtsapi32.dll"));
if (hMod)
{
pfWTSENUMERATESESSIONS wtsEnum = (pfWTSENUMERATESESSIONS)GetProcAddress(hMod, WTSENUMERATESESSIONS);
pfWTSFREEMEMORY wtsFree = (pfWTSFREEMEMORY)GetProcAddress(hMod, "WTSFreeMemory");
if (wtsEnum && wtsFree)
{
PWTS_SESSION_INFO pwsi; // nullptr isn't in my environment and I couldn't get NULL to work
DWORD dwCount = 0;
if (wtsEnum(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pwsi, &dwCount))
{
for (DWORD i = 0; i < dwCount; i++)
{
TCHAR str[32];
switch (pwsi[i].State)
{
case WTSActive:
strcpy(str, "Active");
break;
case WTSConnected:
strcpy(str, "Disconnected");
break;
case WTSConnectQuery:
strcpy(str, "ConnectQuery");
break;
case WTSShadow:
strcpy(str, "Shadow");
break;
case WTSDisconnected:
strcpy(str, "Disconnected");
break;
case WTSIdle:
strcpy(str, "Idle");
break;
case WTSListen:
strcpy(str, "Listen");
break;
case WTSReset:
strcpy(str, "Reset");
break;
case WTSDown:
strcpy(str, "Down");
break;
case WTSInit:
strcpy(str, "Init");
break;
default:
strcpy(str, "Unknown");
}
printf(_T("Winstation Name : %s, Session Id : %d, Session State : %s\n"), pwsi[i].pWinStationName, pwsi[i].SessionId, str);
}
wtsFree(pwsi);
}
}
FreeLibrary(hMod);
}
Sleep(10000); // read it in the console
return 0;
}
Many thanks in helping me set this up and for your ideas.