Oracle External Procedures with Delphi DLL - oracle

I have been trying to configure the "External Procedures" Feature for my Oracle Database 18c on Windows 10.
The documentation claims, to set up this feature, it is basically just necessary to set the environment values in extrproc.ora - which in my case is just one line of: SET EXTPROC_DLLS=ANY (for testing purposes).
I have created a DLL to test my configuration by using Delphi with the following code:
library testdll;
function Sum(x, y: Integer): Integer; stdcall;
begin
Result := x + y;
end;
function Subtract(x, y: Integer): Integer; stdcall;
begin
Result := x - y;
end;
procedure TEST; stdcall;
begin
end;
exports
TEST,
Sum,
Subtract;
begin
end.
So my first question would be: Do I need to compile the DLLs as x64 or as x86? Also, is the calling convention stdcall compatible with Oracle 18c?
Furthermore I have created a Library Alias in my database using:
CREATE OR REPLACE LIBRARY MySchema.TESTDLL AS 'C:\testdll.dll'
The external procedure has been published by
create or replace PROCEDURE TESTPROCEDURE
AS LANGUAGE C
NAME "TEST"
LIBRARY TESTDLL;
Now that I have everything setup on the database, I tried calling the procedure:
begin
TESTPROCEDURE();
end;
But when I tried calling it, I get the following Error:
ORA-06520: PL/SQL: Fehler beim Laden der externen Library
ORA-06522: Unable to load DLL
ORA-06512: in "MySchema.TESTPROCEDURE", Zeile 1
ORA-06512: in Zeile 2
06520. 00000 - "PL/SQL: Error loading external library"
*Cause: An error was detected by PL/SQL trying to load the external
library dynamically.
*Action: Check the stacked error (if any) for more details.
Info:
The database spawns the extproc.exe properly, after I call the procedure
How do I get this work properly and are Delphi DLLs even compatible?
This is my listener.ora file:
# listener.ora Network Configuration File: C:\app\oracle\product\18.0.0\dbhomeXE\NETWORK\ADMIN\listener.ora
# Generated by Oracle configuration tools.
DEFAULT_SERVICE_LISTENER = XE
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = CLRExtProc)
(ORACLE_HOME = C:\app\oracle\product\18.0.0\dbhomeXE)
(PROGRAM = extproc)
(ENVS = "EXTPROC_DLLS=ONLY:C:\app\oracle\product\18.0.0\dbhomeXE\bin\oraclr18.dll")
)
)
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
)
)
And this is my tnsnames.ora file:
# tnsnames.ora Network Configuration File: C:\app\oracle\product\18.0.0\dbhomeXE\NETWORK\ADMIN\tnsnames.ora
# Generated by Oracle configuration tools.
XE =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = XE)
)
)
LISTENER_XE =
(ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))
ORACLR_CONNECTION_DATA =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
)
(CONNECT_DATA =
(SID = CLRExtProc)
(PRESENTATION = RO)
)
)
Any ideas what could be wrong with my configuration?
Edit:
This is the .c dll example code from the database:
/*
** Copyright (c) 1997 by Oracle Corporation
**
** NAME
** EXTERN.C
**
** DESCRIPTION
** Sample Windows NT External Procedure: find_max
**
*/
#include <windows.h>
#define NullValue -1
/*
This function simply returns the returns the larger of x and y.
*/
long __declspec(dllexport) find_max(long x,
short x_indicator,
long y,
short y_indicator,
short *ret_indicator)
{
/* It can be tricky to debug DLL's that are being called by a process
that is spawned only when needed, as in this case.
Therefore try using the DebugBreak(); command.
This will start your debugger. Uncomment the following line and
you can step right into your code.
*/
/* DebugBreak(); */
/* first check to see if you have any nulls */
/* Just return a null if either x or y is null */
if ( x_indicator==NullValue || y_indicator==NullValue) {
*ret_indicator = NullValue;
return(0);
} else {
*ret_indicator = 0; /* Signify that return value is not null */
if (x >= y) return x;
else return y;
}
}
And this is the make.bat file:
REM USAGE: just type MAKE
if (%PROCESSOR_ARCHITECTURE%)==(IA64) goto win64_ia64
if (%PROCESSOR_ARCHITECTURE%)==(AMD64) goto win64_amd64
cl -I. /LD -Zi extern.c /link msvcrt.lib /nod:libcmt /DLL
goto fi
:win64_ia64
cl /DWIN64 /D_WIN64 /DSS_64BIT_SERVER /D_IA64_=1 -I. /LD -Zi extern.c /link msvcrt.lib /nod:libcmt /DLL
goto fi
:win64_amd64
cl /GS- /DWIN64 /D_WIN64 /DSS_64BIT_SERVER /D_AMD64_=1 -I. /LD -Zi extern.c /link msvcrt.lib /MACHINE:AMD64 /nod:libcmt /DLL
goto fi
:fi

