Cannot find output of Win32 TraceLogging - c

I tried to replicate the short sample program for TraceLogging that Microsoft provides (see below, with minor changes). I completed the "development" (rather copy) in Visual Studio 2019. All is fine, compiles without issues, runs without issues, but nowhere on my PC I can find an updated *.etl or *.log file, nor do I find an entry somewhere in the Event Viewer.
I carefully read the Microsoft documentation and I searched the Internet for many hours, but no useful findings.
I know that I'm stupid from time to time and I must miss something obvious, but what is it? Any hints, please? Many thanks!
#include <windows.h> // or <wdm.h> for kernel-mode.
#include <winmeta.h>
#include <TraceLoggingProvider.h>
#include <stdio.h>
// Define the GUID to use in TraceLoggingRegister
// {5B5852D4-DC24-4A0F-87B6-9115AE9D2768}
TRACELOGGING_DEFINE_PROVIDER ( // defines g_hProvider
g_hProvider, // Name of the provider variable
"Test-Test", // Human-readable name of the provider
(0x5b5852d4, 0xdc24, 0x4a0f, 0x87, 0xb6, 0x91, 0x15, 0xae, 0x9d, 0x27, 0x68) ); // Provider GUID
int main ( int argc, char *argv[] ) // or DriverEntry for kernel-mode.
{
HRESULT hrRegister;
hrRegister = TraceLoggingRegister ( g_hProvider );
if ( !SUCCEEDED ( hrRegister ) ) {
printf ( "TraceLoggingRegister failed. Stopping." );
return 1;
}
TraceLoggingWrite (
g_hProvider,
"MyEvent1",
// TraceLoggingChannel ( WINEVENT_CHANNEL_CLASSIC_TRACE ),
// TraceLoggingLevel ( WINEVENT_LEVEL_CRITICAL ),
TraceLoggingString ( argv[0], "arg0" ), // field name is "arg0"
TraceLoggingInt32 ( argc ) ); // field name is implicitly "argc"
TraceLoggingUnregister ( g_hProvider );
return 0;
}

