Behaviour at opening or referencing in Excel for an XLL having a DLL as dependency - visual-studio

Take an XLL. You can
open it from excel by doing "File" then "Open" etc or by dragging and dropping it in a blank (or not) Excel spreadsheet (it is the same)
reference it from Excel using Excel's Add-ins manager by (in recent Excel versions) doing "File", "Options", "Add-ins" and in "Manage" choose "Excel Add-ins", then "Go", then browse to point to the XLL then "OK" so that it appears and is ticked in the Add-ins list, then "OK" to reference it. (That's the "OK" I will I refer to in what follows.)
Now I have an XLL1 that I compile with only one visual studio project. Opening it or referencing it doesn't make any difference and I have no problem. Great.
The code of XLL1 is basically lots of XLL functions that process inputs in the Excel C Api format to convert them in some internal (and Excel C Api independant) format and then feed these processed inputs to underlying C++ functions that performe intricated numerical calculations etc. I decided to separe the Excel C API stuff (the interface) from this underlying Excel C API independant numerical stuff by plugging the latter in a C++ DLL (that I will simply call "DLL" here) that the XLL's project now links to. I call the resulting XLL "XLL2".
Now if I open (in the sense defined above) XLL2 I get the following error :
the file format and extension of 'XLL2.xll' don't match. The file could be corrupted of unsafe. Blah Blah
that you usually get when you have an architecture mismatch between your Excel and your XLL, which is not my case here as everything (my Excel and my projects platforms) is 64 bits -- I confirmed it with Dependency Walker. (Plus, I don't see why architecture would matter suddenly for XLL2's "execution" when it doesn't for XLL1's compilation.)
If I try to reference (in the sense defined above) XLL2 in Excel, at the final "OK" I get the following error :
Microsoft Excel cannot access the XLL2.xll". There are several possible reasons : the file name or path does not exist ; the file is being used by another program ; the workbook you are trying to save has the same name as a currently open workbook.
Of course, none of this three reasons is verified in my case.
I naturally suspected that at the opening or referencing of XLL2 Excel cannot get the information about XLL2 depending on DLL, hence yells the only error message it is allowed to yell at that point.
Indeed, when I put XLL2 and DLL next to EXCEL.exe in the same folder (vomiting smiley) I had no issue with open or referencing it. But sadly, I don't intend to ship my code inside EXCEL.exe's folder so that option isn't a real one for me. Naturally, I tried to add "$(UniversalCRT_LibraryPath_x86)" or "$(UniversalCRT_LibraryPath_x64)" to the additional libraries directories in XLL2's properties in "linker --> general" (where I naturally already add the path to the DLL XLL2 links to). It just allowed me to open1 XLL2 at debug in visual studio. In the normal utilisation, outside of visual studio, the problem remained, while both in debug or normal execution, referencing XLL2 wasn't working.
Then I told myself that the only solution is in fact a proper use of LoadLibrary not just before Excel "loads" the XLL, but after that and just before it loads the XLL functions (that depend on DLL). I modified the Excel C API template code as follows :
// Excel calls xlAutoOpen when it loads the XLL.
__declspec(dllexport) int WINAPI xlAutoOpen(void)
{
static XLOPER12 xDLL; // The filename of this XLL.
int i;
#if DEBUG
debugPrintf("xlAutoOpen\n");
#else
#endif
// Fetch the name of this XLL. This is used as the first arg
// to the REGISTER function to specify the name of the XLL.
Excel12f(xlGetName, &xDLL, 0);
if (xDLL.xltype == xltypeStr)
{
std::wstring wpath(xDLL.val.str);
std::string path(wpath.begin(), wpath.end());
path.erase(0, 1);
path = std::regex_replace(path, std::regex("XLL2.xll"), "DLL.dll");
std::wstring wpath2 = std::wstring(path.begin(), path.end());
LPCWSTR lpath = wpath2.c_str();
HINSTANCE hinstLib = LoadLibrary(lpath);
if (hinstLib == NULL)
return -1;
}
#if DEBUG
debugPrintf("XLL Name : %S\n", xDLL.val.str);
#else
#endif
// Loop through the g_rgUDFs[] table, registering each
// function in the table using xlfRegister.
for (i = 0; i < g_rgNumUDFs; i++)
by adding the if (xDLL.xltype == xltypeStr) part and choose to delay the DLL loading in XLL2's linker properties in VIsual Studio. And I also added $(UniversalCRT_LibraryPath_x64) (or $(UniversalCRT_LibraryPath_x86)) in the additional library directory for the linker in XLL2 project's properties.
And indeed, at debug or at execution, I can open XLL2 and use XLL2 as I was opening and using XLL1 : without any problem. (Important : would I not have add $(UniversalCRT_LibraryPath_x64) as described above, open the XLL2 would have triggered no error, but no functions of XLL2 would have been seen from Excel.)
But would I try to reference XLL2, I still have the same issue. (And referencing it matters as it allows for XLL2 to be loaded next times the user opens Excel, while just opening the XLL2 opens it only for the current Excel instance, not for the next ones.)
What puzzles me is that when you reference an XLL for the first time, Excel calls the XLL's xlAddInManagerInfo12 function (see https://learn.microsoft.com/en-us/office/client-developer/excel/xladdinmanagerinfo-xladdinmanagerinfo12 for instance) while when I put a breakpoint at the beginning of that functions in my XLL2's code, I never break at it at debug when I reference XLL2, I just directly have the Microsoft Excel cannot access the XLL2.xll". There are several possible reasons error message after doing "OK" and that's it. (Also, checking DLLs loaded by that Excel instance in Process Explorer doesn't show XLL2 nor DLL.)
I would like to know the proper way to achieve using XLL2 as I was using XLL1 (opening or referencing).

Related

Why am I seeing the error "the system cannot find the file specified" in Visual Studio C++?

I am getting an error "the system cannot find the file specified" in Visual Studio C++ when I try to run my program.
You pressed F5 to start the program.
The code was compiled.
The linker failed because it couldn't find int main()
This means there was no program to run.
To fix it you need to define a function called main* that will look like this:
int main (void)
{
// Call the function that you think starts your program, i.e.
Bob();
wprintf(L"Press enter to exit\n");
return fgetc(stdin);
}
The press F7 to make sure it compiles.
Once it has compiled you can use F5 again.
*All C, C++ programs have to have a function called main its the first user visible function that is called. All your code will be called from within main.
Some times main is 'hidden', such as wmain of MFC GUIs, etc.
I can only guess, because you have not given enough information.
Nevertheless, at first I would check the compiling options of the solution and projects for any wrong or missing paths.
If that would not fix the issue, I would check if I have access to all the directories.
Besides, I would avoid using space in any names or paths, because those can cause issues too. I would use camel case notation instead.

Adding an export to a DLL without recompiling it

I have a DLL that I want to use/debug. I don't have the source.
Looking at it with IDA, I found 3 things :
DllMain does nothing
The code I need is self contained in a function that only calls a few Windows API. It does not reference anything else in that DLL.
That self contained function is not exported
I could extract the assembly code and link it to a C program, but I wonder:
Is it possible to (and how should I) add an entry to the export table of an existing DLL without recompiling it?
Yes, you can do that but most tools don't support this. For example using CFF Explorer, it's easier to convert an existing export to what you want it to be. Just edit the function RVA and exported name. Since you only need the one thing, it shouldn't be a problem that you're removing some other export.
You could even do it with a hex editor since it doesn't involve moving anything rebuilding the header, it's just an in-place edit.

In a Visual Studio Extension, get the line range of the function in which the debugger is stopped

I have a Visual Studio extension that hooks into debugging events. When the debugger stops at a line of code, my IDebugEventCallback2 callback gets called, and I can find out the filename and line number where the debugger has stopped via IDebugThread2::EnumFrameInfo.
I'd like to know the range of source code lines that the current function spans.
I'm hoping it's possible to derive the information I need from the debugger interfaces - the debugger must know the line range of functions. If that's not possible, I'm open to any other methods. In an ideal world the solution would work without the project system - many people, myself included, use Visual Studio as a stand-alone debugger without using the project system. (Also, I can't rely on Roslyn - it needs to work in existing versions of Visual Studio.)
Edit: Carlos's method of using FileCodeModel works well, as long as the file is part of a project. I'd still love to know whether there's a method that doesn't require the project system.
Given a FRAMEINFO retrieved with IEnumDebugFrameInfo2.Next, you can use the following code to get the file name, the first line of code of the current frame and the current line of code:
IDebugStackFrame2 stackFrame = frmInfo.m_pFrame;
if (stackFrame != null)
{
TEXT_POSITION[] begin = new TEXT_POSITION[1];
TEXT_POSITION[] end = new TEXT_POSITION[1];
IDebugDocumentContext2 debugDocumentContext2;
stackFrame.GetDocumentContext(out debugDocumentContext2);
if (debugDocumentContext2 != null)
{
string fileName;
debugDocumentContext2.GetName((uint)enum_GETNAME_TYPE.GN_FILENAME, out fileName);
debugDocumentContext2.GetSourceRange(begin, end);
}
}
FWIW, the IDebugDocumentContext2 interface has a Seek method that allows you to advance lines or statements of code in the stack frame. I guess you can advance until failure to get the end line of code of the stack frame.
To get info about code elements and start/end points using the project system (and without Roslyn) you have to use the automation model (EnvDTE.ProjectItem.FileCodeModel). Given a EnvDTE.ProjectItem and a line of code, you can use for example: HOWTO: Get the code element at the cursor from a Visual Studio .NET macro or add-in

Does LabWindows/CVI have something similar to _setmode() for setting the file (or stream) translation mode to binary or text?

I am using gSoap to generate ANSI C source code, that I would like to build within the LabWindows/CVI environment, on a Windows 7, 64 bit OS. The gSoap file stdsoap2.c includes several instances of the _setmode() function, with the following prototype:
int _setmode (int fd, int mode);
Where fd is a file descriptor, and mode is set to either _O_TEXT or _O_BINARY.
Oddly enough, even though LW/CVI contains an interface to Microsoft's SDK, this SDK does not contain a prototype to _setmode in any of its included header files, even though the help link to the SDK contains information on the function.
Is anyone aware of the method in LabWindows/CVI used to set file (or stream) translation mode to text, or binary.
Thanks,
Ryyker
Closing the loop on this question.
I could not use the only offered answer for the reason listed in my comment above.
Although I did use the SDK, it was not to select a different version of the OpenFile function, rather it was to support the use of a function that an auto-code generator used, _setmode() but that was not supported by my primary development environment (LabWindows/CVI).
So, in summary, my solution WAS to include the SDK to give me definition for _setmode as well as including the following in my non- auto-generated code:
#define _O_TEXT 0x4000 /* file mode is text (translated) */
#define _O_BINARY 0x8000 /* file mode is binary (untranslated) */
So, with the caveat that this post describes what I actually did, I am going to mark the answer #gary offered as the answer, as it was in the ball park. Thanks #gary.
It sounds like you just want to open a file as either ASCII or binary. So you should be able to replace the instances of _setmode() with the LW/CVI OpenFile() function as described here. Here's a short example reading a file as binary.
char filename = "path//to//file.ext"
int result;
result = OpenFile(filename, VAL_READ_ONLY, VAL_OPEN_AS_IS, VAL_BINARY);
if (result < 0)
// Error, notify user.
else
// No error.
Also note this warning from the page:
Caution The Windows SDK also contains an OpenFile function. If you
include windows.h and do not include formatio.h, you will get compile
errors if you call OpenFile.

error in creating include file REPORT/PROGRAM statement missing

I do some examples and i need help with one or few errors. I create : sourse file: Type Include , Status test , Aplication system(Local object).
code:
*&---------------------------------------------------------------------*
*& Include Z_EB_MEMBERLIST13_A_SELECTION *
*&---------------------------------------------------------------------*
SELECTION-SCREEN BEGIN OF BLOCK member
WITH FRAME TITLE text-001.
PARAMETERS: par01 AS CHECKBOX,
par02 AS CHECKBOX,
par03 AS CHECKBOX,
par04 AS CHECKBOX.
SELECTION-SCREEN END OF BLOCK member.
activation (ctrl+F3) passes but if i want check syntax (ctrl+F2) show error:
REPORT/PROGRAM statement missing, or program type is I(INCLUDE)
Pls: Where is problem?
thx
The message is only a warning. If this was an executable program the lack of a "REPORT" statement would be a problem; however for include programs, as soon as I include it in any report, It will compile successfully using CTRL+F2
Include programs can not be executed (activated) as it requires that the include is referanced in a program "REPORT" somewhere. From the code you have pasted I think you want to change the program type to "executable program" in attributes of the source program and then include a "REPORT" statement at the top of your file followed by the source filename.
ie.
REPORT Z_EB_MEMBERLIST13_A_SELECTION.
Might be worth having a look at the following link for a full overview.
http://help.sap.com/saphelp_nw2004s/helpdata/en/fc/eb2d5a358411d1829f0000e829fbfe/content.htm
Of note this section taken from above link.
Best of luck.
Include Programs
In contrast to all other program types, include programs do not represent stand-alone compilation units with a memory area of their own. Include programs cannot be executed. They merely serve as a library for ABAP source code. They are exclusively used to organize program texts into small editable units which can be inserted at any place in other ABAP programs using the INCLUDE statement. There is no technical relationship between include programs and processing blocks. Includes are more suitable for logical programming units, such as data declarations, or sets of similar processing blocks. The ABAP Workbench has a mechanism for automatically dividing up module pools, function groups and class pools into include programs. You create your own include programs using the ABAP Editor.

Resources