Based on the information provided, I can see the following:
The C example is compiled as a 64 bit DLL.
The C example does not specify a calling convention and so uses the default which is cdecl.
The example C functions accept 5 arguments, but your functions accept only 2.

Related

Problem with correct installation of rstan

I'm using R 4.2.2, RTools 42, both installed in D:, and tried to install Rstan and bmrs to solve problems of a Bayesian stat course. I have also a "Makevar" file in ~\.R folder containing:
CXXFLAGS=-O3 -Wno-unused-variable -Wno-unused-function
Honestly, I don't know what the above code does!
I tried to run the following code:
fit_press <- brm(rt ~ 1,
data = df_spacebar,
family = gaussian(),
prior = c(
prior(uniform(0, 60000), class = Intercept, lb = 0, ub = 60000),
prior(uniform(0, 2000), class = sigma, lb = 0, ub = 2000)
),
chains = 4,
iter = 2000,
warmup = 1000
)
and got the following Error:
Compiling Stan program...
Error in compileCode(f, code, language = language, verbose = verbose) :
In addition: Warning message:
In system2(cmd, args = paste(" CMD SHLIB", basename(libCFile)), :
'CreateProcess' failed to run 'D:\Program Files\R\R-4.2.2\bin\x64\R.exe CMD SHLIB file1ec818233.cpp'
Error in sink(type = "output") : invalid connection
Any suggestions?
Thank you very much in advance.

Where Debug info stored in Framework?

As the title mentioned, I'm confusing about where is the debug info stored in .Framework file.
I googled for serval days, what I gots are:
Framework binary compiled by debug mode will include a debug info segment, to indicate the symbol location. Release mode compiling will move it to a dSYM file.
But, what confused me is, I build a framework with ninja, and it doesn't generate dSYM file. Meanwhile I can't find the symbol location by dwarfdump command or MachOView app. As Regards strings command can get some relative file path results, like ../../flutter/fml/memory/task_runner_checker.cc.
Here dwarfdump prints:
Flutter.framework/Flutter: file format Mach-O arm64
.debug_info contents:
Here is my questions:
When I triggered breakpoint at somewhere in the framework, the source code still shows. Why?
When I run lldb command, it shows:
(lldb) image lookup -a $pc --verbose
Address: Flutter[0x0000000001964f18] (Flutter.__TEXT.__text + 26604184)
Summary: Flutter`dart::BootstrapNatives::DN_LoadLibraryFromTypedData(dart::Thread*, dart::Zone*, dart::NativeArguments*) + 44 [inlined] dart::NativeArguments::NativeArgAt(int) const at object.cc:537
Flutter`dart::BootstrapNatives::DN_LoadLibraryFromTypedData(dart::Thread*, dart::Zone*, dart::NativeArguments*) + 44 [inlined] dart::DN_HelperLoadLibraryFromTypedData(dart::Isolate*, dart::Thread*, dart::Zone*, dart::NativeArguments*) at object.cc:534
Flutter`dart::BootstrapNatives::DN_LoadLibraryFromTypedData(dart::Thread*, dart::Zone*, dart::NativeArguments*) + 44 at object.cc:534
Module: file = "/Users/xx/Library/Developer/Xcode/DerivedData/XXX-ddigzjlnuypwnydlawevfrkmdsov/Build/Products/Debug-iphoneos/XXX.app/Frameworks/Flutter.framework/Flutter", arch = "arm64"
CompileUnit: id = {0x00000000}, file = "/Users/xx/Documents/workspace/aion/flutter_engine/src/third_party/dart/runtime/lib/object.cc", language = "c++14"
Function: id = {0x7d40006244d}, name = "dart::BootstrapNatives::DN_LoadLibraryFromTypedData(dart::Thread*, dart::Zone*, dart::NativeArguments*)", mangled = "_ZN4dart16BootstrapNatives27DN_LoadLibraryFromTypedDataEPNS_6ThreadEPNS_4ZoneEPNS_15NativeArgumentsE", range = [0x0000000117310eec-0x0000000117311490)
FuncType: id = {0x7d40006244d}, byte-size = 0, decl = bootstrap_natives.h:507, compiler_type = "class dart::ObjectPtr (class dart::Thread *, class dart::Zone *, class dart::NativeArguments *)"
Blocks: id = {0x7d40006244d}, range = [0x117310eec-0x117311490)
id = {0x7d40006249b}, ranges = [0x117310f18-0x1173113e0)[0x117311404-0x117311490), name = "DN_HelperLoadLibraryFromTypedData", decl = object.cc:534, mangled = _ZN4dartL33DN_HelperLoadLibraryFromTypedDataEPNS_7IsolateEPNS_6ThreadEPNS_4ZoneEPNS_15NativeArgumentsE, demangled = dart::DN_HelperLoadLibraryFromTypedData(dart::Isolate*, dart::Thread*, dart::Zone*, dart::NativeArguments*)
id = {0x7d40006253a}, range = [0x117310f18-0x117310f24), name = "NativeArgAt", decl = native_arguments.h:129, mangled = _ZNK4dart15NativeArguments11NativeArgAtEi, demangled = dart::NativeArguments::NativeArgAt(int) const
LineEntry: [0x0000000117310f18-0x0000000117310f24): /Users/xx/Documents/workspace/xxx/flutter_engine/src/third_party/dart/runtime/vm/native_arguments.h:132:14
Symbol: id = {0x0013fd0f}, range = [0x0000000117310eec-0x0000000117311490), name="dart::BootstrapNatives::DN_LoadLibraryFromTypedData(dart::Thread*, dart::Zone*, dart::NativeArguments*)", mangled="_ZN4dart16BootstrapNatives27DN_LoadLibraryFromTypedDataEPNS_6ThreadEPNS_4ZoneEPNS_15NativeArgumentsE"
Variable: id = {0x7d400062553}, name = "this", type = "const dart::NativeArguments *", location = DW_OP_reg20 W20, decl =
Variable: id = {0x7d40006255c}, name = "index", type = "int", location = <decoding error> 00 00 00, decl = native_arguments.h:129
Variable: id = {0x7d4000624a8}, name = "isolate", type = "dart::Isolate *", location = , decl = object.cc:534
Variable: id = {0x7d4000624ad}, name = "thread", type = "dart::Thread *", location = , decl = object.cc:534
Variable: id = {0x7d4000624b2}, name = "zone", type = "dart::Zone *", location = DW_OP_reg19 W19, decl = object.cc:534
Variable: id = {0x7d4000624bb}, name = "arguments", type = "dart::NativeArguments *", location = DW_OP_reg20 W20, decl = object.cc:534
Variable: id = {0x7d4000624c4}, name = "program", type = "unique_ptr<dart::xx_kernel::Program, std::__1::default_delete<dart::xx_kernel::Program> >", location = DW_OP_breg31 WSP+64, decl = object.cc:556
Variable: id = {0x7d400062468}, name = "thread", type = "dart::Thread *", location = DW_OP_reg24 W24, decl = object.cc:534
Variable: id = {0x7d400062479}, name = "zone", type = "dart::Zone *", location = DW_OP_reg19 W19, decl = object.cc:534
Variable: id = {0x7d40006248a}, name = "arguments", type = "dart::NativeArguments *", location = DW_OP_reg20 W20, decl = object.cc:534
So, where the lldb get Compile Unit and LineEntry outputs?
Debug information on Darwin systems exists in one of two places: In the .o files, and later after dsymutil is run to create a .dSYM, it exists in the .dSYM bundle, all collected together, relocated to the actual binary's addresses.
This was a build-link-debug performance enhancement. Linking all of the debug information -- updating all the symbol addresses, copying it all around -- is very slow, so leaving the debug information in the .o files for this common iterative development cycle, and having the debugger locate the .o files and update the addresses of the functions internally, allows for rapid development.
Leaving all of the debug information in the .o files requires that they all be present, of course! And at the same file paths. So it is not good when you need to move a binary between computers, or save it for later debugging. For these cases, you link the debug information with dsymutil and you get a .dSYM bundle.

Cannot connect to Oracle 19 with unixODBC

I'm trying to connect to an Oracle 19 database using unixODBC 2.3.7.
I'm using the Oracle Developer Days VirtualBox VM.
When trying to troubleshoot via isql I always get this error:
# isql -v -3 oracledsn
[HY000][unixODBC][Oracle][ODBC][Ora]ORA-12545: Connect failed because target host or object does not exist
[ISQL]ERROR: Could not SQLConnect
Sadly I can't reproduce it now, but when last trying it I always had
[IM004][unixODBC][Driver Manager]Driver's SQLAllocHandle on SQL_HANDLE_HENV failed
[ISQL]ERROR: Could not SQLConnect
This is the same error message I get from my (proprietary) C++ code, so I'm pretty sure the problem is just the connection settings.
I have checked https://www.connectionstrings.com/oracle/ but none of the combinations work.
Here are all my config settings:
# odbcinst -j -130-
unixODBC 2.3.7
DRIVERS............: /usr/local/etc/odbcinst.ini
SYSTEM DATA SOURCES: /usr/local/etc/odbc.ini
FILE DATA SOURCES..: /usr/local/etc/ODBCDataSources
USER DATA SOURCES..: /home/florian/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8
# cat /usr/local/etc/odbcinst.ini
[oraclex]
Description = Oracle ODBC driver for Oracle 19
Driver = /mnt/libsqora.so.19.1
Setup =
FileUsage =
CPTimeout =
CPReuse =
Driver Logging = 7
[ODBC]
Trace = Yes
TraceFile = /tmp/odbc.log
ForceTrace = Yes
Pooling = No
DEBUG = 1
# cat ~/.odbc.ini
[oracledsn]
Driver = oraclex
Server = 10.1.6.19
Port = 1521
Uid = system
Pwd = oracle
Database = orclcdb/orcl/"<empty>"
ORA-12545: Connect failed because target host or object does not exist
I also tried ServerName
ServerName = //10.1.6.19/orclcdb or //10.1.6.19/orcl
[28000][unixODBC][Oracle][ODBC][Ora]ORA-01017: invalid username/password; logon denied
Here's the /tmp/odbc.log:
[ODBC][18832][1564563250.044774][__handles.c][460]
Exit:[SQL_SUCCESS]
Environment = 0x1fd7780
[ODBC][18832][1564563250.044854][SQLSetEnvAttr.c][189]
Entry:
Environment = 0x1fd7780
Attribute = SQL_ATTR_ODBC_VERSION
Value = 0x3
StrLen = 0
[ODBC][18832][1564563250.044886][SQLSetEnvAttr.c][381]
Exit:[SQL_SUCCESS]
[ODBC][18832][1564563250.044914][SQLAllocHandle.c][377]
Entry:
Handle Type = 2
Input Handle = 0x1fd7780
[ODBC][18832][1564563250.044944][SQLAllocHandle.c][493]
Exit:[SQL_SUCCESS]
Output Handle = 0x1fd8090
[ODBC][18832][1564563250.044983][SQLConnect.c][3721]
Entry:
Connection = 0x1fd8090
Server Name = [oracledsn][length = 9 (SQL_NTS)]
User Name = [NULL]
Authentication = [NULL]
UNICODE Using encoding ASCII 'ANSI_X3.4-1968' and UNICODE 'UCS-2LE'
DIAG [HY000] [Oracle][ODBC][Ora]ORA-12545: Connect failed because target host or object does not exist
[ODBC][18832][1564563250.082037][SQLConnect.c][4244]
Exit:[SQL_ERROR]
[ODBC][18832][1564563250.082070][SQLGetDiagRec.c][677]
Entry:
Connection = 0x1fd8090
Rec Number = 1
SQLState = 0x7fffeabcac60
Native = 0x7fffeabcac5c
Message Text = 0x7fffeabcac70
Buffer Length = 500
Text Len Ptr = 0x7fffeabcac5a
[ODBC][18832][1564563250.082092][SQLGetDiagRec.c][726]
Exit:[SQL_SUCCESS]
SQLState = HY000
Native = 0x7fffeabcac5c -> 12545
Message Text = [[unixODBC][Oracle][ODBC][Ora]ORA-12545: Connect failed because target host or object does not exist
I've also been trying out this C code from https://www.easysoft.com/developer/languages/c/odbc_tutorial.html#connect_full - basically the same result.
# cat test2.c
# compile with gcc -I ../include test2.c -o test2 -lodbc
#include <stdio.h>
#include <sql.h>
#include <sqlext.h>
/*
* see Retrieving ODBC Diagnostics
* for a definition of extract_error().
*/
static void extract_error(
char *fn,
SQLHANDLE handle,
SQLSMALLINT type);
main() {
SQLHENV env;
SQLHDBC dbc;
SQLHSTMT stmt;
SQLRETURN ret; /* ODBC API return status */
SQLCHAR outstr[1024];
SQLSMALLINT outstrlen;
/* Allocate an environment handle */
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
/* We want ODBC 3 support */
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
/* Allocate a connection handle */
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
/* Connect to the DSN mydsn */
ret = SQLDriverConnect(dbc, NULL, "DSN=oracledsn;", SQL_NTS,
outstr, sizeof(outstr), &outstrlen,
SQL_DRIVER_COMPLETE);
if (SQL_SUCCEEDED(ret)) {
printf("Connected\n");
printf("Returned connection string was:\n\t%s\n", outstr);
if (ret == SQL_SUCCESS_WITH_INFO) {
printf("Driver reported the following diagnostics\n");
//extract_error("SQLDriverConnect", dbc, SQL_HANDLE_DBC);
}
SQLDisconnect(dbc); /* disconnect from driver */
} else {
fprintf(stderr, "Failed to connect\n");
//extract_error("SQLDriverConnect", dbc, SQL_HANDLE_DBC);
}
/* free up allocated handles */
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
SQLFreeHandle(SQL_HANDLE_ENV, env);
}
As another point reference I can connect to said Oracle Dev VM with sqlplus from another machine like this:
./sqlplus sys/oracle#10.1.6.19/orclcdb as sysdba
SQL>
./sqlplus system/oracle#10.1.6.19/orclcdb
SQL>
On the VM I have a file /u01/app/oracle/product/version/db_1/network/admin/tnsnames.ora but I am not completely sure if I also need one on the client?
ORCLCDB=localhost:1521/orclcdb
ORCL=
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 0.0.0.0)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orcl)
)
)
I finally found the solution, although this is for 12.2.0.1 and not 19
# cat .odbc.ini
[oracledsn]
Driver=oracle
UID=system
PWD=oracle
DBQ=192.168.178.32:1521/ORCLCDB
ServerName=//192.168.178.32:1521/ORCLCDB
cat /usr/local/etc/odbcinst.ini
[oracle]
Description = foo
Driver = /mnt/libsqora.so.19.1
Driver Logging = 7
UsageCount=1
FileUsage=1
[ODBC]
Trace = Yes
TraceFile = /tmp/odbc.log
ForceTrace = Yes
Pooling = No
# LD_LIBRARY_PATH=. isql -v oracledsn system oracle
+---------------------------------------+
| Connected! |
I have no Oracle-specific environment variables set, but if it works if I take the value of ServerName verbatim for TWO_TASK.
I had success with oracle 19c, with one minor change here - remove the "//" from ServerName i.e.
ServerName=192.168.178.32:1521/ORCLCDB

How do I fix OCIServerAttach failing with DATABASE error: ORA-12541: TNS:no listener?

I have my client on a Windows machine and the Oracle server on a Centos Virtual machine.
I am able to connect from my PC to the server using SQLPlus using the following string:
sqlplus MyName/MyPassword#IPADDRESS/xe
My tnsnames.ora files is:
# tnsnames.ora Network Configuration File:
XE =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = IPAddress)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = XE)
)
)
EXTPROC_CONNECTION_DATA =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC_FOR_XE))
)
(CONNECT_DATA =
(SID = PLSExtProc)
(PRESENTATION = RO)
)
)
However, when I try to use the same connection information from my application the connections fails in the OCIServerAttach call.
Here is the code framework:
OCIEnvCreate(&tmpEnv,
OCI_OBJECT | OCI_THREADED,
NULL,
NULL,
NULL,
NULL,
0,
NULL);
OCISessionBegin(serviceContextHandle_, errorHandle_, sessionHandle_, OCI_CRED_RDBMS, OCI_DEFAULT);
OCIHandleAlloc( (dvoid *) environmentHandle_, (dvoid **) &serviceContextHandle_, OCI_HTYPE_SVCCTX, (size_t) 0, (dvoid **) 0);
OCIHandleAlloc( (dvoid *) environmentHandle_, (dvoid **) &serverHandle_, OCI_HTYPE_SERVER, (size_t) 0, (dvoid **) 0);
OCIHandleAlloc( (dvoid *) environmentHandle_, (dvoid **) &errorHandle_, OCI_HTYPE_ERROR, (size_t) 0, (dvoid **) 0);
OCIHandleAlloc( (dvoid *) environmentHandle_, (dvoid **) &sessionHandle_, OCI_HTYPE_SESSION, (size_t) 0, (dvoid **) 0);
/* here path is an std::string which contains the IPAddress followed by '/xe'*/
result = OCIServerAttach( serverHandle_, errorHandle_, (text *)path_.c_str(), (sb4)path_.length(), 0);
The IPAddress matches the IPAddress in the tnsnames.ora and listener.ora file. There is no port number being used as I intended to use the default 1521.
To the best of my knowledge the listeners are set up and working correctly otherwise I would not be able to connect via SQLPlus. My ORACLE_HOME value is as I expect. The install directory is in my PATH.
Anyone have any ideas of what might be wrong?

GetOpenFileName fails in 64 bit, but works in 32Bit?

I have the following code, I use to Open a File Open Dialog using Win32 API. It works fine in 32bit, but fails when I use in a 64bit (In a DLL). What am I doing wrong?
char Filestring[256];
Filter = "OBJ files\0*.obj\0\0";
char* returnstring = NULL;
OPENFILENAME opf;
opf.hwndOwner = mainHWND;
opf.lpstrFilter = Filter;
opf.lpstrCustomFilter = 0;
opf.nMaxCustFilter = 0L;
opf.nFilterIndex = 1L;
opf.lpstrFile = Filestring;
opf.lpstrFile[0] = '\0';
opf.nMaxFile = 256;
opf.lpstrFileTitle = 0;
opf.nMaxFileTitle=50;
opf.lpstrInitialDir = Path;
opf.lpstrTitle = "Open Obj File";
opf.nFileOffset = 0;
opf.nFileExtension = 0;
opf.lpstrDefExt = "*.*";
opf.lpfnHook = NULL;
opf.lCustData = 0;
opf.Flags = (OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT) & ~OFN_ALLOWMULTISELECT;
opf.lStructSize = sizeof(OPENFILENAME);
if(GetOpenFileName(&opf))
{
returnstring = opf.lpstrFile;
if (returnstring) {
result = returnstring;
}
}
EDIT: By failing, I meant that the Open File Dialog doesn't show up. The code still returns zero without any errors.
EDIT 2: I have called CommDlgExtendedError() and it returned 1. From the MSDN reference, does it mean the dialog has invalid lStructSize? I have checked the sizeof(OPENFILENAME) and it returned 140 bytes.
UPDATE: In my Project Settings, Under Code Generation the "Struct Member Alignment" is set to 4 Bytes(/Zp4). I changed this to default and it magically worked. Look for the answers and their comments below for more information.
You aren't initialising lpTemplateName and so it contains random stack noise. This in turn will lead to 'hInstance` being references which also contains stack noise.
When calling a function like this you should first of all zero out the struct and only fill in the fields that are non-zero. Something like this:
OPENFILENAME opf={0};
opf.lStructSize = sizeof(OPENFILENAME);
opf.hwndOwner = mainHWND;
opf.lpstrFilter = Filter;
opf.nFilterIndex = 1L;
opf.lpstrFile = Filestring;
opf.lpstrFile[0] = '\0';
opf.nMaxFile = 256;
opf.lpstrInitialDir = Path;
opf.lpstrTitle = "Open Obj File";
opf.lpstrDefExt = "*.*";
opf.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
There was no need to exclude OFN_ALLOWMULTISELECT explicitly since you were not including it in the first place!
EDIT
You state in a comment that this doesn't work. Calling CommDlgExtendedError is a good idea and should tell you why it fails.
You could also try to run the minimal possible GetOpenFileName which is this:
char Filestring[MAX_PATH] = "\0";
OPENFILENAME opf={0};
opf.lStructSize = sizeof(OPENFILENAME);
opf.lpstrFile = Filestring;
opf.nMaxFile = MAX_PATH;
GetOpenFileName(&opf);
I have the very same problem and a partial solution :
+ the simple following simple example (proposed abobe) was not working in x64 mode.
+ I changed the complie option "struct Member Alignment" from 1byte /Zp1 to default which solved this problem (by introducing others !!!)
char Filestring[MAX_PATH] = "\0";
OPENFILENAME opf={0};
opf.lStructSize = sizeof(OPENFILENAME);
opf.lpstrFile = Filestring;
opf.nMaxFile = MAX_PATH;
GetOpenFileName(&opf);
To find out more you should call CommDlgExtendedError to get the error code what went wrong. Besides this I would initialize all member of the struct to 0 with
ZeroMemory(&opf, sizeof(opf));
Since the file open dialog is in reality a COM component it could be worth to check out if your thread apartment state is different under 64 bit.
if( RPC_E_CHANGED_MODE == CoInitialize(NULL) )
ASSERT(FALSE); // MTA Apartment found
CoUnitialize()
Yours,
Alois Kraus
As a note in Microsoft Office 2010 64-bit we gave up and used the internal wrappers as the structure turned into 140 bytes and we were not sure how to change alignment.
Application.GetOpenFilename(FileFilter, FilterIndex, Title, ButtonText, MultiSelect)
and Application.GetSaveAsFilename(InitialFilename, FileFilter, FilterIndex, Title, ButtonText)
http://msdn.microsoft.com/en-us/library/ff834966.aspx
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel._application.getopenfilename.aspx
Needless to say we think all individuals with fairly heavy applications in Excel should start considering other options as maintaining future versions across multiple clients and platforms may just be... insane!
I managed to get around this problem by setting the packing appropriately before including the header file. That way, for the purpose of this one function, we were using the 'default' 16 byte alignment, but did not have to change the packing alignment for the rest of our program:
#ifdef _WIN64
#pragma pack( push )
#pragma pack( 16 )
#include "Commdlg.h"
#pragma pack( pop )
#else
#include "Commdlg.h"
#endif // _WIN64

Resources