Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

memory overflow errors 3

Status
Not open for further replies.

jordanking

Programmer
Sep 8, 2005
351
Hello,

I am using VBA in access 2003.

I have a lengthy procedure that makes calls to several functions defined in seperate modules. Does the memory space used by the external function's variables get freeed once the call to the function is complete? Or do I need to manually release the vraibles memory space usage once the procedure is finished with them?
 
You may Erase the dynamic arrays and set the objects to Nothing.

Hope This Helps, PH.
Want to get great answers to your Tek-Tips questions? Have a look at FAQ219-2884 or FAQ181-2886
 
Memory allocated to variables, and objects created by, and only referenced from within, the function should be freed by the system when the function ends. Unless you have reason to believe this isn't happening then you shouldn't need to do anything specific.

Enjoy,
Tony

------------------------------------------------------------------------------------
We want to help you; help us to do it by reading this: Before you ask a question.

Professional Office Developers Association
 
thanks for the reply,

by erase the dynamic array do you mean redim them?
and by objects, you mean recordsets etc..

what about simple varaibles like:
dim i as integer
or
dim strTemp as string

here is an example of an array i use.
this is declared publically,
Code:
Public Type Buffer
    Data(1999) As Byte
End Type
then within the individual modules I declare an instance of this array type
Code:
Dim dtbuffer As Buffer
 
thanks tony jollans,

I might need to give more bakcground.

I am running a Windows xp PC and Access 2003. I have a procedure called from a form that is rather lengthy. It interfaces with an external accounting software program. I had to update my procedure as the accounting software was updated as well. The previous version works fine, but I am having trouble with my new version. The new code has less lines and variables and runs faster, however, it crashes. The procedure gathers data from access and exports "invoices" into the accounting software. It is heavily reliant on API calls; one set works with the accounting database and the second primarily used api is as follows:
Code:
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
The procedure works great with just one or two "invoice" exports. But the procedure is intended to push between 500 and 1000 invoices into the accounting software. This procedure is crashing at random points. It causes access to throw a modal form that says the application has to be closed. It then tries to create a backup etc....

I think that this is a memory error, so...

I commented out all of the calls to the external functions, which is where the vast majority of the "CopyMemory" calls exist. Then everything works fine.
There are 17 external functions with 3 to 20 calls to this windows api each, and these are all called numerous times for each of the 500 - 1000 records. (I can post an example if you like.) Is it possible that this is somehow loading up the memory? I checked the "task manager" while the procedure is running and it is at arounf 26,000 K. I don't know if that helps at all.

I also have a few nested loops which i know can slow things down, might that be the issue?

I realise that this is vague, so thanks for your help,

JK
 
(a) I am not an expert in this area
(b) I would need to see a lot more code to be able to even try to isolate any problem

In general, neither built-in housekeeping nor your own freeing of variables can be guaranteed to free up memory used like this. It depends where you get the memory from in the first place. If you are allocating it with API calls you will need to explicitly free it with API calls as well. If you are accidentally writing off the end of the memory you think you are using you can cause untold damage. You are, essentially, working without a safety net and outside the (relatively) safe confines of VBA and must be responsible for all that you do. I'm afraid there is no easy answer and good old-fashioned debugging is called for.

Enjoy,
Tony

------------------------------------------------------------------------------------
We want to help you; help us to do it by reading this: Before you ask a question.

Professional Office Developers Association
 
The software that i am interfacing with provides API's for working with records in an external database. Each of these records need to be passed to the API as a byte array that is 2000 bytes long. Each field of the record has a specific start and end point in the byte array. Therefor I use the copymemory function to place the information I need at specific points in a byte array.

Code:
Option Explicit
Public Function CreateITLULi(ByRef pITLULi As Buffer, ByVal idsITRec As Long, ByVal z As Integer, ByVal sItmCode As String, ByVal sItmDetail As String, ByVal dblAmt As Double, ByVal dblPrice As Double, ByVal dtLine As Date)

Dim strTemp As String
Dim strTemp2 As String
Dim blank As Buffer
Dim lCode As Long
Dim lDesc As Long
Dim lngDec As Long
Dim lngDec2 As Long
Dim bytCode(52) As Byte
Dim bytDesc(255) As Byte
Dim dblTax As Double
Dim sUnit As String
Dim strTemp3 As String
Dim lUnit As Long
Dim lngDec3 As Long
Dim bytUnit(15) As Byte
Dim lTaxRev As Long
Dim lTaxCode As Long
Dim sLineDate As String
Dim lLgth As Long

Dim iTemp0 As Integer
Dim iTemp1 As Integer
Dim dTemp0 As Double

Dim i As Integer

iTemp0 = 0
iTemp1 = 1
dTemp0 = 0


