question

knotslanding-0152 avatar image
0 Votes"
knotslanding-0152 asked knotslanding-0152 commented

Common Dialog Save As works in Access 32-bit but not in Acess 64-bit - GetSaveFileName API; CommDlgExtendedError error code 1; LenB does not solve the problem

Hello, I am writing for some help. Sorry for the length of my message but I didn't want to leave anything out. I just hope I am clear. Any help is most appreciated. Thanks in advance. I am using a user defined common dialog box class (which I named CommonDialogAPI) to display a Save As dialog box in an Access mdb (not accdb) database. (After giving the file a name or accepting the default name I give it in code and the location to where to save the file, data (from a query) is exported and save to that file.) We have users who have Office 2010 32-bit and Office 2010 64-bit. This work without any problems for users who have Access 2010 32-bit but does not work for users who have Access 2010 64-bit. Let me give some background: When I run the code to open the Save As dialog box in Access 2010 32-bit, it works great and has no problems. However, when I run the same code on a computer with Access 2010 64-bit, it doesn't work as expected. The Save As file dialog box isn't displayed. However, the error code that I am getting after calling CommDlgExtendedError API is code 1. I am guessing error code 1 (or 0x0001), but I could be wrong, refers to CDERR_STRUCTSIZE 0x0001 "The lStructSize member of the initialization structure for the corresponding common dialog box is invalid." I am using the API called GetSaveFileName. In addition, I also used the Win32API_PtrSafe.TXT where I copied the declarations and types from. I have made the following modifications. 1. Added the attribute PrtSafe to the Declare statements. It is required on the Access 2010 64-bit machine; otherwise you get a compile error. 2. Updated Long datatype to LongPtr datatype if it refers to a memory address. 3. Left Long datatype as is if it refers to a value. 4. Added conditional compilation #If Win54 #Else #End to distinguish if the user has 32-bit Office/Access or 64-bit Office/Access. Later, I may add a VBA7 check (which is only available in Access 2010) for users who have an earlier version of Access. For now, I just need to get this working with Access 2010 64-bit. 5. For testing purposes, I added a message box in code to show the error returned from CommDlgExtendedError API. The code is 1. I am trying to determine why the Save As file dialog box isn't shown and this error if I am not mistaken provides some incite. Here is my question. How do I resolve the following error after calling the API CommDlgExtendedError on a 64-bit Office computer (okay on a 32-bit office computer)? CDERR_STRUCTSIZE 0x0001 "The lStructSize member of the initialization structure for the corresponding common dialog box is invalid." It doesn't seem to matter but I changed the following when I initialize the OpenFileName object. With OpenFile .lStructSize = Len(OpenFile) ... changed to With OpenFile .lStructSize = LenB(OpenFile) Here is my code (copied and pasted) with the declare statements, type, and the call to the API from my CommondDialog class. Option Compare Database #If Win64 = True Then 'Office 2010 compatible with both 32-bit and 64-bit - for specific 64-bit office 2010 users 'updated memory addresses to longPtr Private Type OPENFILENAME lStructSize As Long hwndOwner As LongPtr hInstance As LongPtr lpstrFilter As String lpstrCustomFilter As String nMaxCustFilter As Long nFilterIndex As Long lpstrFile As String nMaxFile As Long lpstrFileTitle As String nMaxFileTitle As Long lpstrInitialDir As String lpstrTitle As String Flags As Long nFileOffset As Integer nFileExtension As Integer lpstrDefExt As String lCustData As Long lpfnHook As LongPtr End Type Private Declare PtrSafe Function GetOpenFileName Lib "comdlg32.dll" Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long Private Declare PtrSafe Function GetSaveFileName Lib "comdlg32.dll" Alias "GetSaveFileNameA" (pOpenfilename As OPENFILENAME) As Long Private Declare PtrSafe Function CommDlgExtendedError Lib "comdlg32.dll" () As Long Private Declare PtrSafe Function GetLastError Lib "kernel32" () As Long #Else 'Earlier versions before Office 2010 and 32-bit office Private Type OPENFILENAME lStructSize As Long hwndOwner As Long hInstance As Long lpstrFilter As String lpstrCustomFilter As String nMaxCustFilter As Long nFilterIndex As Long lpstrFile As String nMaxFile As Long lpstrFileTitle As String nMaxFileTitle As Long lpstrInitialDir As String lpstrTitle As String Flags As Long nFileOffset As Integer nFileExtension As Integer lpstrDefExt As String lCustData As Long lpfnHook As Long lpTemplateName As String End Type 'PtrSave is required as it won't compile on a 64-bit Office computer Private Declare PtrSafe Function GetOpenFileName Lib "comdlg32.dll" Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long Private Declare PtrSafe Function GetSaveFileName Lib "comdlg32.dll" Alias "GetSaveFileNameA" (pOpenfilename As OPENFILENAME) As Long Private Declare PtrSafe Function CommDlgExtendedError Lib "comdlg32.dll" () As Long Private Declare PtrSafe Function GetLastError Lib "kernel32" () As Long #End If Private mstrFileName As String Private mblnStatus As Boolean Public Property Let GetName(strName As String) mstrFileName = strName End Property Public Property Get GetName() As String GetName = mstrFileName End Property Public Property Let GetStatus(blnStatus As Boolean) mblnStatus = blnStatus End Property Public Property Get GetStatus() As Boolean GetStatus = mblnStatus End Property Public Function SaveFileDialog(lngFormHwnd As Long, lngAppInstance As Long, strInitDir As String, strFileFilter As String, strDefaultFileName As String) As Long Dim SaveFile As OPENFILENAME Dim X As Long With SaveFile .lStructSize = LenB(SaveFile) .hwndOwner = lngFormHwnd .hInstance = lngAppInstance .lpstrFilter = strFileFilter .nFilterIndex = 1 .lpstrFile = String(257, 0) .nMaxFile = Len(SaveFile.lpstrFile) - 1 .lpstrFileTitle = SaveFile.lpstrFile .nMaxFileTitle = SaveFile.nMaxFile .lpstrInitialDir = strInitDir .lpstrTitle = "Save As" .Flags = 0 End With 'update the default file name SaveFile.lpstrFile = strDefaultFileName & String(257 - Len(strDefaultFileName), 0) 'fixed length string padded with nulls X = GetSaveFileName(SaveFile) If X = 0 Then Dim retval As Long retval = CommDlgExtendedError() MsgBox "Common Dialog errror CODE " + CStr(retval) End If If X = 0 Then mstrFileName = "none" mblnStatus = False Else 'mstrFileName = Trim(SaveFile.lpstrFile) 'revsion mstrFileName = Trim((Left(SaveFile.lpstrFile, InStr(1, SaveFile.lpstrFile, vbNullChar) - 1))) mblnStatus = True End If End Function ' 'This class method is unused ' Public Function OpenFileDialog(lngFormHwnd As Long, lngAppInstance As Long, strInitDir As String, strFileFilter As String) As Long Dim OpenFile As OPENFILENAME Dim X As Long With OpenFile .lStructSize = Len(OpenFile) .hwndOwner = lngFormHwnd .hInstance = lngAppInstance .lpstrFilter = strFileFilter .nFilterIndex = 1 .lpstrFile = String(257, 0) .nMaxFile = Len(OpenFile.lpstrFile) - 1 .lpstrFileTitle = OpenFile.lpstrFile .nMaxFileTitle = OpenFile.nMaxFile .lpstrInitialDir = strInitDir .lpstrTitle = "Open File" .Flags = 0 End With X = GetOpenFileName(OpenFile) If X = 0 Then mstrFileName = "none" mblnStatus = False Else mstrFileName = Trim(OpenFile.lpstrFile) mblnStatus = True End If End Function Lastly, I have a command button on a form that calls SaveFileDialog that passes the parameter values like. lngResult = cdlg.SaveFileDialog(lngFormHwnd, lngAppInstance, strInitDir, strFileFilter, strDefaultFileName) I am exporting a query (named QExportToExcelCustDBUnpaid) results to an Excel file. Here is the code and it again works in Access 2010 32-bit just not in Access 2010 64-bit. Private Sub cmdSaveFile_Click() Dim cdlg As New CommonDialogAPI Dim lngFormHwnd As Long Dim lngAppInstance As Long Dim strInitDir As String Dim strFileFilter As String Dim lngResult As Long Dim strDefaultFileName As String lngFormHwnd = Me.Hwnd lngAppInstance = Application.hWndAccessApp 'strInitDir = "C:\" 'random strFileFilter = "Excel Files (*.xls)" & Chr(0) & "*.xls" & Chr(0) strDefaultFileName = "QExportToExcelCustDBUnpaid" lngResult = cdlg.SaveFileDialog(lngFormHwnd, _ lngAppInstance, strInitDir, strFileFilter, strDefaultFileName) If cdlg.GetStatus = True Then DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel8, "QExportToExcelCustDBUnpaid", cdlg.GetName, False MsgBox "Exported QExportToExcelCustDBUnpaid to " & cdlg.GetName, vbInformation, "Tricor Main Menu" End If 'release resource (object variable) If Not cdlg Is Nothing Then Set cdlg = Nothing End If End Sub The problem is in the line X = GetSaveFileName(SaveFile) which returns zero. Normally, a zero is returned when the user clicks cancel, but in my case the Save File Dialog isn't being shown so I am not clicking the cancel button. So why isn't this working? And why am I getting error code 1 after calling CommDlgExtendedError API? How to I resolve the .lStructSize if I am reading the error correctly. In the immediate (debug) window, the value of len(OpenFile) is 120 on the Access 2010 64-bit machine but len(OpenFile) is 76 on the Access 2010 32-bit machine. If I can't get this to work, then an option is to uninstall Office 2010 64-bit and then install Office 2010 32-bit which I know will fix the problem but I wanted to see if I can resolve this issue to work with both 32-bit and 64-bit Access 2010. I believe there is a way for the code to be compatible but so far it isn't working in Access 2010 64-bit but does in Access 2010 32-bit. Any help is most appreciated. Thanks in advance.

