|
Cool.
Well I'm not getting the exit code, but I do get a still active code which is OK for now.
Thanks Chuck.
|
|
|
|
|
That's the defined behavior of GetExitCodeProcess(). The exit code does not exist until the process exits! The defined return is "Still Active", meaning "No exit code exists", or colloquially expressed, "Yo, Dude, What Exit Code? It Ain't Exited Yet".
|
|
|
|
|
This is the complete code I wrote.
If no one has an objection to the GetExitCodeProcess in the loop, I'll stick with it because it works. I just thought it was completely wrong to use, or perhaps I'm just getting better at this.
if (CreateProcess(sz_SQLServer_Install_FileName, szParameters, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS, 0, sz_SQLServer_Install_FolderPath, &si, &pi) ) {
while(WAIT_TIMEOUT == WaitForSingleObject(pi.hProcess, 10)) {
MSG oMSG;
while(::PeekMessage(&oMSG, NULL, 0,0, PM_NOREMOVE)) {
if(::GetMessage(&oMSG, NULL, 0,0) ) {
::TranslateMessage(&oMSG);
::DispatchMessage(&oMSG);
}
else {
break;
}
if (ndx != 100) {
SendMessage(g_SQLServer_Install_Progress_Bar, PBM_SETPOS, (WPARAM)ndx, 0);
UpdateWindow(g_SQLServer_Install_MDIWindow);
Sleep(100);
++ndx;
}
else {
if( iMO == 0 ) {
SetWindowText(g_SQLServer_Install_Progress_Text, L"Installation may take up to 30 minutes");
iMO = 1;
}
else {
SetWindowText(g_SQLServer_Install_Progress_Text, L"Installing Microsoft SQL Server Express 2008");
iMO = 0;
}
UpdateWindow(g_SQLServer_Install_MDIWindow);
ndx = 0;
}
GetExitCodeProcess(pi.hProcess, (unsigned long *)&exit_status);
if (exit_status != 259)
break;
}
}
GetExitCodeProcess(pi.hProcess, (unsigned long *)&exit_status);
if (exit_status == 0) {
SendMessage(g_SQLServer_Install_MDIWindow, WM_COMMAND, (WPARAM)g_SQLServer_Install_IDC_COMMAND, (LPARAM)g_SQLServer_Install_IDM_COMPLETE );
}
else {
SendMessage(g_SQLServer_Install_MDIWindow, WM_COMMAND, (WPARAM)g_SQLServer_Install_IDC_COMMAND, (LPARAM)g_SQLServer_Install_IDM_CANCEL );
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
exitCode = 1;
bResult = TRUE;
|
|
|
|
|
Well, the call inside the loop is pretty useless but if it makes you happy...
The sole purpose, as written, is to cause the big while loop to break out if the response is anything but "still running". But the WaitForSingleObject() returning anything other than "WAIT_TIMEOUT" (that is, WAIT_OBJECT_0) also breaks out of the loop (the "else clause") which will happen as soon as the thread stops running (which sets the exit code and signals the hProcess object).
Now, I'll grant you that by having the call at the bottom of the while loop may cause you to notice the process termination a couple of microseconds earlier than if you went to the top of the loop but you counter act that miniscule gain by discarding the status you might have received then and issue another GetExitCodeProcess() immediately upon exiting the while loop.
So, it adds nothing to the code, adds processing at each iteration. But, like I said, if it makes you happy ....
|
|
|
|
|
HA, I just read the documentation on GetExitCodeProcess()[^] (twice to be sure).
If I were a malicious / devious person, I'd write my process to "return 259;" as the exit status. Since GetExitCodeProcess() returns either status STILL_ACTIVE or the return code from the process. If 259 is my exit code, following Microsoft's method for testing the status would be fooled into thinking the process is still running. Does this count as "Amusing Microsoft API Tricks"
Anyway, WaitForSingleObject() on the hProcess object is the only way to be sure.
|
|
|
|
|
Hmm.
So if I just removed my GetExitCode from the loop, I'm good to go?
It's not that I like it.
I'll remove it and run the test again a couple of more times, just to make sure.
Thanks Chuck
|
|
|
|
|
Jim,
I observe that you are passing a variable named sz_SQLServer_Install_FileName to CreateProcess which would obviously imply that you are installing SQL Server. Are you using a MSI installer? According to the documentation... the MsiExec.exe and InstMsi.exe MSI installers will return useful status/error codes.
Error Codes[^]
Best Wishes,
-David Delaune
|
|
|
|
|
I'm running these programs. I think Microsoft wrote the installer.
I just finished running the test, and put the line back in. Can't detect a cancel when I click the red X square during the expansion of the files to the temp dir. With the line, I can detect the click. So I guess the line is useful. I just want to cover my butt, who knows what the user will do.
So I detect the cancel, use the message pump to acknowledge the cancel, messagebox, start the installation again.
WCHAR *szFileName_X86 = L"SQLEXPRADV_x86_ENU.EXE";
WCHAR *szFileName_X64 = L"SQLEXPRADV_x64_ENU.EXE";
|
|
|
|
|
Thanks for your help last night Randor. Your open and closed probe questions help me understand why I needed that line of code, to detect if the process was still running.
It did not dawn on me that that the exit code came from the program that I was running, and that I can't trust the program to produce an exit code upon exit.
As far as the WaitForSingleObject goes, I have it set for 10, because I needed the cosmetic loop to run, and needed to be able to send messages to the pump.
I like the way it is, so I will leave it, and add something to make a check for completion, like checking to see if the final install log file exist or something.
I was tired last night so I left the office.
FYI:
Almost done with the initial version of my program, and everything actually works. I was able to translate all my vb programs so far which amazes me. Should be done by the end of the month, and will then make the deployment package.
Going to need some alpha testers, I wonder if Code Project can help me with that.
Thanks
jkirker
|
|
|
|
|
Hi,
i am confused about the two. Can someone explain what is the difference and when would one use one or the other?
Thanks
|
|
|
|
|
Are you talking about implicit vs. explicit linking?
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
|
|
|
|
|
dynamic linking: your code is built to use functions in a shared library; your code runs, the OS finds the shared lib and gives you access to the functions you need.
dynamic loading: your code can discover, load and execute code from shared libraries. but the shared libraries might not even exist at run time. your code explicitly loads the shared library. best example of this is plugins.
|
|
|
|
|
What is the highest concurrent rate of a server,
5000?
|
|
|
|
|
Rate of what?
Please, if you must post questions then think about what you are trying to discover, and give proper details.
Unrequited desire is character building. OriginalGriff
I'm sitting here giving you a standing ovation - Len Goodman
|
|
|
|
|
Hi,
Your question is porrly poorly written because "concurrent rate of a server" is a vague description. I am assuming that you mean to write "concurrent socket connections to a server".
If I am correct then you should have a look in your registry at:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters
Most Microsoft operating systems have the MaxUserPort value set to a default value of 5000. Yes, this is the maximum number sockets that can be connected to a server.
Best Wishes,
-David Delaune
Heh, yes my answer was poorly written! I have fixed the spelling!
modified 21-Feb-12 20:19pm.
|
|
|
|
|
Hi All.
I call the following when I need to save a report in my MFC base program:
m_pEndThread = AfxBeginThread(Save2CWGCoilRep, &m_CoilRep[iCRepPos], THREAD_PRIORITY_NORMAL,0,0);
UINT Save2CWGCoilRep(LPVOID pParam)
{
I use CStdioFile to them save data to file
}
Then before I save another report, I will call
if(m_pEndCoilThread != NULL)
::WaitForSingleObject(m_pEndCoilThread->m_hThread, INFINITE);
to see if the thread is finished before calling again to save another report. And if not just wait. It seems in Win7 now, this call will not exit so that the program will just freeze. It does not freeze all the time, seems random. In WinXP, the same program seems not to have this problem. I am not sure what I am missing.
Any help or suggestion will be appreciated. Thanks!
Stan the man
|
|
|
|
|
You may ran into a race condition. A common way to check if a thread has been terminated (ensure that STILL_ACTIVE is not returned by your thread):
if (m_pEndCoilThread != NULL &&
m_pEndCoilThread->m_hThread != INVALID_HANDLE_VALUE)
{
DWORD dwExitCode = 0;
::GetExitCodeThread(m_pEndCoilThread->m_hThread, &dwExitCode);
if (dwExitCode == STILL_ACTIVE)
::WaitForSingleObject(m_pEndCoilThread->m_hThread, INFINITE);
m_pEndCoilThread = NULL;
}
|
|
|
|
|
Hi Jochen.
Thanks. I will try it out....
Stan
|
|
|
|
|
From the code example I take it that you create the Save2CWGCoilRep thread from the UI thread, i.e. from a menu or button handler function. My guess then is that you have a deadlock:
1. The background thread sends messages to the UI thread. Maybe it tries to display a message box, but it could be anything really.
2. The UI thread is suspended in WaitForSingleObject and can't process the message.
It's risky to suspend the UI thread as you don't know when or why messages are posted to it. I would instead protect the critical parts of Save2CWGCoilRep with a critical section and take away the WaitForSingleObject call in the UI thread.
|
|
|
|
|
Where does m_pEndCoilThread come from? DOnt you mean m_pEndThread?
==============================
Nothing to say.
|
|
|
|
|
class Base
{
char * ptr;
public:
Base(){}
Base(char * str)
{
ptr = new char[strlen(str)];
strcpy(ptr,str);
}
};
class Derived : public Base
{
char * ptr_s;
public:
Derived(char * str1,char * str2):Base(str2)
{
ptr_s = new char[strlen(str1)];
strcpy(ptr_s,str1);
}
Derived(const Derived & sec)//:Base(sec.ptr)
{
this->ptr_s = new char[strlen(sec.ptr_s)];
strcpy(this->ptr_s,sec.ptr_s);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Derived Obj1("sunil","singh");
Derived Obj2 = Obj1;
return 0;
}
Obj1 is a derived class object where base class char pointer is initialized with "singh" and
derived class char pointer is initilized with "sunil". I want to create Obj2 out of Obj1. Separate memory should be created for
Obj2 char pointer (base part and derived part as well) and that should be initialized with the strings contained in Obj1.
Here the problem is: Derived class part can be initialized with copy constructor. How to initialize the base class char poniter of Obj2 with the base class part of Obj1. char pointers in
both the classes are private.
I tried using initializer list but could not succeed.
Is there some proper way to do this?
Thanks for any help or suggestion in advance.
|
|
|
|
|
What's wrong in having a copy c'tor in base class, too. That would take care of this.
|
|
|
|
|
Does this do what you want?
I:
1) Added +1 to all of the strlen statements used for memory allocation (strlen doesn't account for the NULL terminator - which you need to allow for when storing the string, as opposed to simply displaying it)
2) Made the data members protected
3) Uncommented the call to the Base constructor in the 2nd constructor for Derived
Code:
#include <string.h>
#include <stdio.h>
class Base
{
public:
Base(){}
Base(char * str)
{
ptr = new char[strlen(str)+1];
strcpy(ptr,str);
}
protected:
char * ptr;
};
class Derived : public Base
{
public:
Derived(char * str1,char * str2):Base(str2)
{
ptr_s = new char[strlen(str1)+1];
strcpy(ptr_s,str1);
}
Derived(const Derived & sec):Base(sec.ptr)
{
printf("Derived(const Derived &sec)\n");
this->ptr_s = new char[strlen(sec.ptr_s)+1];
strcpy(this->ptr_s,sec.ptr_s);
}
void showName()
{
printf("%s %s\n", ptr_s, ptr);
}
protected:
char * ptr_s;
};
int main(int argc, char* argv[])
{
Derived Obj1("sunil","singh");
Derived Obj2 = Obj1;
Obj1.showName();
Obj2.showName();
return 0;
}
Result:
Derived(const Derived &sec)
sunil singh
sunil singh
Process returned 0 (0x0) execution time : 0.055 s
Press any key to continue.
|
|
|
|
|
Thanks for your help.
Here in my scenario char * ptr; is private in base class.
So the Derived copy constructor statement will fail
Derived(const Derived & sec):Base(sec.ptr)
We will not be allowed to access the base class data using "sec.ptr".
So if my base class data is private, what should I do to initialize the base class data.
Thanks,
Sunil
|
|
|
|
|
How about providing a copy constructor in the base class as others have suggested?
Something like so (the displayed output is identical to my previous post):
#include <string.h>
#include <stdio.h>
class Base
{
public:
Base(){}
Base(char * str)
{
ptr = new char[strlen(str)+1];
strcpy(ptr,str);
}
Base(const Base &src)
{
this->ptr = new char[strlen(src.ptr)+1];
strcpy(this->ptr,src.ptr);
}
virtual void showName()
{
printf("%s", ptr);
}
private:
char * ptr;
};
class Derived : public Base
{
public:
Derived(char * str1,char * str2):Base(str2)
{
ptr_s = new char[strlen(str1)+1];
strcpy(ptr_s,str1);
}
Derived(const Derived & sec):Base(sec)
{
printf("Derived(const Derived &sec)\n");
this->ptr_s = new char[strlen(sec.ptr_s)+1];
strcpy(this->ptr_s,sec.ptr_s);
}
void showName()
{
printf("%s", ptr_s);
}
void showFullName()
{
showName();
printf(" ");
Base::showName();
printf("\n");
}
private:
char * ptr_s;
};
int main(int argc, char* argv[])
{
Derived Obj1("sunil","singh");
Derived Obj2 = Obj1;
Obj1.showFullName();
Obj2.showFullName();
return 0;
}
|
|
|
|
|