sLineDate = CStr(Format(dtLine, "dd/mm/yyyy"))
sItmDetail = sLineDate & " -- " & sItmDetail

strTemp = ""
lCode = Len(sItmCode)
For i = 0 To (lCode - 1)
    strTemp = Mid(sItmCode, (i + 1), 1)     'Returns character at position
    lngDec = DecimalCode(strTemp)      'Custom function that returns the decimal value of a ascii chracter
    bytCode(i) = CByte(lngDec)
Next
lLgth = 0

strTemp2 = ""
lDesc = Len(sItmDetail)
For i = 0 To (lDesc - 1)
    strTemp2 = Mid(sItmDetail, (i + 1), 1)     'Returns character at position
    lngDec2 = DecimalCode(strTemp2)      'Custom function that returns the decimal value of a ascii chracter
    bytDesc(i) = CByte(lngDec2)
Next
lLgth = 0

sUnit = "Each"
strTemp3 = ""
lUnit = Len(sUnit)
For i = 0 To (lUnit - 1)
    strTemp3 = Mid(sUnit, (i + 1), 1)     'Returns character at position
    lngDec3 = DecimalCode(strTemp3)      'Custom function that returns the decimal value of a ascii chracter
    bytUnit(i) = CByte(lngDec3)
Next

dblTax = Math.Round((dblAmt * (0.06)), 3)
lTaxRev = 21
lTaxCode = 12

Call CopyMemory(pITLULi.Data(0), idsITRec, 4)  '' itrecid number
Call CopyMemory(pITLULi.Data(4), z, 2)   'line number
Call CopyMemory(pITLULi.Data(6), bytCode(0), 52)
Call CopyMemory(pITLULi.Data(58), bytUnit(0), 15)
Call CopyMemory(pITLULi.Data(73), iTemp1, 2)
Call CopyMemory(pITLULi.Data(75), bytDesc(0), 255)
Call CopyMemory(pITLULi.Data(330), dblAmt, 8)
Call CopyMemory(pITLULi.Data(338), dTemp0, 8)
Call CopyMemory(pITLULi.Data(346), dTemp0, 8)
Call CopyMemory(pITLULi.Data(354), dblPrice, 8)
Call CopyMemory(pITLULi.Data(362), dTemp0, 8)
Call CopyMemory(pITLULi.Data(370), dTemp0, 8)
Call CopyMemory(pITLULi.Data(378), iTemp0, 2)
Call CopyMemory(pITLULi.Data(380), lTaxCode, 4)
Call CopyMemory(pITLULi.Data(384), lTaxRev, 4)
Call CopyMemory(pITLULi.Data(388), dblTax, 8)
Call CopyMemory(pITLULi.Data(396), blank.Data(0), 1604)

the following code is a public declaration in another module that outlines the "buffer" variable type

Code:
Public Type Buffer
    Data(1999) As Byte
End Type

if a record in the databse only uses 396 bytes, the record needs to be padded with "blanks" for the remianing 1604 bytes to make a complete 2000 byte record.

this above function is one of the external functions that is called by the larger procedure. The larger procedure queries the access tables to get the data and then passes it to these functions, these functions return the completed record to the procedure which then inserts the record into the external database.

Is there an api call that clears the byte array that I have built?
 
the record needs to be padded with "blanks" for the remianing
I'd double check ALL lines like this:
Call CopyMemory(pITLULi.Data(X), blank.Data(0), Y)
to be sure that X+Y always = 2000

Hope This Helps, PH.
Want to get great answers to your Tek-Tips questions? Have a look at FAQ219-2884 or FAQ181-2886
 
Certainly I would check as PHV says. I would go further and double check whether your external software might be using any of the values you give it to trigger it to write off the end of the buffer - in fact, if you have a new version of the accounting software, are you quite sure that it isn't at fault?. I'd also like to know where and how pITLULi is defined.

If you can, try making all the data areas you pass longer, perhaps 4000 bytes, and see if that helps. You can, and presumably the accounting package will, ignore all beyond the first 2000 bytes but the extra will allow 'safe' space should there be a small overflow in error.

There may be good reason for using the APIs but I don't really see it in this code. You're not doing anything that couldn't be done in 'normal' VBA. Why do you need to do it this way?

Enjoy,
Tony

------------------------------------------------------------------------------------
We want to help you; help us to do it by reading this: Before you ask a question.

Professional Office Developers Association
 
If you create a user-defined type in VBA, is there any way to map this on to the buffer, so you can use symbolic references to the contents rather than explicit positions? Kind of like setting a pointer for a C struct or establishing addressability for an assembler DSECT?

The VBA help states
User-defined types are often used with data records, which frequently consist of a number of related elements of different data types.
On the face of it, this sounds like what you need. It then goes on to give a really trivial example which doesn't really help.