windows-api
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.


Hello,

It looks like my message doesn't look the same as when I previewed it with the code lines numbered and the paragraphs separated. It appears as one very long paragraph which is very hard to read. Let me know if you would like me to try again.

I am not sure why it did this to my message as it looked fine when I previewed it.

In addition, if I posted to the wrong group or tag, please also let me know. Thanks.




0 Votes 0 ·
MotoX80 avatar image
0 Votes"
MotoX80 answered

Yes, it's almost impossible to read, Especially the code. But i get the general idea. Have you seen these links?

https://stackoverflow.com/questions/1091484/show-open-file-dialog/1101541#1101541

https://docs.microsoft.com/en-us/office/vba/api/Access.Application.FileDialog

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

knotslanding-0152 avatar image
0 Votes"
knotslanding-0152 answered

Hello,

Thanks for your suggestions.

I appreciate it. I am attaching a pdf file of the text and code I tried to post. Tthe link to the uploaded file is at the bottom of this message.


Let me know if you can read this and understand what I am trying to ask. I know my original posting is very hard to read and it didn't look at all like it was when I previewed it. Sorry.


I know it is very wordy but I didn't want to leave anything out. It is always better to provide information.


As to using Application.FileDialog(3), I will give it a try but that will require rewriting my code and not using the API GetSaveFileName.