First of all, running this C++ code will not generate the .log or .etl file you want, it just sends the TraceLogging event, you need to capture it in other ways to generate the etl file.
According to the MSDN,You have two steps to capture TraceLogging events:
Capture trace data with WPR
Capture TraceLogging events on Windows Phone
First create a .WPRP file, I used the same C++ code and WPRP file from MSDN as follow.
test.cpp
#include <windows.h> // or <wdm.h> for kernel-mode.
#include <winmeta.h>
#include <TraceLoggingProvider.h>
#include <stdio.h>
// Define the GUID to use in TraceLoggingProviderRegister
// {3970F9cf-2c0c-4f11-b1cc-e3a1e9958833}
TRACELOGGING_DEFINE_PROVIDER(
g_hMyComponentProvider,
"SimpleTraceLoggingProvider",
(0x3970f9cf, 0x2c0c, 0x4f11, 0xb1, 0xcc, 0xe3, 0xa1, 0xe9, 0x95, 0x88, 0x33));
void main()
{
char sampleValue[] = "Sample value";
// Register the provider
TraceLoggingRegister(g_hMyComponentProvider);
// Log an event
TraceLoggingWrite(g_hMyComponentProvider, // handle to my provider
"HelloWorldTestEvent", // Event Name that should uniquely identify your event.
TraceLoggingValue(sampleValue, "TestMessage")); // Field for your event in the form of (value, field name).
// Stop TraceLogging and unregister the provider
TraceLoggingUnregister(g_hMyComponentProvider);
}
Sample WPRP file
<?xml version="1.0" encoding="utf-8"?>
<!-- TODO:
1. Find and replace "SimpleTraceLoggingProvider" with the name of your provider.
2. See TODO below to update GUID for your event provider
-->
<WindowsPerformanceRecorder Version="1.0" Author="Microsoft Corporation" Copyright="Microsoft Corporation" Company="Microsoft Corporation">
<Profiles>
<EventCollector Id="EventCollector_SimpleTraceLoggingProvider" Name="SimpleTraceLoggingProvider">
<BufferSize Value="64" />
<Buffers Value="4" />
</EventCollector>
<!-- TODO:
1. Update Name attribute in EventProvider xml element with your provider GUID, eg: Name="3970F9cf-2c0c-4f11-b1cc-e3a1e9958833". Or
if you specify an EventSource C# provider or call TraceLoggingRegister(...) without a GUID, use star (*) before your provider
name, eg: Name="*MyEventSourceProvider" which will enable your provider appropriately.
2. This sample lists one EventProvider xml element and references it in a Profile with EventProviderId xml element.
For your component wprp, enable the required number of providers and fix the Profile xml element appropriately
-->
<EventProvider Id="EventProvider_SimpleTraceLoggingProvider" Name="*SimpleTraceLoggingProvider" />
<Profile Id="SimpleTraceLoggingProvider.Verbose.File" Name="SimpleTraceLoggingProvider" Description="SimpleTraceLoggingProvider" LoggingMode="File" DetailLevel="Verbose">
<Collectors>
<EventCollectorId Value="EventCollector_SimpleTraceLoggingProvider">
<EventProviders>
<!-- TODO:
1. Fix your EventProviderId with Value same as the Id attribute on EventProvider xml element above
-->
<EventProviderId Value="EventProvider_SimpleTraceLoggingProvider" />
</EventProviders>
</EventCollectorId>
</Collectors>
</Profile>
<Profile Id="SimpleTraceLoggingProvider.Light.File" Name="SimpleTraceLoggingProvider" Description="SimpleTraceLoggingProvider" Base="SimpleTraceLoggingProvider.Verbose.File" LoggingMode="File" DetailLevel="Light" />
<Profile Id="SimpleTraceLoggingProvider.Verbose.Memory" Name="SimpleTraceLoggingProvider" Description="SimpleTraceLoggingProvider" Base="SimpleTraceLoggingProvider.Verbose.File" LoggingMode="Memory" DetailLevel="Verbose" />
<Profile Id="SimpleTraceLoggingProvider.Light.Memory" Name="SimpleTraceLoggingProvider" Description="SimpleTraceLoggingProvider" Base="SimpleTraceLoggingProvider.Verbose.File" LoggingMode="Memory" DetailLevel="Light" />
</Profiles>
</WindowsPerformanceRecorder>
Then start the capture using WPR from an elevated (run as Administrator) Command Prompt window.
wpr.exe -start C:\Users\songz\Desktop\test.wprp
Next you may run the application that contains your events and stop the trace capture.
wpr.exe -stop C:\Users\songz\Desktop\test.etl description
This can generate the etl file you need normally.
After completing the above operations, you should capture TraceLogging events. According to the github, you can use the following commands:
xperf -start MySession -f C:\Users\songz\Desktop\test.etl -on 3970F9cf-2c0c-4f11-b1cc-e3a1e9958833
xperf -stop MySession
Note:You should use like xperf -start MySession -f MyFile.etl -on Id
Finally you can view the corresponding information through WPA.

