|
You need to initialize your parameters as described in the documentation[^], so that the API knows how long your buffers are and what values you want returned. Using NULL as the input parameter tells the API to ignore that part of the request.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
You could do it like this:
int datatype;
unsigned char *value;
int sizeofvalue;
if(RegEnumValue(hKey,i,achValue,&cchValue,0,&datatype,value,&sizeofvalue) == ERROR_SUCCESS){
switch(datatype){
case REG_DWORD:
printf("%d",*((int*)value));
break;
case REG_NONE:
printf(...);
break;
case REG_EXPAND_SZ:
printf(...);
break;
case REG_SZ:
printf(...);
break;
case REG_MULTI_SZ:
printf(...);
break;
default:
printf(...);
break;
}
}
Where datatype will identify the type of information, achValue the name of the value, value the actual value and sizeofvalue the size of the value.
I added an example printf for REG_DWORD.
modified 13-Sep-18 21:01pm.
|
|
|
|
|
As mentioned in my response, this still will not get the values since the variables datatype , value and sizeofvalue need to be initialised before each call to RegEnumValue() .
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
I am interested in using my static lib to create a dll (implicitly linking). which means I need to (in vs2008) create a dll project that should generate the following:
1. header file (which have exported function declarations. These are simple wrappers to actual functions in the static lib and are using __declspec(dllexport) which are in the dll centric .cpp file)
2. import lib which will be made as a result of creating the dll
3. the actual dll which is created.
Now this is what I have:
I have made a test program that will utilize the above dll(including the import lib/header files) to test it. in this I have included all the three items. now the exe compiles/links without issue. however in the main.cpp when i call the exported functions they seem to see the exported dlls...but can't seem to execute the static library functions which they wrap.
i have tested the static lib against the exe and it works fine.
but when i put the dll in between the wrappers just dont seem to run the static library functions... i am at a loss.
there are no compile/link errors. no warnings. i am just thinking hhow do i find out if the static lib is properly being used by the dll? i have no way of knowing that i guess in vs2008. any help would be most apprecaited
|
|
|
|
|
Android Lava wrote: when i call the exported functions they seem to see the exported dlls...but can't seem to execute the static library functions which they wrap.
I'm not sure I understand what you mean here. Are you saying that the functions inside the DLL are supposed to call functions inside the static library? If that is the case then the static library needs to be linked in to your DLL when it is built.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
How do you know it's not calling the functions? Is the output wrong? Are you using breakpoints in the static library and seeing that you are not getting there?
If I get what you are doing, you should have TWO copies of the static library functions in your final memory image, one copy accessed by the main code and linked in when the main.exe is built and one copy inside the DLL itself that was linked in when the DLL file was created.
So, you say "can't seem to execute the static library functions". How have you determined that?
|
|
|
|
|
I don't understand the question. At a minimus perhaps it has two parts.
Part 1: If you want to write code to create a dll (not use an existing one) then you need to deal with doing just that. HOW the dll is used after that is irrelevant to this part of the code. And if I was doing it I would create source code and then plan on providing a compiler/linker which I would then call from the code so that those created the dll.
Part 2: There is code X that uses a dll Y. If Y does not exist when X is created then X must call the dll dynamically. Y of course must be a dll (or another type of binary that can be dynamically loaded.) X cannot randomly use any Y. X must know/assume some behavior of Y. So X loads Y, then looks for exactly the functionality that it demands. Then it calls that.
The two parts have nothing to do with each other. Implement them and test them independently from each other.
|
|
|
|
|
straight win32 app
I used the URLDownloadToFile to download a program file, probably a mistake, but a start at least. I wrote my IBindStatusCallback, and attached it to the URL program.
So I have a Window, not a dialog box, probably another mistake, I call the function in my window, and the function runs, but the wndproc in the window stops running, so my cancel button won't fire.
I tried to think of a way, that allows the OnProcess loop to give control back to the window, so I can capture the cancel button, but no luck.
Any points in the right direction would be helpful. I did see the data exchange thing, but thought it was for dialog boxes. This is my first experience in this.
|
|
|
|
|
Well, you could start a separate thread to do the URLDownloadToFile call and let the OnProcess (which I assume is your button) to return processing windows messages.
You would have to implement a "completion" message back from the thread to the main window to tell it when the download was complete and return the status of the download (success, fail, cancelled).
You cancel button should then work but, of course, you now need to tell the thread that the cancel button was hit or test a "cancelled" flag in the callback.
|
|
|
|
|
I was just reading up on creating threads.
So I guess I would create a thread in the window code, and run the class code in the thread.
Creating a thread looks like it takes alot of code.
Maybe the cancel button is not worth it.
|
|
|
|
|
Nah, creating thread is pretty straightforward, there are lots of examples here on code project. A quick search should find several like this CThread - a Worker Thread wrapper class[^]
Or you can use CWinThread (if you're derived from CWinApp). Or use _beginthreadex()[^] native Windows API.
Threads is a good thing to learn early. In this case, you can create the simplest of thread routines and not worry about complicated events and synchronization later. Just get it to do your download and send a completion message to your main windows process.
|
|
|
|
|
Well since I used the simplest way to download, the simplest thread sounds like a good idea. I'll start that tomorrow.
Right now I'm experimenting with creating a cancel button in the download class, and seeing if I can capture the message when the button is clicked. I got the button to appear and position itself with the right font in the Callback under BINDSTATUS_CONNECTING
I wrapped the original call to the class in a do loop, to keep it from progressing. Got the idea from the socket code I wrote.
I'm just messing around right now, experimenting till morning.
do {
bResult = caDownload._download_SQLServerExpress_GUI(cWnd, pb_SQL_Server_Download_Status, lbl_SQL_Server_Download_Status, bAreWeX64 );
} while (bResult == FALSE);
|
|
|
|
|
Which one was the easy one?, they both look intimidating.
|
|
|
|
|
They're both a lot easier having done them than before. I had no idea about threads or sockets 2 1/2 weeks ago, but have now learnt enough to get by and have cobbled together a rss reader - one which can pre-load all subscribed feeds in parallel, each in it's own thread.
Notifying the main window of a thread's completion is the easier of the two. Without MFC, it's as (1) defining a user message, and deciding on the format (if any) of the passed information (2) sending that message to the main window upon completion (3) handling that message in the main window.
E.g
Part 1 - Main window completion notification mechanism
#define WM_GALLERY_HTML_AVAILABLE WM_USER+1
#define WM_FILE_DOWNLOAD_COMPLETE WM_USER+2
...
...
...
__stdcall void galleryHtmlDownloadedCallbackFunc(PVOID data)
{
downloadHeader_t *hdr;
hdr = (downloadHeader_t*)data;
galHtmlStr = hdr->result;
galUrlStr = hdr->szUrl;
free(hdr->result);
hdr->result = NULL;
hdr->contentLen = 0;
PostMessage(mainDlgWnd, WM_GALLERY_HTML_AVAILABLE, 0, 0);
}
...
...
...
...
case WM_INITDIALOG:
mainDlgWnd = hwndDlg;
editWnd = GetDlgItem(hwndDlg, IDC_RICHEDIT1);
SetFocus(GetDlgItem(hwndDlg, IDC_RICHEDIT1));
return FALSE;
case WM_FILE_DOWNLOAD_COMPLETE:
return true;
case WM_GALLERY_HTML_AVAILABLE:
findLinks((char*)galHtmlStr.c_str(), links, validExts);
strLinks.clear();
printf("Number of links: %d\n", links.size());
for (i=0; i<links.size(); i++)
strLinks += links[i] + "\n";
SetDlgItemText(mainDlgWnd, IDC_RICHEDIT1, strLinks.c_str());
return true;
case WM_CLOSE:
EndDialog(hwndDlg, 0);
return TRUE;
Part 2 - Threading mechanism
static downloadHeader_t curDownload;
GetWindowText(urlHwnd, szUrl, urlLen+1);
savePath = getFolderName(mainDlgWnd, "C:\\001");
if (strlen(szUrl) && strlen(savePath))
{
curDownload.szUrl = szUrl;
curDownload.callback = (voidFuncPtr)galleryHtmlDownloadedCallbackFunc;
curDownload.saveFilePath = savePath;
_beginthread(DownloadThread, NULL, (void*)&(curDownload));
}
delete savePath;
delete szUrl;
typedef void (*voidFuncPtr)(void*);
struct downloadHeader_t
{
string szUrl; voidFuncPtr callback; string saveFilePath; PVOID lParam;
SOCKET conn;
char *readBuffer, *sendBuffer, *tmpBuffer, *result;
string server, filepath, filename;
long thisReadSize, headerLen, contentLen;
};
void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename)
{
string::size_type n;
string url = mUrl;
if (url.substr(0,7) == "http://")
url.erase(0,7);
n = url.find('/');
if (n != string::npos)
{
serverName = url.substr(0,n);
filepath = url.substr(n);
n = filepath.rfind('/');
filename = filepath.substr(n+1);
}
else
{
serverName = url;
filepath = "/";
filename = "";
}
}
SOCKET connectToServer(char *szServerName, WORD portNum)
{
struct hostent *hp;
unsigned int addr;
struct sockaddr_in server;
SOCKET conn;
conn = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (conn == INVALID_SOCKET)
return NULL;
if(inet_addr(szServerName)==INADDR_NONE)
{
hp=gethostbyname(szServerName);
}
else
{
addr=inet_addr(szServerName);
hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
}
if(hp==NULL)
{
closesocket(conn);
return NULL;
}
server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
server.sin_family=AF_INET;
server.sin_port=htons(portNum);
if(connect(conn,(struct sockaddr*)&server,sizeof(server)))
{
closesocket(conn);
return NULL;
}
return conn;
}
int getHeaderLength(char *content)
{
const char *srchStr1 = "\r\n\r\n", *srchStr2 = "\n\r\n\r";
char *findPos;
int ofset = -1;
findPos = strstr(content, srchStr1);
if (findPos != NULL)
{
ofset = findPos - content;
ofset += strlen(srchStr1);
}
else
{
findPos = strstr(content, srchStr2);
if (findPos != NULL)
{
ofset = findPos - content;
ofset += strlen(srchStr2);
}
}
return ofset;
}
void __stdcall DownloadThread(void *dat)
{
downloadHeader_t *hdr;
hdr = (downloadHeader_t *)dat;
const int bufSize = 512;
printf("download thread - \n");
hdr->result = NULL;
hdr->readBuffer = (char*)malloc(bufSize);
hdr->sendBuffer = (char*)malloc(bufSize);
hdr->tmpBuffer = (char*)malloc(bufSize);
printf("download thread - \n");
mParseUrl((char*)hdr->szUrl.c_str(), hdr->server, hdr->filepath, hdr->filename);
printf("download thread - \n");
hdr->conn = connectToServer((char*)hdr->server.c_str(), 80);
if (hdr->conn)
printf("download thread - connected?\n");
sprintf(hdr->tmpBuffer, "GET %s HTTP/1.0", hdr->filepath.c_str());
strcpy(hdr->sendBuffer, hdr->tmpBuffer);
strcat(hdr->sendBuffer, "\r\n");
sprintf(hdr->tmpBuffer, "Host: %s", hdr->server.c_str());
strcat(hdr->sendBuffer, hdr->tmpBuffer);
strcat(hdr->sendBuffer, "\r\n");
strcat(hdr->sendBuffer, "\r\n");
send(hdr->conn, hdr->sendBuffer, strlen(hdr->sendBuffer), 0);
hdr->contentLen = 0;
while(1)
{
memset(hdr->readBuffer, 0, bufSize);
hdr->thisReadSize = recv (hdr->conn, hdr->readBuffer, bufSize, 0);
if ( hdr->thisReadSize <= 0 )
break;
hdr->result = (char*)realloc(hdr->result, hdr->thisReadSize+hdr->contentLen);
memcpy(hdr->result+hdr->contentLen, hdr->readBuffer, hdr->thisReadSize);
hdr->contentLen += hdr->thisReadSize;
}
hdr->headerLen = getHeaderLength(hdr->result);
hdr->contentLen -= hdr->headerLen;
memmove(hdr->result, hdr->result+hdr->headerLen, hdr->contentLen);
realloc(hdr->result, hdr->contentLen+1);
hdr->result[hdr->contentLen] = 0x0;
closesocket(hdr->conn);
free(hdr->readBuffer);
free(hdr->sendBuffer);
free(hdr->tmpBuffer);
if (hdr->callback)
hdr->callback(hdr);
}
|
|
|
|
|
Nice job on the code modules. I can see that creating a thread to run the program in is not that difficult. I have an idea of the components needed now after examining your sample.
Let's see what I can some up with today. I'll be back.
|
|
|
|
|
Not a problem.
I just noticed that I declared the DownloadThread function as being __stdcall. It's a mistake, and should read __cdecl. Errors with calling conventions are, I suspect, begin the majority of my program crashes.
FWIW, it's _beginthreadex that wants a thread function called using __stdcall.
|
|
|
|
|
I didn't run your sample. I just examined it for reverse engineering to suit my needs.
I spend half a day yesterday cleaning up my code, but was not able to successfully test my thread. I got confused with how the process worked. In other words, I show the cancel button, then I run a function, and the cancel button won't send a message back to wndproc until it completes. So I messsed around with the way I made the original class, and fixed my mistakes.
Today I will try to get my thread working.
|
|
|
|
|
He, he. I'm familiar with that feeling. It took me quite a while to wrap my head around when and where was the appropriate place to do things. I've since re-done the function such that it can send progress callbacks too.
Haven't bothered with a cancel functionality yet - though I'd imagine it to be a simple matter of setting a flag from the main program thread, checking this flag each time through the receive loop in the download thread. (may have to use a mutex to access the flag in a safe manner)
|
|
|
|
|
I have the create thread working now. just went simple on it.
I already had the progress bar and status text working, now the new thread launches the download, and the cancel button responds to my click. I know how to stop the Download Callback with E_ABORT, I just need to figure out how to send that message to the progress loop.
After that, I need to figure out how to destroy the thread on cancel, or when the download is complete, _endthread,
but I'm not sure if I need to close the handle, and the WaitForObject.
Getting there. Still experimenting with it, so it's not finalized.
hThread = (HANDLE)_beginthreadex(NULL, 0, SQL_SoftwareDownload, NULL, 0, (unsigned *)&thread_id);
return resultCode;
}
unsigned int __stdcall SQL_SoftwareDownload(void *) {
int iReturn = 0;
HRESULT hr = E_FAIL;
callback = new SQL_Servers_BindCallback;
callback->m_MDIChild = hSQL_Servers_Download;
callback->m_Progress_Text = lbl_SQL_Server_Download_Status;
callback->m_Progress_Bar = pb_SQL_Server_Download_Status;
callback->b_Downloading = TRUE;
hr = URLDownloadToFile(NULL, szUrl, szFileName, 0, callback);
if (hr == S_OK)
iReturn = 1;
return iReturn;
}
|
|
|
|
|
Congrats.
The HANDLE that you get back from _beginthreadex() should eventually be cleaned up with CloseHandle(). There is a resource that is backed by the HANDLE and you should clean that up when you are done with it.
The HANDLE remains a "waitable object" even after the thread exits. That way, you can do a WaitForSingleObject() on the HANDLE to either wait for the thread to finish or to test for completion (by adding a timeout value to the wait). Once the thread exits, the HANDLE becomes "signaled" and that will satisfy any wait, even one that comes along after the thread completes. That's why you have to eventually close it.
|
|
|
|
|
That sounds tricky.
I'm not sure what to do yet, but I'm moving forward. Here is my process
Button Click Next ->
_download_SQLServer_Begin - GUI stuff - Call _download_SQLServer_Package
_download_SQLServer_Package - Download Prep - Start Thread
SQL_SoftwareDownload - load callback, call urldownload function
_download_SQLServer_End - End Thread stuff, GUI unwind
cancel button click!
_cancel_SQLServer_Package - stop download process- send message to bind callback to E_ABORT
call _download_SQLServerEnd for thread and GUI unwind
I had to split the gui stuff in half - sorry to bombard you with code, I'm sure theirs much to delete
BOOL _download_SQLServer_Begin( HWND cWnd )
{
BOOL bResult = FALSE;
BOOL bPreFlightCheck = FALSE;
BOOL bAreWeX64 = FALSE;
CA_Windows caWin;
ShowWindow(lbl_SQL_Server_Download_Instructions, SW_HIDE);
ShowWindow(bt_SQL_Server_Download_Next, SW_HIDE);
ShowWindow(bt_SQL_Server_Download_Exit, SW_HIDE);
ShowWindow(bt_SQL_Server_Download_Cancel, SW_SHOW);
ShowWindow(lbl_SQL_Server_Download_Label, SW_HIDE);
ShowWindow(lbl_SQL_Server_Download_Status, SW_SHOW);
bAreWeX64 = caWin._getProcessorArchitecture();
if (bAreWeX64 == TRUE) {
SetWindowText(lbl_SQL_Server_Download_Status, L"Requesting X64 Version from microsoft.com");
}
else {
SetWindowText(lbl_SQL_Server_Download_Status, L"Requesting X86 Version from microsoft.com");
}
SendMessage(pb_SQL_Server_Download_Status, PBM_SETPOS, (WPARAM)0, LPARAM(100));
SendMessage(pb_SQL_Server_Download_Status, PBM_SETSTEP, (WPARAM)1, NULL);
ShowWindow(pb_SQL_Server_Download_Status, SW_SHOW);
UpdateWindow(pb_SQL_Server_Download_Status);
UpdateWindow(cWnd);
bResult = _download_SQLServer_Package( bAreWeX64 );
return bResult;
}
BOOL _download_SQLServer_End( HWND cWnd ) {
BOOL bResult = FALSE;
ShowWindow(bt_SQL_Server_Download_Cancel, SW_HIDE);
ShowWindow(bt_SQL_Server_Download_Exit, SW_SHOW);
if ( bResult == TRUE ) {
SetWindowText(lbl_SQL_Server_Download_Status, L"Download has completed successfully");
ShowWindow(lbl_SQL_Server_Download_Label, SW_SHOW);
BOOL preFlightCheck = FALSE;
Sleep(2000);
int msgboxID = MessageBoxEx(hSQL_Servers_Download,
L"Would you like to install SQL Server now?",
L"SQL Server has been downloaded successfully",
MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONINFORMATION,
LANG_NEUTRAL
);
switch ( msgboxID )
{
case IDOK:
preFlightCheck = TRUE;
break;
case IDCANCEL:
preFlightCheck = FALSE;
break;
}
}
return bResult;
}
BOOL _cancel_SQLServer_Package( HWND cWnd )
{
BOOL bResult = FALSE;
int msgboxID;
msgboxID = MessageBoxEx(hSQL_Servers_Download,
L"Canceling the download will prevent you from installing SQL Server?",
L"SQL Server is downloading now",
MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONINFORMATION,
LANG_NEUTRAL
);
switch ( msgboxID )
{
case IDOK:
_download_SQLServer_End( cWnd );
bResult = FALSE;
break;
case IDCANCEL:
bResult = FALSE;
break;
}
return bResult;
}
BOOL _download_SQLServer_Package( BOOL X64 )
{
BOOL resultCode = FALSE;
BOOL bThread = FALSE;
HRESULT hr = S_OK;
char *fs_X86 = "762761216";
char *fs_X64 = "834555904";
char *ckSum_X86 = "10ef23fe7bd9b6a64dcaf1927dd19df7";
char *ckSum_X64 = "5122f358a63e12a3095aaabba8203292";
WCHAR *szUrl_X86 = L"http://download.microsoft.com/download/9/7/6/9761DF25-CBD8-4D48-9415-065F6BD4E63D/SQLEXPRADV_x86_ENU.exe";
WCHAR *szUrl_X64 = L"http://download.microsoft.com/download/9/7/6/9761DF25-CBD8-4D48-9415-065F6BD4E63D/SQLEXPRADV_x64_ENU.exe";
WCHAR *szFileName_X86 = L"SQLEXPRADV_x86_ENU.EXE";
WCHAR *szFileName_X64 = L"SQLEXPRADV_x64_ENU.EXE";
WCHAR *szTargetFolder = NULL;
WCHAR *szSystemDrive = NULL;
WCHAR *szFileName_X = NULL;
DWORD dwReserved = 0;
LPCWSTR szError = NULL;
if (X64 == FALSE) {
int iSize = wcslen(szUrl_X86)*sizeof(szUrl_X86);
szUrl = szUrl_X86;
}
else {
int iSize = wcslen(szUrl_X64)*sizeof(szUrl_X64);
szUrl = szUrl_X64;
}
CA_Windows *caWin = new CA_Windows;
szSystemDrive = caWin->_getWindowsFolder();
caWin = NULL;
wcsncpy_s(szFileName, szSystemDrive, wcslen(szSystemDrive) );
wcsncat_s(szFileName, L"\\Temp\\", wcslen(L"\\Temp\\") );
if (X64 == FALSE) {
wcsncat_s(szFileName, szFileName_X86, wcslen(szFileName_X86) );
}
else {
wcsncat_s(szFileName, szFileName_X64, wcslen(szFileName_X64) );
}
hThread = (HANDLE)_beginthreadex(NULL, 0, SQL_SoftwareDownload, NULL, 0, (unsigned *)&thread_id);
return resultCode;
}
unsigned int __stdcall SQL_SoftwareDownload(void *) {
int iReturn = 0;
HRESULT hr = E_FAIL;
SQL_Servers_BindCallback *callback;
callback = new SQL_Servers_BindCallback;
callback->m_MDIChild = hSQL_Servers_Download;
callback->m_Progress_Text = lbl_SQL_Server_Download_Status;
callback->m_Progress_Bar = pb_SQL_Server_Download_Status;
callback->Release();
hr = URLDownloadToFile(NULL, szUrl, szFileName, 0, callback);
if (hr == S_OK)
iReturn = 1;
return iReturn;
}
|
|
|
|
|
From a look over your code, I think that you're seeing the way to cancel the download as being to kill the thread. Given that you're using URLDownloadToFile (a blocking function), there's no apparent way to ask it to stop. With this in mind the clear choice would be to kill the thread - PROVIDING it's safe to nuke a thread in the middle of this function.
Providing it's safe, I guess you could just kill the thread. From what I can tell, you can only terminate a thread started with _beginthreadex from within it, using _endthreadex (you can't pass a thread ID to the function). With this in mind - it seems that _beginthreadex and _endthreadex are insufficient for your needs. If you were looping in the download function (like I was with recv in my download function), you could check a flag to see if the download should be discontinued - if so, simply exit the receive loop gracefully, cleaning up any/all memory resources.
Since you're not running a loop that you can check for the flag, it seems that your only option is to cancel the download be exiting/terminating the thread. In this case, it may perhaps be that you need to use the OpenThread, CloseThread, SuspendThread, ResumeThread, TerminateThread family of functions, since they allow you to pass a thread identifier to Suspend, Resume and Terminate any given thread.
There's some history associated with beginthread, _beginthreadex and OpenThread. I forget which supersedes which and for quite what reason, though I recall that there were inherent problems that caused the 're'-implementation. I'm inclide to think it had something to do with memory leaks or issues with handles.
|
|
|
|
|
I tried killing the thread, but it destroyed my window. but that was before I split the functions.
Here's the other half of the code - Callback Statu. I think somehow, there is a way for the callback to receive messages or values during the OnProcess loop, or it's just optimism on my behalf. I should be able to get the loop to ask the window if cancel has been invoked.
There is a code sample on CP that uses a dialog box, in which a handle is created back to the dialog box, I'm not sure if the handle is bi-directional.
FYI:
I'm not not looking for code, just ideas or pointers.
This is the callback, the h file is seperate. I'm fuzzy about this line in the constructor
: m_Progress_Text(NULL), m_Progress_Bar(NULL) {
in which I' not really sure what it means, or what it does.
#include "stdafx.h"
#include <stdlib.h>
#include "atlstr.h"
#include <string>
#include <tchar.h>
#include <math.h>
#include "resource.h"
#include "stdio.h"
#include <commctrl.h>
#include <Windows.h>
#include <Winuser.h>
#include <Winbase.h>
#include "SQL_Servers_BindCallback.h"
#pragma comment(lib, "urlmon.lib")
#pragma comment(lib,"wininet.lib")
#define RETURN_ON_FAILURE(hr) if (FAILED(hr)) return (hr);
SQL_Servers_BindCallback::SQL_Servers_BindCallback ( ) : m_Progress_Text(NULL), m_Progress_Bar(NULL) {
ShowWindow(m_Progress_Bar, SW_SHOW);
UpdateWindow(m_Progress_Bar);
UpdateWindow(m_MDIChild);
SendMessage(m_Progress_Bar, PBM_SETRANGE, 0, MAKELPARAM(0, 5000) );
SendMessage(m_Progress_Bar, PBM_SETPOS, (WPARAM)10, 0);
}
SQL_Servers_BindCallback::~SQL_Servers_BindCallback( ) {
}
HRESULT SQL_Servers_BindCallback::OnProgress(
ULONG ulProgress,
ULONG ulProgressMax,
ULONG ulStatusCode,
LPCWSTR wszStatusText)
{
static int iMsgLen;
static double dBytes;
static double dTotal;
static double dPercent;
static double dPB;
static TCHAR szStatusMessage[80];
wcsncpy_s(szStatusMessage, L"please wait...", wcslen(L"please wait...") );
switch (ulStatusCode) {
case BINDSTATUS_CONNECTING:
wcsncpy_s(szStatusMessage, L"Connecting to download.microsoft.com", wcslen(L"Connecting to download.microsoft.com") );
break;
case BINDSTATUS_DOWNLOADINGDATA:
dBytes = ((float)ulProgress / (1024*1024));
dTotal = ((float)ulProgressMax / (1024*1024));
dPercent = dBytes/dTotal*100;
if ((dBytes > 0.01) && (dTotal > 0.01)) {
swprintf_s(szStatusMessage,
80,
L"Downloading %.2f MB of %.2f MB [%.1f%%]",
dBytes, dTotal, dPercent
);
}
break;
case BINDSTATUS_ENDDOWNLOADDATA:
wcsncpy_s(szStatusMessage, L"Disconnecting from download.microsoft.com", wcslen(L"Disconnecting from download.microsoft.com") );
Sleep(1000);
return TRUE;
break;
}
SetWindowText(m_Progress_Text, szStatusMessage);
dPB = (float)ulProgress/ulProgressMax*5000.0;
SendMessage(m_Progress_Bar, PBM_SETPOS, (WPARAM)dPB, 0);
SendMessage(m_Progress_Bar, PBM_SETRANGE, 0, MAKELPARAM(0, 5000) );
iLoopCount++;
return S_OK;
}
HRESULT SQL_Servers_BindCallback::OnStartBinding(
DWORD dwReserved,
IBinding __RPC_FAR *pib)
{ return S_OK; }
HRESULT SQL_Servers_BindCallback::GetPriority(
LONG __RPC_FAR *pnPriority)
{ return E_NOTIMPL; }
HRESULT SQL_Servers_BindCallback::OnLowResource(
DWORD reserved)
{ return E_NOTIMPL; }
HRESULT SQL_Servers_BindCallback::OnStopBinding(
HRESULT hresult,
LPCWSTR szError)
{ return S_OK; }
HRESULT SQL_Servers_BindCallback::GetBindInfo(
DWORD __RPC_FAR *grfBINDF,
BINDINFO __RPC_FAR *pbindinfo)
{ return E_NOTIMPL; }
HRESULT SQL_Servers_BindCallback::OnDataAvailable(
DWORD grfBSCF,
DWORD dwSize,
FORMATETC __RPC_FAR *pformatetc,
STGMEDIUM __RPC_FAR *pstgmed)
{ return S_OK; }
HRESULT SQL_Servers_BindCallback::OnObjectAvailable(
REFIID riid,
IUnknown __RPC_FAR *punk)
{ return E_NOTIMPL; }
ULONG SQL_Servers_BindCallback::AddRef()
{ return 0; }
ULONG SQL_Servers_BindCallback::Release()
{ return 0; }
HRESULT SQL_Servers_BindCallback::QueryInterface(
REFIID riid,
void __RPC_FAR *__RPC_FAR *ppvObject)
{ return E_NOTIMPL; }
|
|
|
|
|
jkirkerx wrote: I should be able to get the loop to ask the window if cancel has been invoked.
I'm not too sure about this hope. My understanding had been that the communication should be the other way around - i.e The main window will tell the thread to cancel the download, rather than the thread asking the window if it should continue/cancel
jkirkerx wrote: FYI:
I'm not not looking for code, just ideas or pointers.
This is the callback, the h file is seperate. I'm fuzzy about this line in the constructor
: m_Progress_Text(NULL), m_Progress_Bar(NULL) {
in which I' not really sure what it means, or what it does.
Sure thing, sorry if I've innundated you with useless code, confusing the matter.
I think you'l find that the above snippet will call the constructors for the progress text and the progress bar. I reckon you'd find that if you changed the NULLs to "myProgTest" and 50, respectively that you'd have a progress bar at 50% with the text myProgTest - why not give it a try?
After thinking over this some more last night, I decided to have a go at implementing a cancel button for my downloads. In this case, I added an extra field to the struct that I pass to the thread function - a simple boolean flag, bCancelled. Cancelling the download is a simple matter of setting this flag in the struct from the main thread in response to the cancel button.
In the download function, during the receive loop I simply check the status of this flag. If true, I just clean-up the memory I used and exit. It seems to work flawlessly. Also, since it's a graceful exit, I can be certain of the number of bytes downloaded before it was cancelled. This has a two-fold benefit -
(1) I can close the handle on the file I'm saving it too, avoiding total loss of the data and
(2) I know where the download is up to, so I can resume it at a later time of my choosing
Initially, my aim had been to get the size of a resource if given a url. Since wininet didn't seem to provide the functionality, I had to get access to the raw HTTP headers. From what I could determine, this required low-level use of sockets. However, the rewards have been, well worth-while.
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_BTN_CANCEL:
singleDownload.bCancelled = true;
return true;
hdr->contentLen = 0;
while(1)
{
if (hdr->bCancelled)
break;
memset(hdr->readBuffer, 0, bufSize);
hdr->thisReadSize = recv (hdr->conn, hdr->readBuffer, bufSize, 0);
if ( hdr->thisReadSize <= 0 )
break;
|
|
|
|
|
I just didn't want to sound like I wanted someone to fix my code because I posted it. No insult intended.
I got it to work this afternoon. I sort of did the same thing, but not as elaborate. I examined the code sample in CP, and put the if statement back in, and made it a long this time instead of a boolean.
I just needed to get the mechanics working, so I didn't go around in circles trying complex things. I cleaned up this program alot, and got rid of some the erratic side effects of experimenting. It's not perfect in the purest sense, but it's a start. Now I can clean it up some more based on feedback, and write a check to see if the file is already downloaded, and integrate the installation program I already wrote.
Edit:
After reading your post several times, I realize that all of the benefits you listed are well worth it. And you right about pushing the cancel to the thread. But having the byte count, and being able to resume is pretty sweet, when you need to pause to do something else, and pick up again. I wanted to write a socket based program, but I've only been doing this since oct 5, so I had to keep it simple for now. Are you checksuming your download?
I put the variable in the h file for the callback
SQL_Servers_BindCallback::SQL_Servers_BindCallback ( ) : m_Progress_Text(NULL), m_Progress_Bar(NULL) {
}
SQL_Servers_BindCallback::~SQL_Servers_BindCallback( ) {
}
HRESULT SQL_Servers_BindCallback::OnProgress(
ULONG ulProgress,
ULONG ulProgressMax,
ULONG ulStatusCode,
LPCWSTR wszStatusText)
{
static int iMsgLen;
static double dBytes;
static double dTotal;
static double dPercent;
static double dPB;
static TCHAR szStatusMessage[80];
if ( g_fAbortDownload == 0 ) <- put in h file of this cpp file
return E_ABORT;
Then I made the callback global to the window page, and changed the way I close the thread.
BOOL _download_SQLServer_Begin( BOOL bCancel ) {
int errorNumber;
hThread = (HANDLE)_beginthreadex(NULL, 0, SQL_SoftwareDownload, NULL, 0, (unsigned *)&thread_id);
if (hThread == 0) {
errorNumber = errno;
}
Sleep(2000);
int rv = WaitForSingleObjectEx(hThread, 3000, FALSE);
if (rv == WAIT_OBJECT_0)
resultCode = 1;
return resultCode;
}
unsigned int __stdcall SQL_SoftwareDownload(void *) {
int iReturn = 0;
HRESULT hr = E_FAIL;
callback = new SQL_Servers_BindCallback;
callback->m_MDIChild = hSQL_Servers_Download;
callback->m_Progress_Text = lbl_SQL_Server_Download_Status;
callback->m_Progress_Bar = pb_SQL_Server_Download_Status;
hr = URLDownloadToFile(NULL, szUrl, szFileName, 0, callback);
if (hr == S_OK)
iReturn = 1;
return iReturn;
}
BOOL _download_SQLServer_End( BOOL bCancel ) {
BOOL bResult = FALSE;
BOOL bHandle = FALSE;
unsigned iThread;
callback->g_fAbortDownload = 0; < - made the call here, callback picked it up and aborted
callback->Release();
SetWindowText(lbl_SQL_Server_Download_Status, L"Disconnecting from download.microsoft.com");
UpdateWindow(hSQL_Servers_Download);
iThread = WaitForSingleObjectEx(hThread, 3000, FALSE); <- I thought this was needed
bHandle = CloseHandle(hThread); < - I know _endthread is called automatically
if (bHandle == 0) <- Not sure, but it shuts down real smooth like.
Sleep(2000);
ShowWindow(pb_SQL_Server_Download_Status, SW_HIDE);
ShowWindow(bt_SQL_Server_Download_Cancel, SW_HIDE);
ShowWindow(bt_SQL_Server_Download_Exit, SW_SHOW);
|
|
|
|
|