According to https://docs.microsoft.com/en-us/office/vba/api/Access.Application.FileDialog in the Remarks section, it says, "The msoFileDialogOpen and msoFileDialogSaveAs constants are not supported in Microsoft Access." I also tried replacing msoFileDialogSaveAs with 3 but it didn't work. I already set a reference to Microsoft Office 11.0 Object Library. In any case, I will need to do some testing. And more importantly, will using Application.FileDialog(3) work in both 32-bit and 64-bit Office / Access.


Just to reiterate, the API GetSaveFileName works fine in Access 2010 32-bit and the Save As dialog appears as expected. However, in Access 2010 64-bit, the Save As dialog does not display but I was able to get the error code after calling the API CommDlgExtendedError on a 64-bit Office computer. See below. There is no error when ran on the 32-bit Office computer.

CDERR_STRUCTSIZE 0x0001
"The lStructSize member of the initialization structure for the corresponding common dialog box is invalid."


So, the main question is how do I resolve this error "The lStructSize member of the initialization structure for the corresponding common dialog box is invalid". Believing this might be the solution, I already changed

.lStructSize = Len(SaveFile)

To

.lStructSize = LenB(SaveFile)

It still didn't work. Maybe I am doing something wrong. Thanks in advance.

79735-save-as-common-dialog-64-bit-office-does-not-displ.pdf