Zhu Song, thank you very much for your input! Your comments brought me to the right track.
Well, I did not follow strictly your text, but I found new stuff to read.
I want the controller to be part of my application as well. So, what I did is roughly the following:
Prepare EVENT_TRACE_PROPERTIES structure
TraceLogRegister
StartTrace
EnableTraceEx2 (enable)
TraceLoggingWrite
TraceLoggingUnregister
EnableTraceEx2 (disable)
ControlTrace (stop)
This resulted in an xxx.etl file that I could view with tracerpt or WPA.
Thanks again! I'm all fine now.
This is the code in detail:
#include <windows.h> // or <wdm.h> for kernel-mode.
#include <winmeta.h>
#include <TraceLoggingProvider.h>
#include <evntrace.h>
#include <stdio.h>
#include <strsafe.h>
#define LOGFILE_NAME TEXT(".\\Test-Test.etl")
#define LOGSESSION_NAME TEXT("Test-Test-Session")
// Define the GUID to use in TraceLoggingRegister
// {5B5852D4-DC24-4A0F-87B6-9115AE9D2768}
TRACELOGGING_DEFINE_PROVIDER ( // defines g_hProvider
g_hProvider, // Name of the provider variable
"Test-Test", // Human-readable name of the provider
(0x5b5852d4, 0xdc24, 0x4a0f, 0x87, 0xb6, 0x91, 0x15, 0xae, 0x9d, 0x27, 0x68) ); // Provider GUID
static const GUID ProviderGUID = { 0x5b5852d4, 0xdc24, 0x4a0f, {0x87, 0xb6, 0x91, 0x15, 0xae, 0x9d, 0x27, 0x68} };
int main ( int argc, char *argv[] ) // or DriverEntry for kernel-mode.
{
TRACEHANDLE hTrace = 0;
EVENT_TRACE_PROPERTIES *petProperties;
HRESULT hrRegister;
ULONG bufferSize, ret_val;
bufferSize = sizeof ( EVENT_TRACE_PROPERTIES ) + sizeof ( LOGFILE_NAME ) + sizeof ( LOGSESSION_NAME ) + 512; // The additional bytes are necessary because the path of thr LOGFILE_NAME is expanded
petProperties = (EVENT_TRACE_PROPERTIES *) malloc ( bufferSize );
if ( petProperties == NULL ) {
printf ( "Unable to allocate %d bytes for properties structure.\n", bufferSize );
return 1;
}
ZeroMemory ( petProperties, bufferSize );
petProperties->Wnode.BufferSize = bufferSize;
petProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
petProperties->Wnode.ClientContext = 1;
petProperties->Wnode.Guid = ProviderGUID;
petProperties->LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL | EVENT_TRACE_PRIVATE_LOGGER_MODE | EVENT_TRACE_PRIVATE_IN_PROC;
petProperties->MaximumFileSize = 100; // was 1
petProperties->BufferSize = 512;
petProperties->MinimumBuffers = 8;
petProperties->MaximumBuffers = 64;
petProperties->LoggerNameOffset = sizeof ( EVENT_TRACE_PROPERTIES );
petProperties->LogFileNameOffset = sizeof ( EVENT_TRACE_PROPERTIES ) + sizeof ( LOGSESSION_NAME );
StringCbCopy ( (LPWSTR) ((char *) petProperties + petProperties->LogFileNameOffset), sizeof ( LOGFILE_NAME ), LOGFILE_NAME );
hrRegister = TraceLoggingRegister ( g_hProvider );
if ( !SUCCEEDED ( hrRegister ) ) {
printf ( "TraceLoggingRegister failed. Stopping.\n" );
return 1;
}
ret_val = StartTrace ( &hTrace, LOGSESSION_NAME, petProperties );
if ( ret_val != ERROR_SUCCESS ) {
printf ( "StartTrace failed with %i\n", ret_val );
if ( ret_val != ERROR_ALREADY_EXISTS )
return 1;
}
ret_val = EnableTraceEx2 ( hTrace, &ProviderGUID, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_VERBOSE, 0, 0, 0, NULL );
if ( ret_val != ERROR_SUCCESS ) {
printf ( "EnableTraceEx2(enable) failed with %i\n", ret_val );
ret_val = ControlTrace ( hTrace, LOGSESSION_NAME, petProperties, EVENT_TRACE_CONTROL_STOP );
if ( ret_val != ERROR_SUCCESS ) {
printf ( "ControlTrace(stop) failed with %i\n", ret_val );
}
return 1;
}
if ( TraceLoggingProviderEnabled ( g_hProvider, 0, 0 ) )
printf ( "TraceLoggingProvider enabled\n" );
else
printf ( "TraceLoggingProvider NOT enabled\n" );
TraceLoggingWrite (
g_hProvider,
"MyEvent1",
TraceLoggingString ( argv[0], "arg0" ), // field name is "arg0"
TraceLoggingInt32 ( argc ) ); // field name is implicitly "argc"
TraceLoggingUnregister ( g_hProvider );
ret_val = EnableTraceEx2 ( hTrace, &ProviderGUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, TRACE_LEVEL_VERBOSE, 0, 0, 0, NULL );
if ( ret_val != ERROR_SUCCESS ) {
printf ( "EnableTraceEx2(disable) failed with %i\n", ret_val );
}
ret_val = ControlTrace ( hTrace, LOGSESSION_NAME, petProperties, EVENT_TRACE_CONTROL_STOP );
if ( ret_val != ERROR_SUCCESS ) {
printf ( "ControlTrace(stop) failed with %i\n", ret_val );
}
return 0;
}

