|
Hi,
I want to connect through proxy server, so i need to hook the send function and want to change the sending buffer (data) with my few addition .How can i do that.. i am using detours for hooking
|
|
|
|
|
Hello all,
I have a problem I just can't solve.
I have a c++ dll that returns a VARIANT of type VT_ARRAY|VT_BYREF|VT_BSTR.
following what was explained in Step by Step: Calling C++ DLLs from VC++ and VB - Part 3[^]. I turned my whole C++ process into a class.
On the vb side, I have a string array that receives the info back from the C++ dll
it is declared at a form level as private myarray() as string
The problem is that the first time I use it, it works perfectly. But if I push the button a second time, the whole process works until the dll reaches the point where it returns the variant and it just fails.
I don't understand. I'm pretty sure the class is destroyed the first time and so any value contained in the variant is deleted.
Also in vb if I unload and reload the dll every time, then it works well. So it's not an array problem on vb part (and I tried redim and erase and all). But I don't really want to load and unload every time, seems like bad programming and it creates other problems anyway.
So to summarise:
From vb6:
1-call C++ dll to create class
2-call class function to return array
3-call C++ dll to destroy class
first time works without a glinch, second time gets an error when returning the array.
Please help, it's driving me crazy.
Oh and as you might have guessed I'm a noob with C++
Many thanks
|
|
|
|
|
It might help if you reveal the exact error that causes the process to fail.
The difficult we do right away...
...the impossible takes slightly longer.
|
|
|
|
|
Hi, thank you for helping.
If I'm in vb it just crashes so not helpful and in vc the debugger gives me:
Unhandled exception in project.exe (OLEAUT32.DLL): 0xC0000005: Access Violation.
Then it stops at:
77154281 push dword ptr [eax]
So I didn't think it would be that helpful. But as I said, I'm going crazy so thanks again for helping.
|
|
|
|
|
You should attach your debugger to the application and step through the code in your DLL. Obviously something is not being tidied up properly on your first call which is causing it to crash the second time round. Also the stack trace at the time of the crash should point you back to the function in your C++ class that made the fatal call.
|
|
|
|
|
Hello,
thank you for your answer. Actually I reached the same conclusion, I think I am not properly releasing the variant array that's returned to the vb app.
But I don't know what I'm doing wrong.
inside the class it is declared:
static VARIANT pVal;
then it is initialized outside the class with:
VARIANT MYCLASS::pVal;
in my code I do
VariantInit(&pVal);
pVal.vt= VT_ARRAY|VT_BYREF|VT_BSTR;
pVal.pparray = &pSA;
return pVal;
If I'm not mistaken, the return pval; is where the problem occurs.
My deconstructor is:
MYCLASS::~MYCLASS()
{
SafeArrayDestroy(pSA);
if (pVal.vt & VT_BYREF) {
if (*pVal.pbstrVal != 0) {
::SysFreeString(*pVal.pbstrVal);
}
if (pVal.bstrVal != 0) {
::SysFreeString(pVal.bstrVal);
}
}
VariantClear(&pVal);
}
I step throught it in the debugger everything in the deconstructor is used so I really assumed it was all ok. But obviously something is still hanging around. I read that "If the variant was used to explicitly pass a pointer, i.e. (vt & VT_BYREF != 0), then free the memory pointed to." I think that's what I'm doing.
Anyway, once again, I'm so lost.
Thanks again for helping
|
|
|
|
|
I have not used VARIANT types for some years but are you sure that the VariantClear() call in your destructor is required?
|
|
|
|
|
I am 95% sure it is needed. It works with it (the first time) and deleting it doesn't solve my problem.
Actually with or without the whole cleaning up code in the deconstructor, I get the same error.
|
|
|
|
|
lenourien wrote: with or without the whole cleaning up code in the deconstructor, I get the same error.
Looks like the problem is elsewhere then; back to the debugger I'm afraid.
|
|
|
|
|
I really think there is a resource I am not freeing. The problem still happens the second time I return the variant to the vb app. When I go step by step this is were the problem occur. I just can't figure out what is it that I'm not freeing in that variant.
|
|
|
|
|
I found the problem and I am even more confused.
The problem comes from the fact that all my variables and my arrays keep the values put into them during the first run even after I delete the class. So when I do the second run, the values for the first run are still there.
Why?????!!!!!! I thought each new instance of the class will start with fresh variables and arrays...
Why isn't it happening?
|
|
|
|
|
Normally, that is true, but I have the feeling you are using 'static' for most of your variables? In that case, the variables are not bound to a single instance and thus persist as instances are created and destroyed.
Also, I think your code has problems when instantiating more than 1 instance at a time, as the static variables will be 'shared' between instances.
You should consider making pVal and pSA proper members (i.e. non-static), unless you have very good reason to. I'm guessing the caller (VB6) will need the passed string-arrays to persist even if the class instance is destroyed? In that case, making them non-static won't work.
The conclusion is, I think, that you need to seriously reconsider your class design and the lifecycle of your data. It'd help if you'd share your code, so we can assist.
Also, see my other post, about the destructor/clean-up code.
|
|
|
|
|
MicroVirus wrote: you are using 'static' for most of your variables
Yes, I missed that obvious issue; well spotted.
|
|
|
|
|
I'm not sure I understand your cleanup code.
For one thing, how/when is pSA initialised/destroyed? For another, you are passing pVal as an Array of pointers to strings. The reference is stored in pparray. Why are you then using pVal.pbstrVal? This can't be correct, unless you've changed the variant type somewhere.
The only explicit clean-up you need to do is with pSA, but since you haven't shared that part of the code we can't say much about it.
So, remove the entire pVal.vt & VT_BYREF part and verify that you are handling the allocation and cleaning of pSA correctly and at the correct time.
|
|
|
|
|
Hello all,
to answer your questions:
1- I'm using static so that the arrays are automatically initialized when declared, specifically the multi dimentional arrays. Without that I get error messages.
2- I'm using pVal.pbstrVal because it is an array of BSTR that uses VT_BYREF. According to my research releasing the variant won't release the string because it is byref so I have to do it myself. I read that in several places so I just assumed it was correct.
3-psa i initialised somewhere in the code and destroyed in the end and they both worked correctly. Here is the initialisation code:
pSA= SafeArrayCreate(VT_BSTR,1,aDim);
if (pSA != NULL) {
BSTR element = SysAllocString((BSTR)words[low]);
unsigned int length = SysStringByteLen(element);
BSTR wcElement = NULL;
wcElement = SysAllocStringByteLen(NULL, length*2);
MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,(LPCSTR)element
, -1, (LPWSTR)wcElement, length*2);
for(i = 0; i < wordcnt[low]; i++) {
if (hr= SafeArrayPutElement (pSA, &arrIndex, wcElement)){
SafeArrayDestroy(pSA);
}
SysFreeString (wcElement);
SysFreeString (element);
arrIndex++;
}
As you said (microvirus) the problem is that I declare everything as static. although it is not necessary for most I still have the problem with multidimensional arrays.
Here is an example. I have 2 arrays
int nb[81][8];
int nrnb[81];
at some point in my code I have
nb[i][nrnb[i]++] = p;
when the arrays are declared static there is no problem but if not, I get error telling me it can't be accessed.
I know I could use a for loop on those arrays and set everything to 0 and it works but I assume C++ allows for more efficient ways.
I also tried to use new int* nrnb[81] and new int** nb[81][8] but then my code refuses to use them because of pointers problems.
So the solution to the previous problem is don't use static.
The new problem is should I use a for loop on the multidimensional arrays?
Also I know everybody says multidimensional arrays are hell and it's better to use array[width x height] or use vector but I don't really want to do that, the reason being hours of rewritting the code involved and believe me, for me c++ is excruciating pain.
Thanks again
|
|
|
|
|
Your problem is mostly a conceptual one, and less of a programming problem at this point.
First you need to get your design straight. Without seeing *all* the code involved, it's a guessing game here, because there is something wrong with the lifetime of objects and the flow of the program: this encompasses all the relevant code, not just the bits and pieces you posted. However, I'm guessing it's a lot of code, so not very easy/nice to share here.
What would help you and us is if you could 'sketch' how your program works. We know that there is static data. How and where is this initialised? We know that there is a class involved: what does this class do with the data, and does it allocate/initialise (for instance in constructor, and deallocate in destructor). Furthermore, there is some kind of communication with a VB6 application. How does this work?
The problem is that there is data 'floating around' and conceptually, you haven't yet figured out who is responsible for creating it, maintaining it, how and with who it is shared and who is responsible for cleaning it up. I'm guessing somewhat here, but I believe your destructor is cleaning up more than it should (for instance: cleaning up (too much of) the static variables).
So, like I said, a sketch of what happens (without the nitty gritty details of the exact code and functions called) would be nice. I'm thinking like:
class CXyz
{
...
constructor: describe what it does with static data
and what non-static data is allocated
destuctor: ditto
void VB6Function(): describe which data it passes to VB6 and how
}
...
That way, we'll get an overview of the lifetime of the data and the class and where the data is kept. It should shed some light on what the proper lifecycles for the data is (probably, it isn't static as it is now).
I might sound a bit harsh here; if so I'm sorry. Trying to help Maybe someone else can make more sense of it than me, but I believe having the above information will help.
|
|
|
|
|
Hi again Microvirus. First of all I have to be very clear and say that you have in no way shape of form been harsh as far as I'm concerned. You're giving of your time to someone you don't know and you're doing it efficiently and nicely. Thank you.
Before I try to explain my code let me explain what I did and the problems I solved already following your advice. What you said about static variables staying around was a real eye opener so I got rid of all of them. The problem with the array not initialised befor used was easily solved by setting each value to 0 before use. I was concerned about having to use a for loop for that but there was already a for loop in which the array was involved so I just added the 0 there and it worked.
Actually I think I only have one problem left provided nothing else happens after I solve it.
So I'll explain my code and then I'll explain the problem.
What my code does is:
-type a list of words in vb
-send the list to the C++ dll
-c++ works with the word list, create a new list of words using part of the original list
-c++ also generate a string of characters and an int representing the number of words used from the original list
-c++ send the string and the int back to vb through a callback function
-c++ sends the modified word list back to vb through a return (the VT_ARRAY|VT_BYREF|VT_BSTR variable)
So as I said, now everything seems to work except for one last problen. during the second round, the list sent back to vb contains garbage characters and those characters have nothing to do with any characters I put in the list.
For example, the first time I use the list tata, toto, titi. The dll uses the whole list so it sends back tata, toto, titi. Then I try again and this time I send tata, toto, titi, tutu, tete. Once again the dll uses all of them and sends back the whole list but this time I get something like tata, totohh, titihtiti, tutu, tete.
So I think there is still something hanging around and I don't have any static around anymore.
So here is some things from my code
sorry for the very very long message
my dll receives the list from vb as a variable:
LPSAFEARRAY* StringArray
then I have:
char words[1000][26];
int wordlen[1000];
char** StrPtr = 0;
long LowerBound = 0;
long UpperBound = 0;
SafeArrayGetLBound(*StringArray,1,&LowerBound);
SafeArrayGetUBound(*StringArray,1,&UpperBound);
SafeArrayAccessData(*StringArray, (void**)&StrPtr);
for(nr_words = LowerBound; nr_words <= UpperBound; ++nr_words)
{
strcpy(words[nr_words], StrPtr[nr_words]);
}
SafeArrayUnaccessData(*StringArray);
the array words's first dimension is now filled with the word list
then I have
for(i = 0; i < nr_words; i++) {
wordlen[i] = strlen(words[i]);
}
so obviously wordlen is filled with the length of each word from the list.
after that when all the calculation is done and all I have:
LPSAFEARRAY pSA;
pSA= SafeArrayCreate(VT_BSTR,1,aDim);
if (pSA != NULL) {
BSTR element = SysAllocString((BSTR)words[low]);
unsigned int length = SysStringByteLen(element);
BSTR wcElement = NULL;
wcElement = SysAllocStringByteLen(NULL, length*2);
MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,(LPCSTR)element
, -1, (LPWSTR)wcElement, length*2);
for(i = 0; i < wordcnt[low]; i++) {
if (hr= SafeArrayPutElement (pSA, &arrIndex, wcElement)){
SafeArrayDestroy(pSA);
}
SysFreeString (wcElement);
SysFreeString (element);
arrIndex++;
}
This of course uses the word from the list and puts them in the safearray pSA.
and finally I use that safearray as I said already to send it back to vb.
It all works the first time.
I think that the problem comes from the
char** StrPtr = 0; I have at the beginning and that allow me to put the list from vb inside the words[] array in c++.
I think that one needs to be released manually but I don't know how.
I might be wrong though.
Thanks again
|
|
|
|
|
This piece of code hasn't been sitting well with me since the beginning:
lenourien wrote:
LPSAFEARRAY pSA;
pSA= SafeArrayCreate(VT_BSTR,1,aDim);
if (pSA != NULL) {
BSTR element = SysAllocString((BSTR)words[low]);
unsigned int length = SysStringByteLen(element);
BSTR wcElement = NULL;
wcElement = SysAllocStringByteLen(NULL, length*2);
MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,(LPCSTR)element
, -1, (LPWSTR)wcElement, length*2);
for(i = 0; i < wordcnt[low]; i++) {
if (hr= SafeArrayPutElement (pSA, &arrIndex, wcElement)){
SafeArrayDestroy(pSA);
}
SysFreeString (wcElement);
SysFreeString (element);
arrIndex++;
}
The problem here is that a BSTR is different from a C-string in that a BSTR knows its own length. In Visual Basic, a string can contain null (0) characters. In C, that signifies the end of a string, but in Visual Basic it can be part of the string.
MultiByteToWideChar is a function that takes and outputs normal C strings. The SysAllocString-functions take a C-string/length pair and output a BSTR. A BSTR is in fact a 2-byte unsigned integer representing the size of the string, followed by the actual unicode (16 byte) character array. The catch here is that the address the BSTR references (BSTR is a wchar_t*) is to the actual character array. If you take the pointer (a BSTR) and go back 2 bytes, you can read the size field. So, in the code above: *((unsigned short*)(wcElement - 1)) returns the size of wcElement (the actual size! Not the C-size counted to the 0-char, but the actual allocated size, which is VB wise 'the' size). SysAllocStringByteLen is a weird function, in that it puts an ANSI (1 byte) string flatly into a unicode (2 bytes) string without doing a widening conversion.
Furthermore, SysAllocString expects to be given a unicode string, and not a char*, as words is. So, your code now greatly risks reading outside the buffer. It doesn't because words at some point has two 0-chars after each other, which the Sys function recognises as a unicode 0-char and stops on time. But actually, it's reading through words way too far.
So, knowing this, you can for one conclude that the code is wrong in almost every way, but also actually that you can simplify your code a little and make it right.
What you do is you loop through each words list and use strlen to find the length of the words entry. Then you use MultiByteToWideChar to convert to unicode. Then you use SafeArrayPutElement to store the converted string. And this you repeat for every entry of words.
for (i = lowerBoundary; i <= upperBoundary; i++)
{
size_t len = strlen(words[i]); BSTR element = SysAllocStringLen(NULL, len); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, words[i], len, (LPWSTR)element, len);
SafeArrayPutElement(pSA, &i, element);
SysFreeString(element);
}
I think this code should work correctly, but you should check this and see how it fits into the rest yourself.
Finally, in the first piece where you receive StringArray you are taking into account the lower and upper boundary of the array, but then in the rest of the code you assume lower boundary is 0. This is a bug you'll need to fix as well.
At any rate, I'm fairly sure this is where your current problem is stemming from: from SysAllocString copying too many bytes from words[low].
|
|
|
|
|
Where's the bowing down and low smiley here?????
Your code not only worked perfectly it also got rid of a nasty several seconds freeze on the vb side while the dll was freeing memory.
I don't know how to thank you (it's not like my coding skills are ever going to help you although if you're ever interested I'm much better with html & css )
For the lower and upper bound that I check at the beginning, I agree I should do something about that and it would probably be safer to do it but the truth is that the lower boundary here is always 0 as I just let the arrays do their work normally without ever specifying the lower boundary myself. But I want to learn so you're right I'll implement it.
So many thanks again
|
|
|
|
|
You're welcome
Good luck with the rest of your program. I'm guessing you learned quite a bit already from this, seeing as how you solved most of your problems with only a gentle nudge on our side. Keep learning and you'll be lecturing me soon
|
|
|
|
|
fingers crossed
|
|
|
|
|
My pc has Windows 7.
Iam creating connection to a terminal server, using WTSOpenServer() API.
And with the created handle, if i try to transmit message to any other machine other than mine using WTSSendMessage() API, iam getting the GetLastError() as "RPC server unavailable".
can anyone of you pls suggest what would be the solution for this.
Thanks,
Arun P.
|
|
|
|
|
Immediate suggestions that come to mind:
- Try disabling any and all firewalls on both PCs.
- Check the RPC services are all enabled and started and functioning properly (You'll have to look these up.)
- Try some of the Microsoft samples first and see if they run without problem.
|
|
|
|
|
Hi
i have a little knowledge about VC++.
My question is how i can convert a simple ado data type to c++ data type. for example if i want to know the number of rows in a sql table, i should run a sql command like "select count(*) from table tbl", but how i can store the answer of this query in a c++ integer data type?
thanks
|
|
|
|
|
Once you exectue the query, the count will be the data value in the first (only) row, first column of the resultant recordset. So, you will get the data value of the first field as a _variant_t.
Pseudocode:
// Assume you executed: select count(*) as mycount from table
pField = recordSet->Fields->GetItem(_variant_t("mycount"));
_varint_t val = pField->GetValue();
int count = val.iVal;
John
|
|
|
|
|