deploy app with dll wrapper - winforms

I'm trying to deploy a WinForms application that involves importing an external DLL with a wrapper class, like this:
[DllImport(ImportedDllName, CallingConvention=CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern void cfg_put_MixerFilePlayerEnabled(IntPtr cfgPtr, int Enabled);
[DllImport(ImportedDllName, CallingConvention=CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern void cfg_get_MixerFilePlayerEnabled(IntPtr cfgPtr, ref int Enabled);
public int MixerFilePlayerEnabled
{
get { int s = 0; cfg_get_MixerFilePlayerEnabled(m_cfgPtr, ref s); return s; }
set { cfg_put_MixerFilePlayerEnabled(m_cfgPtr, value); }
}
So, when I run my application debbugin, all work right, but when I publish it, I receive a DLL not found exception.
How can I solve this issue?

If your debug machine and publish machine are the same PC:
Find out "the external DLL" is x86 or x64. Your C# wrapper should also run as x86 or x64 correspondingly.
Use dependencywalker to make sure you C# wrapper can find the "the external DLL".
Write a cpp exe demo to make sure cpp exe can use that dll. Then try C# wrapper.
If your debug machine and publish machine are two PCs:
Install the right version of vcredist of "the external DLL".
Use dependencywalker to find out what dll you missed.
Write a cpp exe demo to make sure cpp exe can use that dll. Then try C# wrapper.

Related

How to get a useful error message when a dart:ffi method fails?

I have a dart method that invokes some C code using dart:ffi. When the C code fails (for example due to some memory violation), I don't get any error message to indicate what's wrong, but only the whole process exits immediately with these messages:
[+41627 ms] Service protocol connection closed.
[ ] Lost connection to device.
[ +1 ms] DevFS: Deleting filesystem on the device (file:...)
I am not asking about catching the error, as I know these kinds of errors are fatal, I just want some useful logs to see whats happening.
Can I debug the native code somehow or at least get useful error messages about the reason/location of the crash?
Assumptions: debugging a Windows DLL on Windows using a pure Dart test harness. It should work pretty much the same for a Flutter test harness (i.e. the example app in a Flutter plugin project), but in all likelihood you'll want to do much of the testing in as simple a test environment as you can.
You are going to need two projects: a Visual Studio DLL project and a pure Dart command line. (In what follows my examples are in d:\vcc\Dll2\ and d:\source\testffi2\ respectively. The Dart project has a main function in bin\testffi2.dart.)
For testing I removed the VS C++ DLL project boilerplate and replaced it with:
#include "pch.h"
#include "framework.h"
#include "Dll2.h"
#include <cstdint>
extern "C" __declspec(dllexport) int32_t sum(int32_t a, int32_t b) {
return a + b;
}
and checked that it compiled OK (in Debug/x64 configuration). This built: D:\vcc\Dll2\x64\Debug\Dll2.dll as expected.
In the Dart project I created a simple test harness to load the DLL and the function and call it.
import 'dart:ffi';
import 'dart:io';
void main() {
final dll = DynamicLibrary.open('D:\\vcc\\Dll2\\x64\\Debug\\Dll2.dll');
final sumFunction = dll.lookupFunction<
Int32 Function(
Int32 a,
Int32 b,
),
int Function(
int a,
int b,
)>('sum');
print(sumFunction(123, 321));
sleep(Duration(seconds: 5)); // this is just there so that the VCC debug
// window doesn't immediately close
}
Running this is the Dart IDE or from the command line does, indeed, print 444 as expected, but doesn't give you an opportunity to debug the C.
Finally, back in the Visual Studio project, change the debugging properties to launch the Dart test harness. Set the exe path to the Dart sdk binary, the command line to the Dart main file, and the working folder to the Dart project folder.
Finally, set a breakpoint on the return a + b line and click the Local Windows Debugger button. Execution pauses as expected in the Visual Studio debugger and the values of local variables a and b are visible, etc.

Delay Loaded Package with custom search path

I wrote a BPL project and added output .bpi file to another EXE project. The EXE project compiles and links well. The EXE runs well if I put .bpl file in the same folder as .exe file. However, the EXE fails to run if I put .bpl file in other folders than .exe file, and it shows "The program can't start because XXX.bpl is missing...".
I also wrote a DLL version and put .dll file in other folders than .exe file. I added the .dll to the EXE project's "C++ Linker"->"Advanced"->"Delay load DLLs" list, and added SetDllDirectory() function call to my custom search path. The EXE runs well. It seems that .bpl can not work the same as .dll if delay-loaded with custom search path via SetDllDirectory(), am I right?
Test BPL code is like following:
"TestPackage.h"
class PACKAGE TestPackage
{
TestPackage( void );
int GetInt( void );
};
"TestPackage.cpp"
#include "TestPackage.h"
#pragma package(smart_init)
TestPackage::TestPackage( void ){}
int TestPackage::GetInt( void ){ return 1000; }
And the test form application code is like following:
"TestApp.cpp"
#include "TestPackage.h"
void __fastcall TForm1::Button1Click( TObject* Sender )
{
TestPackage* package = new TestPackage;
int ret = package->GetInt();
Application->MessageBoxA( IntToStr(ret).c_str(), L"test", 0 );
}
I disabled both "Build with runtime packages" and "Dynamic RTL" in application options. I put TestPackage.bpl in the folder as EXE and the EXE worked well. I could upgraded BPL separately from EXE, say to let TestPackage::GetInt() return other numbers. So the net result seems I can deploy my custom BPL with EXE, and other BPLs, like RTL and VCL, are still statically linked in EXE. The only limitation is that my custom BPL must be in the same folder as EXE, is there any way to overcome this?

Is it possible to call a C function from C#.Net

I have a C lib and want to call function in this library from C# application. I tried creating a C++/CLI wrapper on the C lib by adding the C lib file as linker input and adding the source files as additional dependencies.
Is there any better way to achieve this as am not sure how to add C output to c# application.
My C Code -
__declspec(dllexport) unsigned long ConnectSession(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen);
My CPP Wrapper -
long MyClass::ConnectSessionWrapper(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen)
{
return ConnectSession(handle, publicKey, publicKeyLen);
}
The example will be, for Linux:
1) Create a C file, libtest.c with this content:
#include <stdio.h>
void print(const char *message)
{
printf("%s\\n", message);
}
That’s a simple pseudo-wrapper for printf. But represents any C function in the library you want to call. If you have a C++ function don’t forget to put extern C to avoid mangling the name.
2) create the C# file
using System;
using System.Runtime.InteropServices;
public class Tester
{
[DllImport("libtest.so", EntryPoint="print")]
static extern void print(string message);
public static void Main(string[] args)
{
print("Hello World C# => C++");
}
}
3) Unless you have the library libtest.so in a standard library path like “/usr/lib”, you are likely to see a System.DllNotFoundException, to fix this you can move your libtest.so to /usr/lib, or better yet, just add your CWD to the library path: export LD_LIBRARY_PATH=pwd
credits from here
EDIT
For Windows, it's not much different.
Taking an example from here, you only have yo enclose in your *.cpp file your method with extern "C"
Something like
extern "C"
{
//Note: must use __declspec(dllexport) to make (export) methods as 'public'
__declspec(dllexport) void DoSomethingInC(unsigned short int ExampleParam, unsigned char AnotherExampleParam)
{
printf("You called method DoSomethingInC(), You passed in %d and %c\n\r", ExampleParam, AnotherExampleParam);
}
}//End 'extern "C"' to prevent name mangling
then, compile, and in your C# file do
[DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]
public static extern void DoSomethingInC(ushort ExampleParam, char AnotherExampleParam);
and then just use it:
using System;
using System.Runtime.InteropServices;
public class Tester
{
[DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]
public static extern void DoSomethingInC(ushort ExampleParam, char AnotherExampleParam);
public static void Main(string[] args)
{
ushort var1 = 2;
char var2 = '';
DoSomethingInC(var1, var2);
}
}
UPDATE - Feb 22 2019: Since this answer has been getting quite a few upvotes, I decided to update it with a better way of calling the C method. Previously I had suggested using unsafe code, but the safe and correct way is to use MarshalAs attribute for converting a .NET string to a char*. Also, in VS2017 there is no Win32 project anymore, you'll probably have to create a Visual C++ dll or empty project and modify that. Thank you!
You can directly call C functions from C# by using P/Invoke.
Here's a short how-to on creating a C# lbrary that wraps around a C dll.
Create a new C# Library project (I'll call it "Wrapper")
Add a Win32 project to the solution, set application type to: DLL (I'll call it "CLibrary")
You can remove all the other cpp/h files since we won't need them
Rename the CLibrary.cpp file to CLibrary.c
Add a CLibrary.h header file
Now we need to configure the CLibrary project, right-click it and go to properties, and select Configuration: "All Configurations"
In Configuration Properties > C/C++ > Precompiled headers, set Precompiled Headers to: "Not using Precompiled Headers"
In the same C/C++ branch, go to Advanced, change Compile As to: "Compile as C code (/TC)"
Now in the Linker branch, go to General, and change Output File to: "$(SolutionDir)Wrapper\$(ProjectName).dll", this will copy the built C DLL to the C# project root.
CLibrary.h
__declspec(dllexport) unsigned long ConnectSession(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen);
CLibrary.c
#include "CLibrary.h"
unsigned long ConnectSession(unsigned long handle,
unsigned char * publicKey,
unsigned char publicKeyLen)
{
return 42;
}
Right-click CLibrary project, build it, so we get the DLL in the C# project directory
Right-click C# Wrapper project, add existing item, add CLibrary.dll
Click on CLibrary.dll, go to the properties pane, set "Copy to output Directory" to "Copy Always"
It's a good idea to make the Wrapper project dependent on CLibrary so CLibrary gets built first, you can do that by right-clicking the Wrapper project, going to "Project Dependencies" and checking "CLibrary".
Now for the actual wrapper code:
ConnectSessionWrapper.cs
using System.Runtime.InteropServices;
namespace Wrapper
{
public class ConnectSessionWrapper
{
[DllImport("CLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern uint ConnectSession(uint handle,
[MarshalAs(UnmanagedType.LPStr)] string publicKey,
char publicKeyLen);
public uint GetConnectSession(uint handle,
string publicKey,
char publicKeyLen)
{
return ConnectSession(handle, publicKey, publicKeyLen);
}
}
}
Now just call GetConnectSession, and it should return 42.
Result:
Okay well, Open VS 2010, Goto File -> New -> Project -> Visual C++ -> Win32 -> Win32 Project and give it a name (HelloWorldDll in my case), Then in the window that follows under Application Type choose 'DLL' and under Additonal Options choose 'Empty Project'.
Now goto your Solution Explorer tab usually right hand side of VS window, right click Source Files -> Add Item -> C++ file (.cpp) and give it a name (HelloWorld in my case)
Then in the new class paste this code:
#include <stdio.h>
extern "C"
{
__declspec(dllexport) void DisplayHelloFromDLL()
{
printf ("Hello from DLL !\n");
}
}
Now Build the project, after navigate to your projects DEBUG folder and there you should find: HelloWorldDll.dll.
Now, lets create our C# app which will access the dll, Goto File -> New -> Project -> Visual C# -> Console Application and give it a name (CallDllCSharp), now copy and paste this code to your main:
using System;
using System.Runtime.InteropServices;
...
static void Main(string[] args)
{
Console.WriteLine("This is C# program");
DisplayHelloFromDLL();
Console.ReadKey();
}
and build the program, now that we have both our apps built lets use them, get your *.dll and your .exe (bin/debug/.exe) in the same directory, and execute the application output should be
This is C# program
Hello from DLL !
Hope that clears some of your issues up.
References:
How to create a DLL library in C and then use it with C#
NOTE : BELOW CODE IS FOR MULTIPLE METHODS FROM DLL.
[DllImport("MyLibc.so")] public static extern bool MyLib_GetName();
[DllImport("MyLibc.so")] public static extern bool MyLib_SetName(string name);
[DllImport("MyLibc.so")] public static extern bool MyLib_DisplayName(string name);
public static void Main(string[] args)
{
string name = MyLib_GetName();
MyLib_SetName(name);
MyLib_DisplayName(name);
}
The P/Invoke method has been described extensively and repeatedly, ok so far.
What I'm missing here is, that the C++/CLI method has a big advantage: Calling safety.
In contrast to P/Invoke, where the call of the C funtion is like shooting blind into the sky (if this comparison is allowed), nobody will check the function arguments when calling the C function.
Using C++/CLI in this case means normally, you include a headerfile with the functions prototypes you want to use. If you are calling the C function with wrong/to much /to few arguments, the compiler will tell you.
I don't think you can say in general which is the better method, honestly I don't like either of them.

suppress or intercept CRT checks from instances of CRT from other dlls

My program loads several dlls and calls their functions. The dlls can use different versions of CRT.
When C runtime checks the validity of arguments and finds problems, it calls the invalid parameter handle, which in turn, closes the application, with or without the "Send-Don't send" dialog.
I tried calling *_set_invalid_parameter_handler* but it only works if it is called from within the bad dll. I tried SetErrorMode, but all I managed to do is get the process killed without the dialog.
Is there any way to handle those exceptions? I don't care if some resources are compromised. All I want is to allow users to save the configuration. If the dialog appears, they click on it and kill the process.
EDIT
It turns out the solution to load all versions of CRT or to enumerate all DLLs fails. To make all clear, here is a small example to play with:
This would be my main application (let's call the file application.c):
#include <windows.h>
void myInvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) {
wprintf(L"Invalid parameter detected in function %s. File: %s Line: %d\n", function, file, line);
wprintf(L"Expression: %s\n", expression);
}
void fixMyProblem() {
}
int main(int argc, char **argv) {
HMODULE hModule = LoadLibrary("extension.dll");
void (WINAPI *function)() = GetProcAddress(hModule, "function");
fixMyProblem();
function();
}
This application loads a dll that does bad things (it is not developed by me, so I will not accept any solution telling me to fix the bugs there). Lets' call that file extension.c.
#include <stdio.h>
__declspec(dllexport) void function() {
printf("do bad stuff");
fopen(NULL, "r");
}
To compile, do:
cl extension.c /link /OUT:extension.dll /DLL
cl application.c
The question is what do I do in function fixMyProblem(), so that I don't get the send/don't send dialog on XP or the application has stopped working dialog on 7.
According to David Gladfelter I should do
void fixMyProblem() {
_set_invalid_parameter_handler(myInvalidParameterHandler);
}
and also do this for each version CRT available. It turns out that even with one single version of CRT (I use the same for both exe and dll), it still does not work. They both use the same version of CRT, but is seems they do not use the same CRT.
If this is is the case, I assume that the stuff I have to change is inside the DLL. Of course, it does not export *_set_invalid_parameter_handler*.
But to be fair to David Heffernan, here is the implementation for his solution:
#include <Psapi.h>
#pragma comment(lib, "Psapi.lib")
void fixMyProblem() {
HANDLE hProcess = GetCurrentProcess();
HMODULE *hModules;
DWORD requiredSize = 0;
DWORD secondRequiredSize = 0;
if (!EnumProcessModules(hProcess, NULL, 0, &requiredSize)) {
printf("oops\n");
return;
}
hModules = malloc(requiredSize);
if (EnumProcessModules(hProcess, hModules, requiredSize, &secondRequiredSize)) {
int i;
int loadedModules = min(requiredSize, secondRequiredSize) / sizeof(HMODULE);
for (i = 0; i < loadedModules; i++) {
void *(WINAPI *_set_invalid_parameter_handler_function)(void *) = (void *(WINAPI *)(void *)) GetProcAddress(hModules[i], "_set_invalid_parameter_handler");
if (_set_invalid_parameter_handler_function != NULL) {
_set_invalid_parameter_handler_function(myInvalidParameterHandler);
printf("fixed dll %d\n", i);
}
}
} else {
printf("oops\n");
}
free(hModules);
}
For my real application, not this test, I get 1 dll fixed (msvcp90.dll). It still does not fix my problem.
I would appreciate any help in solving this.
If the dll is built with a statically linked CRT, then the state and functions of the CRT will be local to that instance of the dll.
I am assuming the invalid parameter handler used by the CRT is calling the UnhandledExceptionFilter function, from the OS, to show that "nice" error dialog.
You could try to hook functions like UnhandledExceptionFilter or TerminateProcess, making the dll use your own functions instead. You can do this by parsing the Import Address Table of the loaded dll, search for the function name you are interested, and change the address to point to your function.
You could always enumerate the modules in your process and if it's a C runtime then get hold of the invalid parameter handler with a call to GetProcAddress.
But you'd be better off trying to fix the bugs at root. Trying to ignore such problems mostly just leads to further problems because memory gets corrupted and so on.
You could create another DLL that uses the same version of the CRT as the version used by the DLL that causes the invalid parameter handler to be called and register the invalid parameter handler it in that new DLL. The invalid parameter handler is global to the process/CRT-version combination.
If you don't know what version the DLL is using and you can't figure it out, worst-case is you create several DLL's, one for each CRT version:
VS 6 static/dynamic/multithreaded/single-threaded
VS.NET static/dynamic/multithreaded/single-threaded
VS 2003 static/dynamic/multithreaded/single-threaded
VS 2005 static/dynamic
VS 2008 static/dynamic
VS 2010 static/dynamic
You could probably create them as static .lib files and link them all into one (very confused) DLL.

Recommended way to load .so package in tcl either statically or dynamically

I've an executable hosting tcl interpretor, and a library hosting a extension.
I want to be able to build the library dynamically (loaded with Tcl's load)
or statically (single executable, or so loaded implicitly).
The Executable code:
#ifdef GO_STATIC
extern int My_ext_Init(Tcl_Interp* interp);
Tcl_StaticPackage(interp, "my_ext", My_ext_Init, My_ext_Init);
My_ext_Init(interp); // THIS SHOULD NOT BE NEEDED !!
Tcl_SetVariable(interp, "is_statically_linked", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVariable(interp, "is_statically_linked", "0", TCL_GLOBAL_ONLY);
#endif
The library Code .. can be static or dynamic library ( .a or .so / .lib or .dll ):
int My_ext_Init(Tcl_Interp *interp)
{
if (Tcl_PkgProvide(interp, "My_ext", "1.0") == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_CreateObjCommand(interp, /*...etc...*/);
}
The startup tcl code:
global is_statically_linked
if {$is_statically_linked} {
load {} my_ext
} else {
load my_ext my_ext
}
The problem is .. I really shouldn't be calling My_ext_Init(interp); as it
should called by Tcl when I evaluate load {} my_ext
Made community wiki so that the recommended way can be put here.
Registering a “static package” is done in your application init routine, and should be done after the main interpreter is created (but obviously before you start running your scripts in it). It's a mechanism that's really designed to work the “Big Wish” method of program building, when you use Tcl_Main() to do the work of making an interpreter. When you're doing that, there's a callback into your code (typically called Tcl_AppInit though the name is actually arbitrary) that you can specify and which is the ideal place to put calls to Tcl_StaticPackage. The callback will be called at the right point for you to do the static package registration.
However, that's all considered rather old hat these days. A far better method is to always use dynamic libraries and to instead package everything together as a starkit or starpack. The advantage with doing that is that you just need to build your .so files as stub-enabled (strongly recommended anyway) packages and then you include them in your VFS during the packaging process. After that, you can just do package require and it will all work. (Or you can locate the shared libraries in the virtual mount when running and load them directly.) What's even better is that you can deliver a single .kit file that supports multiple platforms; starpacked executables can't be quite that flexible though, as there are some natural restrictions on portability of binary executables. :-)

Resources