5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

MotoX80 avatar image
0 Votes"
MotoX80 answered

If you can use "Application." that would be the best since you can let Access handle the 32/64 differences.

Otherwise it looks like you'll have to test see if you are executing on a 32 or 64 bit OS.

https://stackoverflow.com/questions/6256140/how-to-detect-if-the-computer-is-32-bit-or-64-bit

This site shows that you have to declare the functions differently.

https://social.msdn.microsoft.com/Forums/en-US/1db77ef4-f363-4e60-b680-6cf1c44e22bd/office-32bit-vs-64bit-comdlg32dll

You'll have to distinguish between the names so try adding 32/64 like this. I'm sorry, I don't have an easy way to test this.


     Declare Function GetOpenFileName32 Lib "comdlg32.dll" Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long
        
     Declare Function GetSaveFileName32 Lib "comdlg32.dll" Alias "GetSaveFileNameA" (pOpenfilename As OPENFILENAME) As Long
    
     Declare PtrSafe Function GetOpenFileName64 Lib "comdlg32.dll" Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long
        
     Declare PtrSafe Function GetSaveFileName64 Lib "comdlg32.dll" Alias "GetSaveFileNameA" (pOpenfilename As OPENFILENAME) As Long



5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

knotslanding-0152 avatar image
0 Votes"
knotslanding-0152 answered MotoX80 commented

Hi,


Thanks for your help. The Declare statements I used came directly from the Win32API_PrtSafve text file.


To show you what I am referring to here is Microsoft's general information on this issue:


https://docs.microsoft.com/en-us/office/troubleshoot/office-suite-issues/win32api_ptrsafe-with-64-bit-support


This webpage also has a link to where you can download the Win32APUI-PrtSafe with 64-bit Support text file. That is where I got the file from. When you run the installer after downloading the file form the link, it does not tell you where it installed the information. Look in this -new- folder on your C drive:


C:\Office 2010 Developer Resources\Documents\Office2010Win32API_PtrSafe


If I am reading this correctly, the Declare statement related to GetSaveFileName in the Win32API_PrtSafe.txt file contains updated declarations and types. Of course, they could also be wrong. I am not sure if it is a bug. But I would hope they are correct.


The GetSaveFileName as described in Win32API_PrtSafe refers to Lib "comdlg32.dll" which is why I didn't change it to something like Lib "comdlg64.dll"


The only thing I didn't do was include the last 3 items in the type definition as I don't use them. Including them also didn't resolve the problem. Just to clarify here are those 3 items which I copied from the Win32API_PrtSafe.txt file. As a second test, I also used a Win API Viewer that showed the same thing.

'#if (_WIN32_WINNT >= 0x0500)
pvReserved As LongPtr
dwReserved As Long
FlagsEx As Long
'#endif // (_WIN32_WINNT >= 0x0500)
End Type



I also looked at the webpage


https://social.msdn.microsoft.com/Forums/en-US/1db77ef4-f363-4e60-b680-6cf1c44e22bd/office-32bit-vs-64bit-comdlg32dll


and the declarations are the same except mine are marked as private instead. If I delete the Private attribute, I get the following compile error (32-bit office):


"Compile error: Private Enum and user defined types cannot be used as parameters or return types for public procedures, public data members, or fields of public user defined types."


That is why I added the attribute Private to my Declares i.e. scope.