ETW is an event routing system. TraceLoggingWrite means "send the event to ETW". But if nobody is interested in your event then ETW will just ignore it.
To collect the data from TraceLoggingWrite, there needs to be an ETW session that is listening for events from you. There are many ways to start and control ETW sessions, including the StartTrace API, the AutoLogger registry key, the WPR tool, and the TRACELOG tool.
I usually prefer the TRACELOG tool. It is included with the Windows SDK, so it is installed if I have installed Visual Studio, and it will be on my path if I open a "Developer command prompt". On my computer, TRACELOG is in C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x86\tracelog.exe.
To capture events from the provider you show in your source code, I would run:
TRACELOG -start MySessionName -f MySession.etl -guid #5B5852D4-DC24-4A0F-87B6-9115AE9D2768
After my code has run, I would stop the trace with:
TRACELOG -stop MySessionName
I could then use various tools to decode the trace. I usually use the TRACEFMT tool.
TRACEFMT MySession.etl

Related

How to get data from POST/GET method in a C program?

I want to write a simple program that takes data from forms via POST OR GET. A simple form lets say for adding two numbers.
I saw that libcurl is capable of talking with http protocol but I did not see any example related to this question.
All i saw is how to send data to webpage but not how to get it.
Thank you for your time.
Quick-n-dirty, very untested example based on another project of mine - no warranties express or implied. This assumes that the binary has properly been deployed on the Web server (for example, under the cgi-bin directory on an Apache server) and that the input form has been set up to call it properly:
<form action="http://my-url.com/cgi-bin/adder" method="get"> <!-- or method="post" -->
<label for="v1">V1:</label><input type="text" name="v1"><br>
<label for="v2">V2:</label><input type="text" name="v2"><br>
<input type="submit" name="submitbutton" value="Add">
</form>
Then your C code will look something like
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char **argv )
{
char *interface = getenv( "GATEWAY_INTERFACE" );
/**
* If interface is NULL, then we were not invoked through CGI.
* For this example we just fail silently.
*/
if ( !interface )
exit( EXIT_FAILURE );
char *method = getenv( "REQUEST_METHOD" );
/**
* If method is NULL, then we were not invoked through CGI;
* for this example we'll just fail silently
*/
if ( !method )
exit( EXIT_FAILURE );
if ( strcmp( method, "GET" ) == 0 )
{
/**
* We were invoked from a Web client with the HTTP GET method -
* input parameters will be in the QUERY_STRING environment
* variable as "param=value&param=value"
*/
char *query_string = getenv( "QUERY_STRING" );
do_stuff_with( query_string );
}
else if ( strcmp( method, "POST" ) == 0 )
{
/**
* We were invoked from a Web client with the HTTP POST method -
* input parameters will be received via standard input
*/
char query_string[SOME_SIZE];
if ( fgets( query_string, sizeof query_string, stdin ) )
{
do_stuff_with( query_string );
}
else
// handle error
}
else
{
/**
* Input method is not GET or POST, log an error and fail
*/
fputs( "Don't know how to handle this request\n", stderr );
exit( EXIT_FAILURE );
}
}
Any response back to the Web client will be written through standard output:
printf( "Content-type: text/html\r\n\r\n" );
printf( "<!DOCTYPE HTML><html><head><title>Adder result</title></head>" );
printf( "<body><p>Result of add is %d</p></body></html>", add_result );

mbedtls: error on mbedtls_ctr_drbg_seed