Is there a VBA equivalent to setting a pointer to a struct?

Steve

[small]"Every program can be reduced by one instruction, and every program has at least one bug. Therefore, any program can be reduced to one instruction which doesn't work." (Object::perlDesignPatterns)[/small]
 
Is there a VBA equivalent to setting a pointer to a struct?
No. :(

It is generally possible to get (but not set) a pointer to a variable (VarPtr, etc.), which might be enough, it depends on the interface.

BTW - I can't recall DSECTs ever being mentioned here before :)

Enjoy,
Tony

------------------------------------------------------------------------------------
We want to help you; help us to do it by reading this: Before you ask a question.

Professional Office Developers Association
 
I can't recall DSECTs ever being mentioned here before :)
Oops... showing my age (and my roots) [smile]

Steve

[small]"Every program can be reduced by one instruction, and every program has at least one bug. Therefore, any program can be reduced to one instruction which doesn't work." (Object::perlDesignPatterns)[/small]
 
thanks, everyone, for the posts.

PHV, you were right, I found a few places where the math was wrong, however it did not fix the problem.

Tony J
here is the definition you asked for, it is declared in the main procedure that calls the function I posted above:
Code:
Dim pITLULi As Buffer
here is the declaration of the buffer data type (found in another module):
Code:
Public Type Buffer
    Data(1999) As Byte
End Type

I am not an expert, so i am not sure what you mean when you say there is a way to do this with "Normal VBA".

I need to pass the byte values of the variables to a byte array. So i use the copymemory function because I do not know another way to retrieve the byte value of (for example) a double type variable and pass those bytes into a byte array at a specific point. Is there another way to do this?

I will try increasing the buffer size like you suggested and let you know. I also am a little confused by what you call writting off memory. What is that?


JK
 
Without knowing exactly what your accounting package requires it's difficult to comment - does it say it needs a byte array or is that simply a mechanism to pass a 2000-byte area of memory? Unfortunately, unless you know how VBA stores different types of variable, it is awkward (actually even if you do know, it is still awkward) to build up a memory area in the correct format for an external non-VBA structure and using a byte array is a common practice (and by no means wrong).

The working theory (mine and others here, I think) is that CopyMemory is a very blunt instrument. It will overwrite the memory you tell it to whether or not that memory belongs to your code so if you give it a wrong memory address or a longer length than you want you can easily overwrite parts of Access's own memory perhaps (and that is what I meant by writing off the end of the buffer) which could then cause Access a problem. If you are sure you are not doing that (having checked what PHV told you to) and still have a problem it could be almost anything.

I know that's not a lot of help but it really is an issue for good old debugging - checking every step of the way till you find where it fails and hope to identify why - of course, the error might not be yours at all if you have upgraded your package. Clearly, code you have changed is more suspect than code you haven't. Not sure what else I can say at the moment.



Enjoy,
Tony

------------------------------------------------------------------------------------
We want to help you; help us to do it by reading this: Before you ask a question.

Professional Office Developers Association
 
thanks tony,

I knew it was a little difficult to nail down. I am going to have to step through and isolate one line at a time. Yippeee.

I apprecate the effort, I will post the solution (if found) when I get it.

JK
 
For primitive data types, I don't see why you can't make your user-defined type a bit more user-defined:
Code:
Public Type Buffer
   idsITRecas Long
   z as Integer
   bytCodes(51) as Byte
   bytUnits(14) as Byte
   iTemp1 as Integer
   bytDesc(254) as Byte
   dblAmt as Double
...
End Type
If you want to use 1-based arrays you can do that, but these are zero-based. You can verify you have the length of your buffer correct with Len(VarDeclaredAsTypeBuffer).

Now you can just set the data of each member and do a CopyMemory only once of the entire deal. I bet it runs faster, too...

Or, if you still need to use your original type, then declare a new one as I outlined above, and do the copymemory between them.
 
E^2,

As I said earlier ...
Tony Jollans said:
Unfortunately, unless you know how VBA stores different types of variable, it is awkward (actually even if you do know, it is still awkward) to build up a memory area in the correct format for an external non-VBA structure

The byte arrays in your structure definition will actually be pointers, not data, and the whole structure will not map properly.

Enjoy,
Tony

------------------------------------------------------------------------------------
We want to help you; help us to do it by reading this: Before you ask a question.

Professional Office Developers Association
 
TonyJollans said:
The byte arrays in your structure definition will actually be pointers, not data, and the whole structure will not map properly.

This is not true
 
Ooops, posted before finishing.

However, data in VB UDTs gets packed on 4-byte boundaries using vaguely arcane rules, so - as TonyJollans says - unless you know exactly where the packing is occuring the structure will not map properly.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top