Anyway, to get back to my original question, I still don't know why or what is the cause of the error code 1 that is being returned from the call to the API CommDlgExtendedError (on 64-bit Office; this does not occur on a computer with 32-bit Office)



Here is more information on what I have read about the API CommDlgExtendedError. Again, just providing it to show you what I believe is the main problem:


http://www.jasinskionline.com/WindowsApi/ref/c/commdlgextendederror.html



And the error as described on this webpage was:


CDERR_STRUCTSIZE = &H1
The function was provided with an invalid structure size



I am not completely sure what the ampersand or the H stand for before the number 1 but I am guessing 1 (as a decimal) is the same &H1 (guessing hexadecimal).


Does this seem to be on the right track or I could be mistaken.



Of course, it could be something I am overlooking. Just remember I already used LenB function on the following line of code.


With OpenFile
.lStructSize = LenB(OpenFile)




This was similar to another user who had the same problem I am having but in that case the solution was:


"Try using sizeof (OPENFILENAME) for the lStructSize member"


The closest thing in VBA7 (in Access 2010) is using the LenB function. VBA7 started with Office 2010.


By the way, this webpage on the GetSaveFileName API also uses Len but not LenB. Either way, it doesn't seem to resolve my problem:


http://www.jasinskionline.com/WindowsApi/ref/g/getsavefilename.html


I also tried adding the following 2 items and the fields in the type to see if it worked after adding it to my type definition:

.lCustData = 0
.lpfnHook = 0

It still doesn't work on the Office 64-bit machine but it does work as it always has been on the Office 32-bit machine. Error code 1 is still being returned by the call to the API CommDlgExtendedError.



This is what I believe is the problem. I just don't know how to resolve it on the 64-bit office computer.



Anyway, thanks again. Just wanted to give an update.

· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I'm sorry, I don't think that I can help you. I've done a lot of VBA with Excel over the years when I was still working, but now I have LibreOffice on this laptop and a very old copy of Office XP on my desktop. I have no way to test this.

You have tagged your question as winapi-general. I would recommend adding other office tags to reach out to forum users who might have specific experience in this area.

office-vba-dev
office-access-dev
office-itpro
office-excel-itpro
office-word-itpro
office-outlook-itpro


I would also suggest that you use the Code Sample icon (the 101010) on this web site to post a scaled down version of your code that just tests the call that fails. Something that would be easy for a forum user to just copy and paste and run.

I still think that if you can use Application. calls, that would be best.

Good luck.





0 Votes 0 ·
knotslanding-0152 avatar image
0 Votes"
knotslanding-0152 answered knotslanding-0152 commented

Hello again,

Thanks for the suggestion to use the Application.FileDialog.


I had used the Application.FileDialog solution and it works in both 32-bit and 64-bit Access. The only disadvantage is that it can't apply a filter to the file types. That is why I prefer using the GetSaveFileName API.


I just wish I was able to determine the solution to the call to the API CommDlgExtendedError which returns an error code 1 when ran on a 64-bit Office computer. After calling the GetSaveFileName API on a computer with Office 64-bit, the Save As dialog box doesn't show or display due to this error. It isn't a runtime error. And I already changed the lenB(.lstructsize) in my code but that didn't solve the problem.


It does though run without any problems on a 32-bit Office computer just not on a 64-bit Office computer.


If anyone knows how to determine the cause of this error from the API CommDlgExtendedError, that would be great and make my day. For further and more detailed information, please see my earliest posts and the uploaded PDF file.


Thanks.

· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hello, @knotslanding-0152

It does though run without any problems on a 32-bit Office computer just not on a 64-bit Office computer.

According to your description, GetSaveFileName has a problem in Acess 64-bit. Is the error only on Acess software or all software on 64-bit operating system?

0 Votes 0 ·

Hello,

I am only using this API in both Access 2010 32-bit and Access 2010 64-bit. So I can't tell if this GetSaveFileName API only has problems in Access or other 64-bit software. I only need to use it in Access.

For now, though, the Application.FileDialog solution works perfectly in both Access 32-bit and Access 64-bit as a workaround to the GetSaveFileName.

Thanks.

0 Votes 0 ·