I'm using mbedtls to run SSL server.
The function mbedtls_ctr_drbg_seed returned -34.
My code is below:
const char *pers = "ssl_server2";
mbedtls_havege_state hs;
mbedtls_ssl_session ssn;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
// One HTTPS Request Handling
memset( &ssn, 0, sizeof( mbedtls_ssl_session ) );
/*
* 4. Setup stuff
*/
mbedtls_ssl_init( &ssl );
mbedtls_ssl_config_init( &conf );
mbedtls_ctr_drbg_init( &ctr_drbg );
mbedtls_entropy_init( &entropy );
printf( " . Setting up the RNG and SSL data...." );
if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) pers, sizeof( pers ) ) ) != 0 )
{
printf( " failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n", -ret );
goto ExitFunction;
}
else
printf( " mbedtls_ctr_drbg_seed returned 0x%x ok\n", ret );
As #Gilles rightfully said, the error you are receiving is probably -0x34, which is MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED. This error is returned when the function mbedtls_entropy_func() fails. Please check the the entropy source you are using is strong enough, meaning you have at least one entropy source which is strong, when added with mbedtls_entropy_add_source(). You should also verify that the entropy source you are using can collect enough entropy, and exceeds the threshold set to the source.
There are other locations where mbedtls_entropy_func() might fail, therefore I suggest you check these locations as well.

How to know the address range when searching for a function by its signature?

I'm trying to search for a function by its "signature".
However I can't figure out what address range I'm supposed to be searching?
I've had a look at VirtualQuery() and GetNativeSystemInfo() but I'm not if I'm on the right path or not.
Edit: Question re-attempt.
Using Win32 API I'm trying to find out how to get the start and end address of the executable pages of the process my code is executing in.
This is what I've tried:
SYSTEM_INFO info;
ZeroMemory( &info, sizeof( SYSTEM_INFO ) );
GetNativeSystemInfo( &info ); // GetSystemInfo() might be wrong on WOW64.
info.lpMinimumApplicationAddress;
info.lpMaximumApplicationAddress;
HANDLE thisProcess = GetCurrentProcess();
MEMORY_BASIC_INFORMATION memInfo;
ZeroMemory( &memInfo, sizeof( memInfo ) );
DWORD addr = (DWORD)info.lpMinimumApplicationAddress;
do
{
if ( VirtualQueryEx( thisProcess, (LPVOID)addr, &memInfo, sizeof( memInfo ) ) == 0 )
{
DWORD gle = GetLastError();
if ( gle != ERROR_INVALID_PARAMETER )
{
std::stringstream str;
str << "VirtualQueryEx failed with: " << gle;
MessageBoxA( NULL, str.str().c_str(), "Error", MB_OK );
}
break;
}
if ( memInfo.Type == MEM_IMAGE )
{
// TODO: Scan this memory block for the the sigature
}
addr += info.dwPageSize;
}
while ( addr < (DWORD)info.lpMaximumApplicationAddress );
The reason for doing this is that I'm looking for an un-exported function by its signature as asked here:
Find a function by it signature in Windows DLL
See the answer about "code signature scanning".
While this is enumerating an address range I don't know if this is correct or not since I don't know what the expected range should be. Its just the best I could come up with from looking around MSDN.
the address range when signature scanning a module is from the start of the code section to the start + the section size. the start of the code section and its size are in the PE. most tools take the lazy route and scan the entire module (again using the PE to get the size, but with the module handle as the start address).

What is the correct ConnectionString to pass SQLConnect?

I've found a good example on the MSDN site. SQLConnect
I want to connect to an SQL server, to my database, but unfortunately, SQLConnect always returns -1, and the application freezes.
I have edited the code above:
direxec::direxec()
{
_mbscpy_s( chr_ds_name, MAX_DATA, ( const unsigned char * )"Server=mySQLServer;Database=myDatabaseName;" );
_mbscpy_s( uid, MAX_DATA, ( const unsigned char * )"testuser" );
_mbscpy_s( pwd, MAX_DATA, ( const unsigned char * )"testpassword" );
printf("\n%s",chr_ds_name);
}
void direxec::sqlconn()
{
SQLAllocEnv( &henv );
SQLAllocConnect( henv, &hdbc );
rc = SQLConnect( hdbc, chr_ds_name, SQL_NTS, uid, SQL_NTS, pwd, SQL_NTS );
// Deallocate handles, display error message, and exit.
if ( !MYSQLSUCCESS( rc ) )
{
printf( "\nFailed %d",rc );
SQLFreeConnect( henv );
SQLFreeEnv( henv );
SQLFreeConnect( hdbc );
if ( hstmt ) error_out();
exit( -1 );
}
printf("\nConnected");
rc = SQLAllocStmt( hdbc, &hstmt );
}
Using SQLDriverConnect i can connect to my database, but i want to use SQLConnect if it is possible.
Does anyone have any idea what am i doing wrong?
Thanks!
SqlConnect doesn't accept a connection string, it only accepts a Data Source Name (DSN). DSN's are configured in the ODBC Administration Tool in Administrative Tools. If you want to use a connection string then you need to use SqlDriverConnect

How to compile a user defined function in SQLite

I have an example C function that I would like to attach to a database temp.sqlite (it's from an O'Reilly book, so I know it works). I read the section in the book and sqlite.org, but they assume away that I know how and where to compile this thing with the proper settings. I'm on either Mac (with XCode) or Ubuntu.
I know enough C to alter the code to do what I want, but I have no idea what to do call it from my database temp.sqlite.
Thanks for your help! I've been churning on this!
Update: A few more hours in and I've scrapped together enough stuff from abandoned webpages to create a compile command and generate an error:
richard$ gcc -o wtavg wtavg.c -Wall -W -O2 -L/usr/local/lib -lsqlite3
wtavg.c: In function ‘wtavg_init’:
wtavg.c:63: warning: unused parameter ‘error’
Undefined symbols:
"_main", referenced from:
start in crt1.10.6.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
FWIW, here's wtavg.c, which is straight from the O'Reilly site provided in my book:
/* wtavg.c */
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1;
#include <stdlib.h>
typedef struct wt_avg_state_s {
double total_data; /* sum of (data * weight) values */
double total_wt; /* sum of weight values */
} wt_avg_state;
static void wt_avg_step( sqlite3_context *ctx, int num_values, sqlite3_value **values )
{
double row_wt = 1.0;
int type;
wt_avg_state *st = (wt_avg_state*)sqlite3_aggregate_context( ctx,
sizeof( wt_avg_state ) );
if ( st == NULL ) {
sqlite3_result_error_nomem( ctx );
return;
}
/* Extract weight, if we have a weight and it looks like a number */
if ( num_values == 2 ) {
type = sqlite3_value_numeric_type( values[1] );
if ( ( type == SQLITE_FLOAT )||( type == SQLITE_INTEGER ) ) {
row_wt = sqlite3_value_double( values[1] );
}
}
/* Extract data, if we were given something that looks like a number. */
type = sqlite3_value_numeric_type( values[0] );
if ( ( type == SQLITE_FLOAT )||( type == SQLITE_INTEGER ) ) {
st->total_data += row_wt * sqlite3_value_double( values[0] );
st->total_wt += row_wt;
}
}
static void wt_avg_final( sqlite3_context *ctx )
{
double result = 0.0;
wt_avg_state *st = (wt_avg_state*)sqlite3_aggregate_context( ctx,
sizeof( wt_avg_state ) );
if ( st == NULL ) {
sqlite3_result_error_nomem( ctx );
return;
}
if ( st->total_wt != 0.0 ) {
result = st->total_data / st->total_wt;
}
sqlite3_result_double( ctx, result );
}
int wtavg_init( sqlite3 *db, char **error, const sqlite3_api_routines *api )
{
SQLITE_EXTENSION_INIT2(api);
sqlite3_create_function( db, "wtavg", 1, SQLITE_UTF8,
NULL, NULL, wt_avg_step, wt_avg_final );
sqlite3_create_function( db, "wtavg", 2, SQLITE_UTF8,
NULL, NULL, wt_avg_step, wt_avg_final );
return SQLITE_OK;
}
The user defined function should be compiled as shared library file:
gcc -shared -fPIC -o wtavg.so wtavg.c -lsqlite3
That shared library can then be loaded using an SQLite statement:
SELECT load_extension('/path/to/wt_avg.so', 'wtavg_init');
Unfortunately the version of sqlite3 provided by Apple doesn't support loading shared libraries. You can use sqlite from MacPorts instead. MacPorts programs linking against sqlite should also have the ability to load user defined functions this way.
When using SQLite inside another program however the extension loading mechanism may be disabled for security reasons. In Python for instance you have to call con.enable_load_extension(True) to enable it.

Resources